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.math.BigDecimal;
   19   import java.math.BigInteger;
   20   import java.util.Calendar;
   21   import java.util.Date;
   22   import java.util.GregorianCalendar;
   23   import java.util.TimeZone;
   24   
   25   /**
   26    * Represents an XML Schema-compatible Gregorian date.
   27    * <p>
   28    * There are many date types in XML Schema, and this type
   29    * represents the natural union of all those types.  A GDate
   30    * can hold any subset of date fields (Year, Month, Day, Time,
   31    * Timezone, or some combination). Wherever the specification
   32    * provides guidance, the guidelines in the
   33    * <a target="_blank" href="http://www.w3.org/TR/xmlschema-2/">XML Schema 1.0 specification</a>
   34    * (plus <a target="_blank" href="http://www.w3.org/2001/05/xmlschema-errata">published errata</a>) are followed.
   35    * <p>
   36    * Instances may separately have values or no values for
   37    * the year, month, day-of-month, and time-of-day. Not all
   38    * operations are meaningful on all combinations.
   39    */
   40   public final class GDate implements GDateSpecification, java.io.Serializable
   41   {
   42       private static final long serialVersionUID = 1L;
   43   
   44       // XMLSchema spec requires support only for years 1 to 9999, but XMLBeans covers more up to the following limitations
   45       // to avoid losing precision when transforming to a java.util.Date
   46       static final int MAX_YEAR =  292277265;  // is Long.MAX_VALUE ms in years - 1 (for the 11month, 31days, 23h, 59m, 59sec case).
   47       static final int MIN_YEAR = -292275295; // is Long.MIN_VALUE ms in years + 1970 + 1
   48   
   49       // for fast equality comparison, hashing, and serialization
   50       private transient String _canonicalString;
   51       private transient String _string;
   52   
   53       private int _bits;
   54       private int _CY;
   55       private int _M;
   56       private int _D;
   57       private int _h;
   58       private int _m;
   59       private int _s;
   60       private BigDecimal _fs;
   61       private int _tzsign;
   62       private int _tzh;
   63       private int _tzm;
   64       
   65       
   66       /* package */ static final BigDecimal _zero = BigDecimal.valueOf(0);
   67       /* package */ static final BigDecimal _one = BigDecimal.valueOf(1);
   68   
   69       /**
   70        * Constructs a GDate based on a lexical representation.
   71        */
   72       public GDate(CharSequence string)
   73       {
   74           // first trim XML whitespace
   75           int len = string.length();
   76           int start = 0;
   77           while (len > 0 && isSpace(string.charAt(len - 1)))
   78               len -= 1;
   79           while (start < len && isSpace(string.charAt(start)))
   80               start += 1;
   81   
   82           // pick optional timezone off the end
   83           if (len - start >= 1 && string.charAt(len - 1) == 'Z')
   84           {
   85               _bits |= HAS_TIMEZONE;
   86               len -= 1;
   87           }
   88           else if (len - start >= 6)
   89           timezone: {
   90               int tzsign;
   91               int tzhour;
   92               int tzminute;
   93   
   94               if (string.charAt(len - 3) != ':')
   95                   break timezone;
   96   
   97               switch (string.charAt(len - 6))
   98               {
   99                   case '-':
  100                       tzsign = -1; break;
  101                   case '+':
  102                       tzsign = 1; break;
  103                   default:
  104                       break timezone;
  105               }
  106   
  107               tzhour = twoDigit(string, len - 5);
  108               tzminute = twoDigit(string, len - 2);
  109               if (tzhour > 14)
  110                   throw new IllegalArgumentException("time zone hour must be two digits between -14 and +14");
  111               if (tzminute > 59)
  112                   throw new IllegalArgumentException("time zone minute must be two digits between 00 and 59");
  113               _bits |= HAS_TIMEZONE;
  114               _tzsign = tzsign;
  115               _tzh = tzhour;
  116               _tzm = tzminute;
  117               len -= 6;
  118           }
  119   
  120           // pick date fields off the beginning if it doesn't look like a time
  121           if (start < len && (start + 2 >= len || string.charAt(start + 2) != ':'))
  122           scandate:
  123           {
  124               // parse year sign
  125               boolean negyear = false;
  126               if (start < len && string.charAt(start) == '-')
  127               {
  128                   negyear = true;
  129                   start += 1;
  130               }
  131   
  132               // scan year digits
  133               int value = 0;
  134               int digits = -start;
  135               char ch;
  136               boolean startsWithZero = start < len && digitVal(string.charAt(start))==0;
  137   
  138               for (;;)
  139               {
  140                   ch = start < len ? string.charAt(start) : '\0';
  141                   if (!isDigit(ch))
  142                       break;
  143                   
  144                   if ( startsWithZero && start+digits>=4 )
  145                       throw new IllegalArgumentException("year value starting with zero must be 4 or less digits: " + string);
  146   
  147                   value = value * 10 + digitVal(ch);
  148                   start += 1;
  149               }
  150               digits += start;
  151               if (digits > 9)
  152                   throw new IllegalArgumentException("year too long (up to 9 digits)");
  153               else if (digits >= 4)
  154               {
  155                   _bits |= HAS_YEAR;
  156                   _CY =  negyear ? -value : value;
  157                   if (_CY == 0) throw new IllegalArgumentException("year must not be zero");
  158               }
  159               else if (digits > 0)
  160                   throw new IllegalArgumentException("year must be four digits (may pad with zeroes, e.g., 0560)");
  161   
  162               if ( _CY > MAX_YEAR )
  163                   throw new IllegalArgumentException("year value not supported: too big, must be less than " + MAX_YEAR);
  164   
  165               if ( _CY < MIN_YEAR )
  166                   throw new IllegalArgumentException("year values not supported: too small, must be bigger than " + MIN_YEAR);
  167   
  168               // hyphen introduces a month
  169               if (ch != '-')
  170               {
  171                   if (negyear && !hasYear())
  172                       throw new IllegalArgumentException(); // a single minus
  173                   else
  174                       break scandate;
  175               }
  176               start += 1;
  177   
  178               // two-digit month
  179               if (len - start >= 2)
  180               {
  181                   value = twoDigit(string, start);
  182                   if (value >= 1 && value <= 12)
  183                   {
  184                       _bits |= HAS_MONTH;
  185                       _M = value;
  186                       start += 2;
  187                   }
  188               }
  189   
  190               // hyphen introduces a day
  191               ch = start < len ? string.charAt(start) : '\0';
  192               if (ch != '-')
  193               {
  194                   if (!hasMonth())
  195                       throw new IllegalArgumentException(); // minus after a year
  196                   else
  197                       break scandate;
  198               }
  199               start += 1;
  200   
  201               // two-digit day
  202               if (len - start >= 2)
  203               {
  204                   value = twoDigit(string, start);
  205                   if (value >= 1 && value <= 31)
  206                   {
  207                       _bits |= HAS_DAY;
  208                       _D = value;
  209                       start += 2;
  210                   }
  211               }
  212   
  213               if (!hasDay())
  214               {
  215                   // error in the original schema spec permits an extra '-' here
  216                   if (hasMonth() && !hasYear())
  217                   {
  218                       ch = start < len ? string.charAt(start) : '\0';
  219                       if (ch == '-')
  220                       {
  221                           start += 1;
  222                           break scandate;
  223                       }
  224                   }
  225                   throw new IllegalArgumentException(); // minus after a month
  226               }
  227           }
  228   
  229           // time
  230           if (start < len)
  231           {
  232               if (hasYear() || hasMonth() || hasDay())
  233               {
  234                   if (string.charAt(start) != 'T')
  235                      throw new IllegalArgumentException("date and time must be separated by 'T'");
  236                   start += 1;
  237               }
  238   
  239               if (len < start + 8 || string.charAt(start + 2) != ':' || string.charAt(start + 5) != ':')
  240                   throw new IllegalArgumentException();
  241   
  242               int h = twoDigit(string, start);
  243               if (h > 24)
  244                   throw new IllegalArgumentException("hour must be between 00 and 23");
  245               int m = twoDigit(string, start + 3);
  246               if (m >= 60)
  247                   throw new IllegalArgumentException("minute must be between 00 and 59");
  248               int s = twoDigit(string, start + 6);
  249               if (s >= 60)
  250                   throw new IllegalArgumentException("second must be between 00 and 59");
  251   
  252               start += 8;
  253   
  254               BigDecimal fs = _zero;
  255               if (start < len)
  256               {
  257                   if (string.charAt(start) != '.')
  258                       throw new IllegalArgumentException();
  259                   if (start + 1 < len)
  260                   {
  261                       for (int i = start + 1; i < len; i++)
  262                       {
  263                           if (!isDigit(string.charAt(i)))
  264                               throw new IllegalArgumentException();
  265                       }
  266                       try
  267                       {
  268                           fs = new BigDecimal(string.subSequence(start, len).toString());
  269                       }
  270                       catch (Throwable e)
  271                       {
  272                           throw new IllegalArgumentException();
  273                       }
  274                   }
  275               }
  276   
  277               _bits |= HAS_TIME;
  278               _h = h;
  279               _m = m;
  280               _s = s;
  281               _fs = fs;
  282           }
  283   
  284           if ( hasTime() && _h == 24 )
  285           {
  286               if ( _m != 0 || _s != 0 || _fs.compareTo(_zero) != 0 )
  287                   throw new IllegalArgumentException("if hour is 24, minutes, seconds and fraction must be 0");
  288               else
  289               {   // normalize to next day if it has date or at least has day
  290                   if ( hasDate() )
  291                   {
  292                       GDateBuilder gdb = new GDateBuilder(_CY, _M, _D, _h, _m, _s, _fs, _tzsign, _tzh, _tzm);
  293                       gdb.normalize24h();                    
  294   
  295                       _D = gdb.getDay();
  296                       _M = gdb.getMonth();
  297                       _CY = gdb.getYear();
  298                       _h = 0;
  299                   }
  300                   else if ( hasDay() ) // if no date only days increment
  301                   {
  302                       _D++;
  303                       _h = 0;
  304                   }
  305               }
  306           }
  307           
  308           if (!isValid())
  309               throw new IllegalArgumentException("invalid date");
  310       }
  311   
  312       /**
  313        * Constructs a GDate with the specified year, month, day,
  314        * hours, minutes, seconds, and optional fractional seconds, in
  315        * an unspecified timezone.
  316        * <p>
  317        * Note that by not specifying the timezone the GDate
  318        * becomes partially unordered with respect to times that
  319        * do have a specified timezone.
  320        */
  321       public GDate(
  322               int year,
  323               int month,
  324               int day,
  325               int hour,
  326               int minute,
  327               int second,
  328               BigDecimal fraction)
  329       {
  330           _bits = HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME;
  331   
  332           _CY = year;
  333           _M = month;
  334           _D = day;
  335           _h = hour;
  336           _m = minute;
  337           _s = second;
  338           _fs = fraction == null ? _zero : fraction;
  339   
  340           if (!isValid())
  341               throw new IllegalArgumentException();
  342       }
  343   
  344       /**
  345        * Constructs an absolute GDate with the specified year,
  346        * month, day, hours, minutes, seconds, and optional fractional
  347        * seconds, and in the timezone specified.
  348        * <p>
  349        * If you wish to have a time or date that isn't in a specified timezone,
  350        * then use the constructor that does not include the timezone arguments.
  351        */
  352       public GDate(
  353               int year,
  354               int month,
  355               int day,
  356               int hour,
  357               int minute,
  358               int second,
  359               BigDecimal fraction,
  360               int tzSign,
  361               int tzHour,
  362               int tzMinute)
  363       {
  364           _bits = HAS_TIMEZONE | HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME;
  365   
  366           _CY = year;
  367           _M = month;
  368           _D = day;
  369           _h = hour;
  370           _m = minute;
  371           _s = second;
  372           _fs = fraction == null ? _zero : fraction;
  373           _tzsign = tzSign;
  374           _tzh = tzHour;
  375           _tzm = tzMinute;
  376   
  377           if (!isValid())
  378               throw new IllegalArgumentException();
  379       }
  380   
  381       /**
  382        * Constructs a GDate based on a java.util.Date.
  383        * <p>
  384        * The current offset of the default timezone is used as the timezone.
  385        * <p>
  386        * For example, if eastern daylight time is in effect at the given
  387        * date, the timezone on the east coast of the united states
  388        * translates to GMT-05:00 (EST) + 1:00 (DT offset) == GMT-04:00.
  389        */
  390       public GDate(Date date)
  391       {
  392           // requires some date math, so ctor lives on GDateBuilder
  393           this(new GDateBuilder(date));
  394       }
  395   
  396       /**
  397        * Constructs a GDate based on a java.util.Calendar.
  398        * <p>
  399        * If the calendar does not have some fields set, the same absence
  400        * of information is reflected in the GDate.  Note that
  401        * java.util.GregorianCalendar fills in all fields as soon as any
  402        * are fetched, so constructing a GDate with the same calendar object
  403        * twice may result in a different GDate because of a changed calendar.
  404        * Note that org.apache.xmlbeans.XmlCalendar is stable if you re-get a set field,
  405        * so it does not have the same problem.
  406        */
  407       public GDate(Calendar calendar)
  408       {
  409           // we must scrape the "isSet" information out before accessing anything
  410           boolean isSetYear = calendar.isSet(Calendar.YEAR);
  411           boolean isSetEra = calendar.isSet(Calendar.ERA);
  412           boolean isSetMonth = calendar.isSet(Calendar.MONTH);
  413           boolean isSetDay = calendar.isSet(Calendar.DAY_OF_MONTH);
  414           boolean isSetHourOfDay = calendar.isSet(Calendar.HOUR_OF_DAY);
  415           boolean isSetHour = calendar.isSet(Calendar.HOUR);
  416           boolean isSetAmPm = calendar.isSet(Calendar.AM_PM);
  417           boolean isSetMinute = calendar.isSet(Calendar.MINUTE);
  418           boolean isSetSecond = calendar.isSet(Calendar.SECOND);
  419           boolean isSetMillis = calendar.isSet(Calendar.MILLISECOND);
  420           boolean isSetZone = calendar.isSet(Calendar.ZONE_OFFSET);
  421           boolean isSetDst = calendar.isSet(Calendar.DST_OFFSET);
  422   
  423           if (isSetYear)
  424           {
  425               int y = calendar.get(Calendar.YEAR);
  426               if (isSetEra && calendar instanceof GregorianCalendar)
  427                   if (calendar.get(Calendar.ERA) == GregorianCalendar.BC)
  428                       y = -y; //1 - y;
  429               _bits |= HAS_YEAR;
  430               _CY = y;
  431           }
  432           if (isSetMonth)
  433           {
  434               _bits |= HAS_MONTH;
  435               _M = calendar.get(Calendar.MONTH) + 1; // !!note
  436           }
  437           if (isSetDay)
  438           {
  439               _bits |= HAS_DAY;
  440               _D = calendar.get(Calendar.DAY_OF_MONTH);
  441           }
  442           boolean gotTime = false;
  443   
  444           int h = 0;
  445           int m = 0;
  446           int s = 0;
  447           BigDecimal fs = _zero;
  448   
  449           if (isSetHourOfDay)
  450           {
  451               h = calendar.get(Calendar.HOUR_OF_DAY);
  452               gotTime = true;
  453           }
  454           else if (isSetHour && isSetAmPm)
  455           {
  456               h = calendar.get(Calendar.HOUR) + calendar.get(Calendar.AM_PM) * 12;
  457               gotTime = true;
  458           }
  459   
  460           if (isSetMinute)
  461           {
  462               m = calendar.get(Calendar.MINUTE);
  463               gotTime = true;
  464           }
  465   
  466           if (isSetSecond)
  467           {
  468               s = calendar.get(Calendar.SECOND);
  469               gotTime = true;
  470           }
  471   
  472           if (isSetMillis)
  473           {
  474               fs = BigDecimal.valueOf(calendar.get(Calendar.MILLISECOND), 3);
  475               gotTime = true;
  476           }
  477   
  478           if (gotTime)
  479           {
  480               _bits |= HAS_TIME;
  481               _h = h;
  482               _m = m;
  483               _s = s;
  484               _fs = fs;
  485           }
  486   
  487           if (isSetZone)
  488           {
  489               int zoneOffsetInMilliseconds = calendar.get(Calendar.ZONE_OFFSET);
  490               if (isSetDst)
  491                   zoneOffsetInMilliseconds += calendar.get(Calendar.DST_OFFSET);
  492   
  493               _bits |= HAS_TIMEZONE;
  494               if (zoneOffsetInMilliseconds == 0)
  495               {
  496                   _tzsign = 0;
  497                   _tzh = 0;
  498                   _tzm = 0;
  499                   TimeZone zone = calendar.getTimeZone();
  500                   String id = zone.getID();
  501                   if (id != null && id.length() > 3) switch (id.charAt(3))
  502                   {
  503                       case '+': _tzsign = 1; break;   // GMT+00:00
  504                       case '-': _tzsign = -1; break;  // GMT-00:00
  505                   }
  506               }
  507               else
  508               {
  509                   _tzsign = (zoneOffsetInMilliseconds < 0 ? -1 : +1);
  510                   zoneOffsetInMilliseconds = zoneOffsetInMilliseconds * _tzsign;
  511                   _tzh = zoneOffsetInMilliseconds / 3600000;
  512                   _tzm = (zoneOffsetInMilliseconds - _tzh * 3600000) / 60000;
  513               }
  514           }
  515       }
  516   
  517       /**
  518        * Constructs a GDate based on another GDateSpecification.
  519        */
  520       public GDate(GDateSpecification gdate)
  521       {
  522           if (gdate.hasTimeZone())
  523           {
  524               _bits |= HAS_TIMEZONE;
  525               _tzsign = gdate.getTimeZoneSign();
  526               _tzh = gdate.getTimeZoneHour();
  527               _tzm = gdate.getTimeZoneMinute();
  528           }
  529   
  530           if (gdate.hasTime())
  531           {
  532               _bits |= HAS_TIME;
  533               _h = gdate.getHour();
  534               _m = gdate.getMinute();
  535               _s = gdate.getSecond();
  536               _fs = gdate.getFraction();
  537           }
  538   
  539           if (gdate.hasDay())
  540           {
  541               _bits |= HAS_DAY;
  542               _D = gdate.getDay();
  543           }
  544   
  545           if (gdate.hasMonth())
  546           {
  547               _bits |= HAS_MONTH;
  548               _M = gdate.getMonth();
  549           }
  550   
  551           if (gdate.hasYear())
  552           {
  553               _bits |= HAS_YEAR;
  554               _CY = gdate.getYear();
  555           }
  556       }
  557   
  558       /* package */ static final boolean isDigit(char ch)
  559       {
  560           return ((char)(ch - '0') <= '9' - '0'); // char is unsigned
  561       }
  562   
  563       /* package */ static final boolean isSpace(char ch)
  564       {
  565           switch (ch)
  566           {
  567               case ' ':
  568               case '\t':
  569               case '\r':
  570               case '\n':
  571                   return true;
  572               default:
  573                   return false;
  574           }
  575       }
  576   
  577       /* package */ static final int digitVal(char ch)
  578       {
  579           return (ch - '0');
  580       }
  581   
  582       private static final int twoDigit(CharSequence str, int index)
  583       {
  584           char ch1 = str.charAt(index);
  585           char ch2 = str.charAt(index + 1);
  586           if (!isDigit(ch1) || !isDigit(ch2))
  587               return 100; // not two digits
  588           return digitVal(ch1) * 10 + digitVal(ch2);
  589       }
  590   
  591       /**
  592        * Returns true: all GDate instances are immutable.
  593        */
  594       public final boolean isImmutable()
  595       {
  596           return true;
  597       }
  598   
  599       /**
  600        * Returns a combination of flags indicating the information
  601        * contained by this GDate.  The five flags are
  602        * HAS_TIMEZONE, HAS_YEAR, HAS_MONTH, HAS_DAY, and HAS_TIME.
  603        */
  604       public int getFlags()
  605       {
  606           return _bits;
  607       }
  608   
  609       /**
  610        * True if this date/time specification specifies a timezone.
  611        */
  612       public final boolean hasTimeZone()
  613           { return ((_bits & HAS_TIMEZONE) != 0); }
  614   
  615       /**
  616        * True if this date/time specification specifies a year.
  617        */
  618       public final boolean hasYear()
  619           { return ((_bits & HAS_YEAR) != 0); }
  620   
  621       /**
  622        * True if this date/time specification specifies a month-of-year.
  623        */
  624       public final boolean hasMonth()
  625           { return ((_bits & HAS_MONTH) != 0); }
  626   
  627       /**
  628        * True if this date/time specification specifies a day-of-month.
  629        */
  630       public final boolean hasDay()
  631           { return ((_bits & HAS_DAY) != 0); }
  632   
  633       /**
  634        * True if this date/time specification specifies a time-of-day.
  635        */
  636       public final boolean hasTime()
  637           { return ((_bits & HAS_TIME) != 0); }
  638   
  639       /**
  640        * True if this date/time specification specifies a full date (year, month, day)
  641        */
  642       public final boolean hasDate()
  643           { return ((_bits & (HAS_DAY | HAS_MONTH | HAS_YEAR)) == (HAS_DAY | HAS_MONTH | HAS_YEAR)); }
  644   
  645       /**
  646        * Gets the year. Should be a four-digit year specification.
  647        */
  648       public final int getYear()
  649           { return _CY;  }
  650   
  651       /**
  652        * Gets the month-of-year. January is 1.
  653        */
  654       public final int getMonth()
  655           { return _M;  }
  656   
  657       /**
  658        * Gets the day-of-month. The first day of each month is 1.
  659        */
  660       public final int getDay()
  661           { return _D; }
  662   
  663       /**
  664        * Gets the hour-of-day. Midnight is 0, and 11PM is 23.
  665        */
  666       public final int getHour()
  667           { return _h; }
  668   
  669       /**
  670        * Gets the minute-of-hour. Range from 0 to 59.
  671        */
  672       public final int getMinute()
  673           { return _m; }
  674   
  675       /**
  676        * Gets the second-of-minute. Range from 0 to 59.
  677        */
  678       public final int getSecond()
  679           { return _s; }
  680   
  681       /**
  682        * Gets the fraction-of-second. Range from 0 (inclusive) to 1 (exclusive).
  683        */
  684       public final BigDecimal getFraction()
  685           { return _fs; }
  686   
  687       /**
  688        * Gets the time zone sign. For time zones east of GMT,
  689        * this is positive; for time zones west, this is negative.
  690        */
  691       public final int getTimeZoneSign()
  692           { return _tzsign; }
  693   
  694       /**
  695        * Gets the time zone hour.
  696        *
  697        * This is always positive: for the sign, look at
  698        * getTimeZoneSign().
  699        */
  700       public final int getTimeZoneHour()
  701           { return _tzh; }
  702   
  703       /**
  704        * Gets the time zone minutes.
  705        *
  706        * This is always positive: for the sign, look at
  707        * getTimeZoneSign().
  708        */
  709       public final int getTimeZoneMinute()
  710           { return _tzm; }
  711   
  712       /**
  713        * Gets the rounded millisecond value. Range from 0 to 999
  714        */
  715       public int getMillisecond()
  716       {
  717           if (_fs == null)
  718               return 0;
  719           return _fs.setScale(3, BigDecimal.ROUND_DOWN).unscaledValue().intValue();
  720       }
  721   
  722       /**
  723        * The canonical string representation. Specific moments or
  724        * times-of-day in a specified timezone are normalized to
  725        * UTC time to produce a canonical string form for them.
  726        * Other recurring time specifications keep their timezone
  727        * information.
  728        */
  729       public String canonicalString()
  730       {
  731           ensureCanonicalString();
  732           return _canonicalString;
  733       }
  734   
  735       /**
  736        * True if this GDate corresponds to a valid gregorian date value
  737        * in XML schema.
  738        */
  739       public boolean isValid()
  740       {
  741           return GDateBuilder.isValidGDate(this);
  742       }
  743   
  744       /**
  745        * Returns the Julian date corresponding to this Gregorian date.
  746        * The Julian date (JD) is a continuous count of days from
  747        * 1 January 4713 BC.
  748        */
  749       public int getJulianDate()
  750       {
  751           return GDateBuilder.julianDateForGDate(this);
  752       }
  753   
  754       /**
  755        * Retrieves the value of the current time as an {@link XmlCalendar}.
  756        * <p>
  757        * {@link XmlCalendar} is a subclass of {@link java.util.GregorianCalendar}
  758        * which is slightly customized to match XML schema date rules.
  759        * <p>
  760        * The returned {@link XmlCalendar} has only those time and date fields
  761        * set that are reflected in the GDate object.  Because of the way the
  762        * {@link java.util.Calendar} contract works, any information in the isSet() vanishes
  763        * as soon as you view any unset field using get() methods.
  764        * This means that if it is important to understand which date fields
  765        * are set, you must call isSet() first before get().
  766        */
  767       public XmlCalendar getCalendar()
  768       {
  769           return new XmlCalendar(this);
  770       }
  771   
  772   
  773       /**
  774        * Retrieves the value of the current time as a java.util.Date
  775        * instance.
  776        */
  777       public Date getDate()
  778       {
  779           return GDateBuilder.dateForGDate(this);
  780       }
  781   
  782       /**
  783        * Comparison to another GDate.
  784        * <ul>
  785        * <li>Returns -1 if this < date. (less-than)
  786        * <li>Returns 0 if this == date. (equal)
  787        * <li>Returns 1 if this > date. (greater-than)
  788        * <li>Returns 2 if this <> date. (incomparable)
  789        * </ul>
  790        * Two instances are incomparable if they have different amounts
  791        * of information.
  792        */
  793       public int compareToGDate(GDateSpecification datespec)
  794       {
  795           return GDateBuilder.compareGDate(this, datespec);
  796       }
  797   
  798       /**
  799        * Returns the builtin type code for the shape of the information
  800        * contained in this instance, or 0 if the
  801        * instance doesn't contain information corresponding to a
  802        * Schema type.
  803        * <p> 
  804        * Value will be equal to
  805        * {@link SchemaType#BTC_NOT_BUILTIN},
  806        * {@link SchemaType#BTC_G_YEAR},
  807        * {@link SchemaType#BTC_G_YEAR_MONTH},
  808        * {@link SchemaType#BTC_G_MONTH},
  809        * {@link SchemaType#BTC_G_MONTH_DAY},
  810        * {@link SchemaType#BTC_G_DAY},
  811        * {@link SchemaType#BTC_DATE},
  812        * {@link SchemaType#BTC_DATE_TIME}, or
  813        * {@link SchemaType#BTC_TIME}.
  814        */
  815       public int getBuiltinTypeCode()
  816       {
  817           return GDateBuilder.btcForFlags(_bits);
  818       }
  819   
  820       /**
  821        * Adds a duration to this GDate, and returns a new GDate.
  822        */
  823       public GDate add(GDurationSpecification duration)
  824       {
  825           GDateBuilder builder = new GDateBuilder(this);
  826           builder.addGDuration(duration);
  827           return builder.toGDate();
  828       }
  829   
  830       /**
  831        * Adds a duration to this GDate, and returns a new GDate.
  832        */
  833       public GDate subtract(GDurationSpecification duration)
  834       {
  835           GDateBuilder builder = new GDateBuilder(this);
  836           builder.subtractGDuration(duration);
  837           return builder.toGDate();
  838       }
  839   
  840       /**
  841        * GDate is an immutable class, and equality is computed based
  842        * on its canonical value.
  843        */
  844       public boolean equals(Object obj)
  845       {
  846           if (obj == this)
  847               return true;
  848           if (!(obj instanceof GDate))
  849               return false;
  850   
  851           ensureCanonicalString();
  852           return _canonicalString.equals(((GDate)obj).canonicalString());
  853       }
  854   
  855       /**
  856        * Returns a hash code for this GDate.
  857        */
  858       public int hashCode()
  859       {
  860           ensureCanonicalString();
  861           return _canonicalString.hashCode();
  862       }
  863   
  864       /**
  865        * The canonical string representation. Specific moments or
  866        * times-of-day in a specified timezone are normalized to
  867        * UTC time to produce a canonical string form for them.
  868        * Other recurring time specifications keep their timezone
  869        * information.
  870        */
  871       private void ensureCanonicalString()
  872       {
  873           if (_canonicalString != null)
  874               return;
  875   
  876           boolean needNormalize =
  877               (hasTimeZone() && getTimeZoneSign() != 0 && hasTime() &&
  878               ((hasDay() == hasMonth() && hasDay() == hasYear())));
  879   
  880           if (!needNormalize && getFraction() != null && getFraction().scale() > 0)
  881           {
  882               BigInteger bi = getFraction().unscaledValue();
  883               needNormalize = (bi.mod(GDateBuilder.TEN).signum() == 0);
  884           }
  885   
  886           if (!needNormalize)
  887               _canonicalString = toString();
  888           else
  889           {
  890               GDateBuilder gdb = new GDateBuilder(this);
  891               gdb.normalize();
  892               _canonicalString = gdb.toString();
  893           }
  894       }
  895   
  896       /**
  897        * The natural string representation. This represents the information
  898        * that is available, including timezone. For types that correspond
  899        * to defined schema types (schemaBuiltinTypeCode() > 0),
  900        * this provides the natural lexical representation.
  901        * <p>
  902        * When both time and timezone are specified, this string is not
  903        * the canonical representation unless the timezone is UTC (Z)
  904        * (since the same moment in time can be expressed in different
  905        * timezones). To get a canonical string, use the canonicalString()
  906        * method.
  907        */
  908       public String toString()
  909       {
  910           if (_string == null)
  911               _string = formatGDate(this);
  912           return _string;
  913       }
  914   
  915       private final static char[] _tensDigit =
  916       {
  917           '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
  918           '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
  919           '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
  920           '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
  921           '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
  922           '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
  923           '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
  924           '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
  925           '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
  926           '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
  927       };
  928       private final static char[] _onesDigit =
  929       {
  930           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  931           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  932           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  933           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  934           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  935           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  936           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  937           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  938           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  939           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  940       };
  941   
  942       private static final int _padTwoAppend(char[] b, int i, int n)
  943       {
  944           assert(n >= 0 && n < 100);
  945           b[i] = _tensDigit[n];
  946           b[i + 1] = _onesDigit[n];
  947           return i + 2;
  948       }
  949   
  950       private static final int _padFourAppend(char[] b, int i, int n)
  951       {
  952           if (n < 0)
  953           {
  954               b[i++] = '-';
  955               n = -n;
  956           }
  957           if (n >= 10000)
  958           {
  959               String s = Integer.toString(n);
  960               s.getChars(0, s.length(), b, i);
  961               return i + s.length();
  962           }
  963           int q = n / 100;
  964           int r = n - q * 100;
  965           b[i] = _tensDigit[q];
  966           b[i + 1] = _onesDigit[q];
  967           b[i + 2] = _tensDigit[r];
  968           b[i + 3] = _onesDigit[r];
  969           return i + 4;
  970       }
  971       
  972       private static final TimeZone GMTZONE = TimeZone.getTimeZone("GMT");
  973       private static final TimeZone[] MINUSZONE =
  974               {
  975                   TimeZone.getTimeZone("GMT-00:00"),
  976                   TimeZone.getTimeZone("GMT-01:00"),
  977                   TimeZone.getTimeZone("GMT-02:00"),
  978                   TimeZone.getTimeZone("GMT-03:00"),
  979                   TimeZone.getTimeZone("GMT-04:00"),
  980                   TimeZone.getTimeZone("GMT-05:00"),
  981                   TimeZone.getTimeZone("GMT-06:00"),
  982                   TimeZone.getTimeZone("GMT-07:00"),
  983                   TimeZone.getTimeZone("GMT-08:00"),
  984                   TimeZone.getTimeZone("GMT-09:00"),
  985                   TimeZone.getTimeZone("GMT-10:00"),
  986                   TimeZone.getTimeZone("GMT-11:00"),
  987                   TimeZone.getTimeZone("GMT-12:00"),
  988                   TimeZone.getTimeZone("GMT-13:00"),
  989                   TimeZone.getTimeZone("GMT-14:00"),
  990               };
  991       private static final TimeZone[] PLUSZONE =
  992               {
  993                   TimeZone.getTimeZone("GMT+00:00"),
  994                   TimeZone.getTimeZone("GMT+01:00"),
  995                   TimeZone.getTimeZone("GMT+02:00"),
  996                   TimeZone.getTimeZone("GMT+03:00"),
  997                   TimeZone.getTimeZone("GMT+04:00"),
  998                   TimeZone.getTimeZone("GMT+05:00"),
  999                   TimeZone.getTimeZone("GMT+06:00"),
 1000                   TimeZone.getTimeZone("GMT+07:00"),
 1001                   TimeZone.getTimeZone("GMT+08:00"),
 1002                   TimeZone.getTimeZone("GMT+09:00"),
 1003                   TimeZone.getTimeZone("GMT+10:00"),
 1004                   TimeZone.getTimeZone("GMT+11:00"),
 1005                   TimeZone.getTimeZone("GMT+12:00"),
 1006                   TimeZone.getTimeZone("GMT+13:00"),
 1007                   TimeZone.getTimeZone("GMT+14:00"),
 1008               };
 1009   
 1010       /* package */ static final TimeZone timeZoneForGDate(GDateSpecification date)
 1011       {
 1012           // use a cached timezone if integral; otherwise make a new one.
 1013           if (!date.hasTimeZone())
 1014               return TimeZone.getDefault();
 1015           if (date.getTimeZoneSign() == 0)
 1016               return GMTZONE;
 1017           if (date.getTimeZoneMinute() == 0 && date.getTimeZoneHour() <= 14 && date.getTimeZoneHour() >= 0)
 1018               return date.getTimeZoneSign() < 0 ? MINUSZONE[date.getTimeZoneHour()] : PLUSZONE[date.getTimeZoneHour()];
 1019           
 1020           char[] zb = new char[9];
 1021           zb[0] = 'G';
 1022           zb[1] = 'M';
 1023           zb[2] = 'T';
 1024           zb[3] = (date.getTimeZoneSign() < 0) ? '-' : '+';
 1025           GDate._padTwoAppend(zb, 4, date.getTimeZoneHour());
 1026           zb[6] = ':';
 1027           GDate._padTwoAppend(zb, 7, date.getTimeZoneMinute());
 1028           return TimeZone.getTimeZone(new String(zb));
 1029       }
 1030       
 1031       /* package */ static String formatGDate(GDateSpecification spec)
 1032       {
 1033           // We've used a char[] rather than a StringBuffer for a 4x speedup
 1034           // -YY(10)YY-MM-DDTHH:MM:SS.FFFFFF+ZH:ZM
 1035           // 1 + 10   + 3+ 3+ 3+ 3+ 3+1 + s + 3+ 3 = 33 + s
 1036           BigDecimal fs = spec.getFraction();
 1037           char[] message = new char[33 + (fs == null ? 0 : fs.scale())];
 1038           int i = 0;
 1039   
 1040           if (spec.hasYear() || spec.hasMonth() || spec.hasDay())
 1041           {
 1042               dmy: {
 1043                   if (spec.hasYear())
 1044                       i = _padFourAppend(message, 0, spec.getYear());
 1045                   else
 1046                       message[i++] = '-';
 1047   
 1048                   if (!(spec.hasMonth() || spec.hasDay()))
 1049                       break dmy;
 1050   
 1051                   message[i++] = '-';
 1052                   if (spec.hasMonth())
 1053                       i = _padTwoAppend(message, i, spec.getMonth());
 1054   
 1055                   if (!spec.hasDay())
 1056                       break dmy;
 1057   
 1058                   message[i++] = '-';
 1059                   i = _padTwoAppend(message, i, spec.getDay());
 1060                   break dmy;
 1061               }
 1062               if (spec.hasTime())
 1063                   message[i++] = 'T';
 1064           }
 1065   
 1066           if (spec.hasTime())
 1067           {
 1068               i = _padTwoAppend(message, i, spec.getHour());
 1069               message[i++] = ':';
 1070               i = _padTwoAppend(message, i, spec.getMinute());
 1071               message[i++] = ':';
 1072               i = _padTwoAppend(message, i, spec.getSecond());
 1073               if (fs != _zero) // (optimization ~3%)
 1074               {
 1075                   String frac = fs.toString();
 1076                   int point = frac.indexOf('.');
 1077                   if (point >= 0)
 1078                   {
 1079                       frac.getChars(point, frac.length(), message, i);
 1080                       i += frac.length() - point;
 1081                   }
 1082               }
 1083           }
 1084   
 1085           if (spec.hasTimeZone())
 1086           {
 1087               if (spec.getTimeZoneSign() == 0)
 1088               {
 1089                   message[i++] = 'Z';
 1090               }
 1091               else
 1092               {
 1093                   message[i++] = spec.getTimeZoneSign() > 0 ? '+' : '-';
 1094                   i = _padTwoAppend(message, i, spec.getTimeZoneHour());
 1095                   message[i++] = ':';
 1096                   i = _padTwoAppend(message, i, spec.getTimeZoneMinute());
 1097               }
 1098           }
 1099   
 1100           // it would be nice to use (0, i, message) ctor instead
 1101           return new String(message, 0, i);
 1102       }
 1103   
 1104   }
 1105   

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