Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]

    1   /*   Copyright 2004 The Apache Software Foundation
    2    *
    3    *   Licensed under the Apache License, Version 2.0 (the "License");
    4    *   you may not use this file except in compliance with the License.
    5    *   You may obtain a copy of the License at
    6    *
    7    *       http://www.apache.org/licenses/LICENSE-2.0
    8    *
    9    *   Unless required by applicable law or agreed to in writing, software
   10    *   distributed under the License is distributed on an "AS IS" BASIS,
   11    *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12    *   See the License for the specific language governing permissions and
   13    *  limitations under the License.
   14    */
   15   
   16   package org.apache.xmlbeans;
   17   
   18   import java.util.GregorianCalendar;
   19   import java.util.Calendar;
   20   import java.util.Date;
   21   import java.util.TimeZone;
   22   import java.math.BigDecimal;
   23   
   24   /**
   25    * An XML Schema compatible subclass of {@link java.util.GregorianCalendar GregorianCalendar}.
   26    * XmlCalendar modifies several key details in the behavior of
   27    * GregorianCalendar to make it more useful when dealing with XML dates.
   28    * <p>
   29    * It is easy to convert between XmlCalendar and {@link GDate}, or to
   30    * parse or emit an XmlCalendar using a standard XML Schema
   31    * lexical representation.
   32    * <ol>
   33    * <li>
   34    * To match XML Schema dates, this XmlCalendar is a fully proleptic
   35    * Gregorian calendar by default, which means that Gregorian calendar
   36    * rules are applied backwards in time as if they had always been in
   37    * effect, actual historical circumstances concerning the observance
   38    * of the 1582 decree of Pope Gregory XIII notwithstanding.
   39    * </li>
   40    * <li>
   41    * In order to better support partially-specified dates for XML Schema,
   42    * this implementation provides a stable get(field) method
   43    * that does not modify the instance if you are acessing a field right
   44    * after it was explicitly set: a set followed by a get will always
   45    * return the same thing and will not fill in any other fields. However,
   46    * if you get a field that was not explicitly set, then all the fields
   47    * are still automatically filled and normalized for you, just like a
   48    * regular GregorianCalendar. If you wish to force the completion and
   49    * defaulting of all the fields (without hunting to get one that happens
   50    * to be unset), you can always do so by calling getTime().
   51    * </li>
   52    * <li>
   53    * When a year is unspecified and needs to be filled in automatically
   54    * (for example when using a .get or .getTime method as discussed above),
   55    * the year is defaulted to year 0 (also known as 1 BC).  This is different
   56    * from {@link GregorianCalendar}, which chooses 1970. The reason 0 is preferable
   57    * is that it is a leap year and so it permits the date --2-29 to be specified
   58    * stably. A different default year can be chosen via the static method
   59    * {@link #setDefaultYear(int) XmlCalendar.setDefaultYear()}, or by setting the 
   60    * system property "user.defaultyear". If you do change this value, you should 
   61    * pick another leap year such as 2000 and avoid non-leap years such as 1900.
   62    * </li>
   63    * <li>
   64    * When constructing an XmlCalendar from an XML Schema
   65    * formatted date or time string or GDate object, the timezone
   66    * for the calendar is taken from the string if it is present, or
   67    * taken to be {@link java.util.TimeZone#getDefault() TimeZone.getDefault()} if not.
   68    * <p>
   69    *     For example, the XML timezone "Z" is translated to "GMT";
   70    *     the XML timezone "+05:00" is translated to "GMT+05:00".
   71    * </p>
   72    * </li>
   73    * <li>
   74    * Finally, this implementation provides a String constructor and a
   75    * toString() method that comply with the XML Schema conventions
   76    * for formatting a date. If only a subset of fields have been
   77    * explicitly set, toString() produces a string with the proper subset
   78    * of information.
   79    * </li>
   80    * </ol>
   81    */ 
   82   public class XmlCalendar extends GregorianCalendar
   83   {
   84       /**
   85        * Constructs an XmlCalendar for a standard XML
   86        * schema formatted date string.
   87        * 
   88        * The parser accepts any of the following formats:
   89        * 
   90        * YYYY-MM-DDThh:mm:ss          - dateTime
   91        * YYYY-MM-DD                   - date
   92        *            hh:mm:ss          - time
   93        * YYYY                         - gYear
   94        *    --MM                      - gMonth
   95        *      ---DD                   - gDay
   96        * 
   97        * The parser actually accepts all 16 combinations of subsets of
   98        * fields (Y, M, D, T) using the same scheme, even for combinations
   99        * that are not defined as types in the schema spec, such as
  100        * year, day, and time:
  101        * 
  102        * YYYY--DDThh:mm:ss            - [everything but month specified]
  103        * 
  104        * In the string, each field must be padded to its full width, for
  105        * example, January must be --01, not just --1.
  106        * 
  107        * In particular, a year must be padded to at least four digits, so
  108        * "98" is not a valid year, although "1998" and "0098" are both valid
  109        * years, unambiguously 19 centuries separated from each other.  A year
  110        * may also be preceded by a minus symbol: -0001 is 1 BC and -0002 is
  111        * 2 BC.
  112        *
  113        * Finally a timezone is always allowed (yet optional) at the end.
  114        * Timezones must be either "Z" (UTC, which we translate to GMT),
  115        * or simple offsets from UTC in the range "-14:00" to "+14:00",
  116        * for example: "14:30:00-05:00" specifies 2:30 PM in the
  117        * afternoon at UTC-05:00, which is the same as EST.
  118        * 
  119        * If a timezone is not specified, the default TimeZone is used.
  120        */ 
  121       public XmlCalendar(String xmlSchemaDateString)
  122       {
  123           this(new GDate(xmlSchemaDateString)); // use GDate to parse
  124       }
  125       
  126       /**
  127        * Constructs an XmlCalendar from a GDate.
  128        * 
  129        * If the instance is not completed, you can round-trip to an
  130        * equivalent GDate by writing "new GDate(new XmlCalendar(gdate))".
  131        * However, if you access any of the unset fields of the calendar, all
  132        * the fields will be automatically filled in, so partial dates
  133        * without timezones or other fields will not round-trip after access.
  134        */ 
  135       public XmlCalendar(GDateSpecification date)
  136       {
  137           this(GDate.timeZoneForGDate(date), date);
  138       }
  139       
  140       private XmlCalendar(TimeZone tz, GDateSpecification date)
  141       {
  142           super(tz);
  143           setGregorianChange(_beginningOfTime); // proleptic
  144           clear();
  145           
  146           if (date.hasYear())
  147           {
  148               int y = date.getYear(); // is never 0
  149               if (y > 0)
  150               {
  151                   set(Calendar.ERA, GregorianCalendar.AD);
  152               }
  153               else // y < 0
  154               {
  155                   set(Calendar.ERA, GregorianCalendar.BC);
  156                   //y = 1 - y;
  157                   y = -y; // no need to add 1
  158               }
  159               set(Calendar.YEAR, y);
  160           }
  161           if (date.hasMonth())
  162               set(Calendar.MONTH, date.getMonth() - 1); // note!!
  163           if (date.hasDay())
  164               set(Calendar.DAY_OF_MONTH, date.getDay());
  165           if (date.hasTime())
  166           {
  167               set(Calendar.HOUR_OF_DAY, date.getHour());
  168               set(Calendar.MINUTE, date.getMinute());
  169               set(Calendar.SECOND, date.getSecond());
  170               if (date.getFraction().scale() > 0)
  171                   set(Calendar.MILLISECOND, date.getMillisecond());
  172           }
  173           if (date.hasTimeZone())
  174           {
  175               set(Calendar.ZONE_OFFSET, date.getTimeZoneSign() * 1000 * 60 * (date.getTimeZoneHour() * 60 + date.getTimeZoneMinute()));
  176               set(Calendar.DST_OFFSET, 0); // note!!  if we don't do this, then GregorianCalendar will pick up DST from the time zone
  177           }
  178       }
  179       
  180       /**
  181        * Constructs an XmlCalendar from a Date.
  182        * 
  183        * The default TimeZone is used for computing the various fields.
  184        */
  185       public XmlCalendar(Date date)
  186       {
  187           this(TimeZone.getDefault(), new GDate(date));
  188           complete();
  189       }
  190       
  191       /**
  192        * Constructs an XmlCalendar with the specified year, month, day,
  193        * hours, minutes, seconds, and optional fractional seconds, in
  194        * the default timezone.
  195        */
  196       public XmlCalendar(
  197               int year,
  198               int month,
  199               int day,
  200               int hour,
  201               int minute,
  202               int second,
  203               BigDecimal fraction)
  204       {
  205           this(TimeZone.getDefault(), new GDate(year, month, day, hour, minute, second, fraction));
  206       }
  207       
  208       /**
  209        * Constructs an XmlCalendar with the specified year, month, day,
  210        * hours, minutes, seconds, and optional fractional seconds, in
  211        * the specified timezone.
  212        */
  213       public XmlCalendar(
  214               int year,
  215               int month,
  216               int day,
  217               int hour,
  218               int minute,
  219               int second,
  220               BigDecimal fraction,
  221               int tzSign,
  222               int tzHour,
  223               int tzMinute)
  224       {
  225           this(new GDate(year, month, day, hour, minute, second, fraction, tzSign, tzHour, tzMinute));
  226       }
  227       
  228   
  229       /**
  230        * Gets the value for a given time field.
  231        * 
  232        * Unlike the GregorianCalendar implementation, the get() does not
  233        * force a complete of all fields.  If you wish to force a completion
  234        * of all the fields, call getTime() first.
  235        */
  236       public int get(int field)
  237       {
  238           if (!isSet(field) || isTimeSet)
  239               return super.get(field); // forces a complete
  240           else
  241               return internalGet(field); // does not force a complete.
  242       }
  243   
  244       /**
  245        * Constructs an empty instance with no fields set.
  246        */ 
  247       public XmlCalendar()
  248       {
  249           setGregorianChange(_beginningOfTime); // proleptic
  250           clear();
  251       }
  252       
  253       private static int defaultYear = Integer.MIN_VALUE;
  254       private static final int DEFAULT_DEFAULT_YEAR = 0; 
  255       
  256       /**
  257        * Returns the default year that is used when no year is specified.
  258        */ 
  259       public static int getDefaultYear()
  260       {
  261           if (defaultYear == Integer.MIN_VALUE)
  262           {
  263               try
  264               {
  265                   String yearstring = SystemProperties.getProperty("user.defaultyear");
  266                   if (yearstring != null)
  267                       defaultYear = Integer.parseInt(yearstring);
  268                   else
  269                       defaultYear = DEFAULT_DEFAULT_YEAR;
  270               }
  271               catch (Throwable t)
  272               {
  273                   defaultYear = DEFAULT_DEFAULT_YEAR;
  274               }
  275           }
  276           return defaultYear;
  277       }
  278       
  279       /**
  280        * Sets the default year to be used when no year is specified.
  281        */ 
  282       public static void setDefaultYear(int year)
  283       {
  284           defaultYear = year;
  285       }
  286       
  287       /**
  288        * Overrides GregorianCalendar.computeTime to apply a different
  289        * default year.  (It must be a leap year.)
  290        */ 
  291       protected void computeTime()
  292       {
  293           boolean unsetYear = !isSet(YEAR);
  294           if (unsetYear)
  295               set(YEAR, getDefaultYear());
  296           try
  297           {
  298               super.computeTime();
  299           }
  300           finally
  301           {
  302               if (unsetYear)
  303                   clear(YEAR);
  304           }
  305       }
  306           
  307       private static Date _beginningOfTime = new Date(Long.MIN_VALUE);
  308       
  309       /**
  310        * Prints the XmlCalendar using a standard XML Schema
  311        * format, as described in XmlCalendar(String s).
  312        */
  313       public String toString()
  314       {
  315           return (new GDate(this)).toString(); // use GDate to print
  316       }
  317   }

Home » xmlbeans-2.5.0-src » org.apache » xmlbeans » [javadoc | source]