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.TimeZone;
   23   
   24   /**
   25    * Used to build {@link GDate GDates}.
   26    * <p>
   27    * Like GDate, a GDateBuilder represents an Gregorian Date, Time,
   28    * and Timezone, or subset of information (Year, Month, Day,
   29    * Time, Timezone, or some combination). Wherever it provides
   30    * guidance, the XML Schema 1.0 specification (plus published
   31    * errata) is followed.
   32    * <p>
   33    * Instances may separately set or clear the year, month,
   34    * day-of-month, and time-of-day. Not all operations are
   35    * meaningful on all combinations. In particular, timezone
   36    * normalization is only possible if there is a time, or
   37    * a time together with a full date.
   38    */
   39   public final class GDateBuilder implements GDateSpecification, java.io.Serializable
   40   {
   41       private static final long serialVersionUID = 1L;
   42       
   43       private int _bits;
   44       private int _CY;
   45       private int _M;
   46       private int _D;
   47       private int _h;
   48       private int _m;
   49       private int _s;
   50       private BigDecimal _fs;
   51       private int _tzsign;
   52       private int _tzh;
   53       private int _tzm;
   54   
   55       /**
   56        * Constructs a GDateBuilder specifying no date or time
   57        */
   58       public GDateBuilder()
   59       {
   60       }
   61   
   62       /**
   63        * Builds another GDateBuilder with the same value
   64        * as this one.
   65        */
   66       public Object clone()
   67       {
   68           return new GDateBuilder(this);
   69       }
   70   
   71       /**
   72        * Builds a GDate from this GDateBuilder.
   73        */
   74       public GDate toGDate()
   75       {
   76           return new GDate(this);
   77       }
   78   
   79       /**
   80        * Construts a GDateBuilder by copying another GDateSpecificaiton.
   81        */
   82       public GDateBuilder(GDateSpecification gdate)
   83       {
   84           if (gdate.hasTimeZone())
   85               setTimeZone(gdate.getTimeZoneSign(), gdate.getTimeZoneHour(), gdate.getTimeZoneMinute());
   86   
   87           if (gdate.hasTime())
   88               setTime(gdate.getHour(), gdate.getMinute(), gdate.getSecond(), gdate.getFraction());
   89   
   90           if (gdate.hasDay())
   91               setDay(gdate.getDay());
   92   
   93           if (gdate.hasMonth())
   94               setMonth(gdate.getMonth());
   95   
   96           if (gdate.hasYear())
   97               setYear(gdate.getYear());
   98       }
   99   
  100       // Forms:
  101   
  102       // Date part:
  103       // Year:       (-?\d{4,})
  104       // YearMonth:  (-?\d{4,})-(\d{2})
  105       // Date:       (-?\d{4,})-(\d{2})-(\d{2})
  106       // Month:      --(\d{2})(--)?              //errata R-48
  107       // MonthDay:   --(\d{2})-(\d{2})
  108       // Day:        ---(\d{2})
  109   
  110       // Time part:
  111       // Time:       (\d{2}):(\d{2}):(\d{2})(.\d*)?
  112   
  113       // Timezone part:
  114       // TZ:         (Z)|([+-]\d{2}):(\d{2})
  115   
  116       /**
  117        * Constructs a GDateBuilder from a lexical
  118        * representation. The lexical space contains the
  119        * union of the lexical spaces of all the schema
  120        * date/time types (except for duration).
  121        */
  122       public GDateBuilder(CharSequence string)
  123       {
  124           this(new GDate(string));
  125       }
  126   
  127   
  128       public GDateBuilder(Calendar calendar)
  129       {
  130           this(new GDate(calendar));
  131       }
  132   
  133       /**
  134        * Constructs a GDateBuilder with the specified year, month, day,
  135        * hours, minutes, seconds, and optional fractional seconds, in
  136        * an unspecified timezone.
  137        * <p>
  138        * Note that by not specifying the timezone the GDateBuilder
  139        * becomes partially unordered with respect to timesthat do have a
  140        * specified timezone.
  141        * 
  142        * @param year The year
  143        * @param month The month, from 1-12
  144        * @param day The day of month, from 1-31
  145        * @param hour The hour of day, from 0-23
  146        * @param minute The minute of hour, from 0-59
  147        * @param second The second of minute, from 0-59
  148        * @param fraction The fraction of second, 0.0 to 0.999... (may be null) 
  149        */
  150       public GDateBuilder(
  151               int year,
  152               int month,
  153               int day,
  154               int hour,
  155               int minute,
  156               int second,
  157               BigDecimal fraction)
  158       {
  159           _bits = HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME;
  160   
  161           if (year == 0)
  162               throw new IllegalArgumentException();
  163   
  164           _CY = (year > 0 ? year : year + 1);
  165           _M = month;
  166           _D = day;
  167           _h = hour;
  168           _m = minute;
  169           _s = second;
  170           _fs = fraction == null ? GDate._zero : fraction;
  171   
  172           if (!isValid())
  173               throw new IllegalArgumentException();
  174       }
  175   
  176       /**
  177        * Constructs an absolute GDateBuilder with the specified year,
  178        * month, day, hours, minutes, seconds, and optional fractional
  179        * seconds, and in the timezone specified.
  180        * <p>
  181        * Note that you can reexpress the GDateBuilder in any timezone using
  182        * normalizeToTimeZone(). The normalize() method normalizes to UTC.
  183        * <p>
  184        * If you wish to have a time or date that isn't in a specified timezone,
  185        * then use the constructor that does not include the timezone arguments.
  186        *
  187        * @param year the year
  188        * @param month the month, from 1-12
  189        * @param day the day of month, from 1-31
  190        * @param hour the hour of day, from 0-23
  191        * @param minute the minute of hour, from 0-59
  192        * @param second the second of minute, from 0-59
  193        * @param fraction the fraction of second, 0.0 to 0.999... (may be null)
  194        * @param tzSign the timezone offset sign, either +1, 0, or -1
  195        * @param tzHour the timezone offset hour
  196        * @param tzMinute the timezone offset minute 
  197        */
  198       public GDateBuilder(
  199               int year,
  200               int month,
  201               int day,
  202               int hour,
  203               int minute,
  204               int second,
  205               BigDecimal fraction,
  206               int tzSign,
  207               int tzHour,
  208               int tzMinute)
  209       {
  210           _bits = HAS_TIMEZONE | HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME;
  211   
  212           if (year == 0)
  213               throw new IllegalArgumentException();
  214   
  215           _CY = (year > 0 ? year : year + 1);
  216           _M = month;
  217           _D = day;
  218           _h = hour;
  219           _m = minute;
  220           _s = second;
  221           _fs = fraction == null ? GDate._zero : fraction;
  222           _tzsign = tzSign;
  223           _tzh = tzHour;
  224           _tzm = tzMinute;
  225   
  226           if (!isValid())
  227               throw new IllegalArgumentException();
  228       }
  229   
  230       /**
  231        * Constructs a GDateBuilder based on a java.util.Date.
  232        * <p>
  233        * The current offset of the default timezone is used as the timezone.
  234        * <p>
  235        * For example, if eastern daylight time is in effect at the given
  236        * date, the timezone on the east coast of the united states
  237        * translates to GMT-05:00 (EST) + 1:00 (DT offset) == GMT-04:00.
  238        * 
  239        * @param date the date object to copy
  240        */
  241       public GDateBuilder(Date date)
  242       {
  243           setDate(date);
  244       }
  245   
  246       /**
  247        * True if the instance is immutable.
  248        */
  249       public boolean isImmutable()
  250       {
  251           return false;
  252       }
  253   
  254       /**
  255        * Returns a combination of flags indicating the information
  256        * contained by this GDate.  The five flags are
  257        * HAS_TIMEZONE, HAS_YEAR, HAS_MONTH, HAS_DAY, and HAS_TIME.
  258        */
  259       public int getFlags()
  260       {
  261           return _bits;
  262       }
  263   
  264       /**
  265        * True if this date/time specification specifies a timezone.
  266        */
  267       public final boolean hasTimeZone()
  268           { return ((_bits & HAS_TIMEZONE) != 0); }
  269   
  270       /**
  271        * True if this date/time specification specifies a year.
  272        */
  273       public final boolean hasYear()
  274           { return ((_bits & HAS_YEAR) != 0); }
  275   
  276       /**
  277        * True if this date/time specification specifies a month-of-year.
  278        */
  279       public final boolean hasMonth()
  280           { return ((_bits & HAS_MONTH) != 0); }
  281   
  282       /**
  283        * True if this date/time specification specifies a day-of-month.
  284        */
  285       public final boolean hasDay()
  286           { return ((_bits & HAS_DAY) != 0); }
  287   
  288       /**
  289        * True if this date/time specification specifies a time-of-day.
  290        */
  291       public final boolean hasTime()
  292           { return ((_bits & HAS_TIME) != 0); }
  293   
  294       /**
  295        * True if this date/time specification specifies a full date (year, month, day)
  296        */
  297       public final boolean hasDate()
  298           { return ((_bits & (HAS_DAY | HAS_MONTH | HAS_YEAR)) == (HAS_DAY | HAS_MONTH | HAS_YEAR)); }
  299   
  300       /**
  301        * Gets the year. Should be a four-digit year specification.
  302        */
  303       public final int getYear()
  304           { return (_CY > 0 ? _CY : _CY - 1);  }
  305   
  306       /**
  307        * Gets the month-of-year. January is 1.
  308        */
  309       public final int getMonth()
  310           { return _M;  }
  311   
  312       /**
  313        * Gets the day-of-month. The first day of each month is 1.
  314        */
  315       public final int getDay()
  316           { return _D; }
  317   
  318       /**
  319        * Gets the hour-of-day. Midnight is 0, and 11PM is 23.
  320        */
  321       public final int getHour()
  322           { return _h; }
  323   
  324       /**
  325        * Gets the minute-of-hour. Range from 0 to 59.
  326        */
  327       public final int getMinute()
  328           { return _m; }
  329   
  330       /**
  331        * Gets the second-of-minute. Range from 0 to 59.
  332        */
  333       public final int getSecond()
  334           { return _s; }
  335   
  336   
  337       /**
  338        * Gets the fraction-of-second. Range from 0 (inclusive) to 1 (exclusive).
  339        */
  340       public final BigDecimal getFraction()
  341           { return _fs; }
  342   
  343   
  344       /**
  345        * Gets the rounded millisecond value. Range from 0 to 999
  346        */
  347       public final int getMillisecond()
  348       {
  349           if (_fs == null || _fs == GDate._zero)
  350               return 0;
  351           return _fs.setScale(3, BigDecimal.ROUND_HALF_UP).unscaledValue().intValue();
  352       }
  353   
  354       /**
  355        * Gets the time zone sign. For time zones east of GMT,
  356        * this is positive; for time zones west, this is negative.
  357        */
  358       public final int getTimeZoneSign()
  359           { return _tzsign; }
  360   
  361       /**
  362        * Gets the time zone hour.
  363        * This is always positive: for the sign, look at
  364        * getTimeZoneSign().
  365        */
  366       public final int getTimeZoneHour()
  367           { return _tzh; }
  368   
  369       /**
  370        * Gets the time zone minutes.
  371        * This is always positive: for the sign, look at
  372        * getTimeZoneSign().
  373        */
  374       public final int getTimeZoneMinute()
  375           { return _tzm; }
  376   
  377       
  378   
  379       /**
  380        * Sets the year. Should be a four-digit year specification.
  381        * @param year the year
  382        */
  383       public void setYear(int year)
  384       {
  385           if (year < GDate.MIN_YEAR || year > GDate.MAX_YEAR)
  386               throw new IllegalArgumentException("year out of range");
  387           if (year == 0)
  388               throw new IllegalArgumentException("year cannot be 0");
  389           _bits |= HAS_YEAR; _CY = (year > 0 ? year : year + 1);
  390       }
  391   
  392       /**
  393        * Sets the month-of-year. January is 1.
  394        * @param month the month, from 1-12
  395        */
  396       public void setMonth(int month)
  397       {
  398           if (month < 1 || month > 12)
  399               throw new IllegalArgumentException("month out of range");
  400           _bits |= HAS_MONTH; _M = month;
  401       }
  402   
  403       /**
  404        * Sets the day-of-month. The first day of each month is 1.
  405        * @param day the day of month, from 1-31
  406        */
  407       public void setDay(int day)
  408       {
  409           if (day < 1 || day > 31)
  410               throw new IllegalArgumentException("day out of range");
  411           _bits |= HAS_DAY; _D = day;
  412       }
  413   
  414       /**
  415        * Sets the time. Hours in the day range from 0 to 23;
  416        * minutes and seconds range from 0 to 59; and fractional
  417        * seconds range from 0 (inclusive) to 1 (exclusive).
  418        * The fraction can be null and is assumed to be zero.
  419        * @param hour the hour of day, from 0-23 or 24 only if min, sec and fraction are 0
  420        * @param minute the minute of hour, from 0-59
  421        * @param second the second of minute, from 0-59
  422        * @param fraction the fraction of second, 0.0 to 0.999... (may be null)
  423        */
  424       public void setTime(int hour, int minute, int second, BigDecimal fraction)
  425       {
  426           if (hour < 0 || hour > 24 )
  427               throw new IllegalArgumentException("hour out of range");
  428           if (minute < 0 || minute > 59)
  429               throw new IllegalArgumentException("minute out of range");
  430           if (second < 0 || second > 59)
  431               throw new IllegalArgumentException("second out of range");
  432           if (fraction != null && (fraction.signum() < 0 || fraction.compareTo(GDate._one) > 1))
  433               throw new IllegalArgumentException("fraction out of range");
  434           if ( hour == 24 && (minute!=0 || second!=0 || (fraction!=null && (GDate._zero.compareTo(fraction)!=0)) ))
  435               throw new IllegalArgumentException("when hour is 24, min sec and fracton must be 0");
  436   
  437           _bits |= HAS_TIME;
  438           _h = hour;
  439           _m = minute;
  440           _s = second;
  441           _fs = fraction == null ? GDate._zero : fraction;
  442       }
  443   
  444       /**
  445        * Sets the time zone without changing the other time
  446        * fields. If you with to adjust other time fields to express
  447        * the same actual moment in time in a different time zone,
  448        * use normalizeToTimeZone.
  449        * <p>
  450        * Timezones must be between -14:00 and +14:00. Sign
  451        * must be -1 or 1 (or 0 for UTC only), and the offset hours
  452        * and minute arguments must be nonnegative.
  453        * 
  454        * @param tzSign the timezone offset sign, either +1, 0, or -1
  455        * @param tzHour the timezone offset hour
  456        * @param tzMinute the timezone offset minute 
  457        */
  458       public void setTimeZone(int tzSign, int tzHour, int tzMinute)
  459       {
  460           if (!((tzSign == 0 && tzHour == 0 && tzMinute == 0) ||
  461                 ((tzSign == -1 || tzSign == 1) &&
  462                  (tzHour >= 0 && tzMinute >= 0) &&
  463                  (tzHour == 14 && tzMinute == 0 || tzHour < 14 && tzMinute < 60))))
  464               throw new IllegalArgumentException("time zone out of range (-14:00 to +14:00). (" +
  465                   (tzSign<0 ? "-" : "+") + tzHour + ":" + tzMinute + ")");
  466   
  467           _bits |= HAS_TIMEZONE;
  468           _tzsign = tzSign;
  469           _tzh = tzHour;
  470           _tzm = tzMinute;
  471       }
  472       
  473       /**
  474        * Sets the time zone based on a number of offset minutes rather
  475        * than sign/hour/minute; for example, setTimeZone(-60) is the
  476        * same as setTimeZone(-1, 1, 0).
  477        */
  478       public void setTimeZone(int tzTotalMinutes)
  479       {
  480           if (tzTotalMinutes < -14 * 60 || tzTotalMinutes > 14 * 60)
  481               throw new IllegalArgumentException("time zone out of range (-840 to 840 minutes). (" + tzTotalMinutes + ")");
  482           
  483           int tzSign = tzTotalMinutes < 0 ? -1 : tzTotalMinutes > 0 ? 1 : 0;
  484           tzTotalMinutes *= tzSign;
  485           int tzH = tzTotalMinutes / 60;
  486           int tzM = tzTotalMinutes - tzH * 60;
  487           
  488           setTimeZone(tzSign, tzH, tzM);
  489       }
  490   
  491       /**
  492        * Clears the year. After clearing, hasYear returns false and the
  493        * value of getYear is undefined.
  494        */
  495       public void clearYear()
  496           { _bits &= ~HAS_YEAR; _CY = 0;  }
  497   
  498       /**
  499        * Clears the month-of-year. After clearing. hasMonth returns false and
  500        * the value of getMonth is undefined.
  501        */
  502       public void clearMonth()
  503           { _bits &= ~HAS_MONTH; _M = 0;  }
  504   
  505       /**
  506        * Clears the day-of-month. After clearing. hasDay returns false and
  507        * the value of getDay is undefined.
  508        */
  509       public void clearDay()
  510           { _bits &= ~HAS_DAY; _D = 0;  }
  511   
  512       /**
  513        * Clears the time-of-day.
  514        * After clearing. hasTime returns false and
  515        * the value of getTime is undefined.
  516        */
  517       public void clearTime()
  518       {
  519           _bits &= ~HAS_TIME;
  520           _h = 0;
  521           _m = 0;
  522           _s = 0;
  523           _fs = null;
  524       }
  525   
  526       /**
  527        * Clears the timezone. After clearing. hasTimeZone returns false and
  528        * the value of getTimeZoneHour and getTimeZoneMinute are undefined.
  529        * Does not change the other time fields.
  530        */
  531       public void clearTimeZone()
  532       {
  533           _bits &= ~HAS_TIMEZONE;
  534           _tzsign = 0;
  535           _tzh = 0;
  536           _tzm = 0;
  537       }
  538   
  539       /**
  540        * True if all date fields lie within their legal ranges.  A GDateBuilder
  541        * can be invalid, for example, if you change the month to February
  542        * and the day-of-month is 31.
  543        */
  544       public boolean isValid()
  545       {
  546           return isValidGDate(this);
  547       }
  548   
  549       /* package */ static final boolean isValidGDate(GDateSpecification date)
  550       {
  551           if (date.hasYear() && date.getYear() == 0)
  552               return false;
  553   
  554           if (date.hasMonth() && (date.getMonth() < 1 || date.getMonth() > 12))
  555               return false;
  556   
  557           if (date.hasDay() && 
  558               (date.getDay() < 1 || date.getDay() > 31 || 
  559                date.getDay() > 28 && 
  560                date.hasMonth() && 
  561                (date.hasYear() ? 
  562                 date.getDay() > _maxDayInMonthFor((date.getYear() > 0 ? 
  563                                                    date.getYear() : 
  564                                                    date.getYear() + 1), 
  565                                                   date.getMonth()) : 
  566                 date.getDay() > _maxDayInMonth(date.getMonth()))))
  567               return false;
  568   
  569           if (date.hasTime() && ((date.getHour() < 0 || date.getHour() > 23 ||
  570               date.getMinute() < 0 || date.getMinute() > 59 ||
  571               date.getSecond() < 0 || date.getSecond() > 59 ||
  572               date.getFraction().signum() < 0 || date.getFraction().compareTo(GDate._one) >= 0)  ) &&
  573               // check for 24:00:00 valid format
  574               !(date.getHour() == 24 && date.getMinute() == 0 && date.getSecond() == 0 &&
  575                 date.getFraction().compareTo(GDate._zero) == 0 ) )
  576               return false;
  577   
  578           if (date.hasTimeZone() &&
  579               (!((date.getTimeZoneSign() == 0 && date.getTimeZoneHour() == 0 && date.getTimeZoneMinute() == 0) ||
  580                 ((date.getTimeZoneSign() == -1 || date.getTimeZoneSign() == +1) &&
  581                   // NB: allow +00:00 and -00:00
  582                   // (date.getTimeZoneHour() == 0 && date.getTimeZoneMinute() > 0 || date.getTimeZoneHour() > 0 && date.getTimeZoneMinute() >= 0) &&
  583                  (date.getTimeZoneHour() >= 0 && date.getTimeZoneMinute() >= 0) &&
  584                  (date.getTimeZoneHour() == 14 && date.getTimeZoneMinute() == 0 || date.getTimeZoneHour() < 14 && date.getTimeZoneMinute() < 60)))))
  585               return false;
  586   
  587           // everyting looks kosher
  588           return true;
  589       }
  590   
  591   
  592       /**
  593        * Normalizes the instance, ensuring date and time fields are within
  594        * their normal ranges.
  595        * <p>
  596        * If no timezone or no time is specified, or if a partial date is specified, this
  597        * method does nothing, and leaves the timezone information as-is.
  598        * <p>
  599        * If a time or time and date is specified, this method normalizes the timezone
  600        * to UTC.
  601        */
  602       public void normalize()
  603       {
  604           // DateTime or Time, with TimeZone: normalize to UTC.
  605           // In the process all the fields will be normalized.
  606           if (hasDay() == hasMonth() && hasDay() == hasYear() &&
  607               hasTimeZone() && hasTime() )
  608           {
  609               normalizeToTimeZone(0, 0, 0);
  610           }
  611           else
  612           {
  613               // No timezone, or incomplete date.
  614               _normalizeTimeAndDate();
  615           }
  616   
  617           // remove trailing zeros from fractional seconds
  618           if (hasTime() && _fs != null && _fs.scale() > 0)
  619           {
  620               if (_fs.signum() == 0)
  621                   _fs = GDate._zero;
  622               else
  623               {
  624                   BigInteger bi = _fs.unscaledValue();
  625                   String str = bi.toString();
  626                   int lastzero;
  627                   for (lastzero = str.length(); lastzero > 0; lastzero -= 1)
  628                       if (str.charAt(lastzero - 1) != '0')
  629                           break;
  630                   if (lastzero < str.length())
  631                       _fs = _fs.setScale(_fs.scale() - str.length() + lastzero);
  632               }
  633           }
  634       }
  635   
  636      /**
  637        * Normalizes the instance when hour is 24. If day is present, hour 24 is equivalent to hour 00 next day.
  638        */
  639       void normalize24h()
  640       {
  641           if ( !hasTime() || getHour()!=24 )
  642               return;
  643   
  644           _normalizeTimeAndDate();
  645       }
  646   
  647   
  648       private void _normalizeTimeAndDate()
  649       {
  650           long carry = 0;
  651   
  652           if (hasTime())
  653               carry = _normalizeTime();
  654   
  655           if (hasDay())
  656               _D += carry;
  657   
  658           if (hasDate())
  659           {
  660               _normalizeDate();
  661           }
  662           else if (hasMonth())
  663           {
  664               // with incomplete dates, just months can be normalized:
  665               // days stay denormalized.
  666               if (_M < 1 || _M > 12)
  667               {
  668                   int temp = _M;
  669                   _M = _modulo(temp, 1, 13);
  670                   if (hasYear())
  671                       _CY = _CY + (int)_fQuotient(temp, 1, 13);
  672               }
  673           }
  674       }
  675   
  676       /**
  677        * If the time and timezone are known, this method changes the timezone to the
  678        * specified UTC offset, altering minutes, hours, day, month, and year as
  679        * necessary to ensure that the actual described moment in time is the same.
  680        * <p>
  681        * It is an error to operate on instances without a time or timezone, or
  682        * with a partially specified date.
  683        * 
  684        * @param tzSign the timezone offset sign, either +1, 0, or -1
  685        * @param tzHour the timezone offset hour
  686        * @param tzMinute the timezone offset minute 
  687        */
  688       public void normalizeToTimeZone(int tzSign, int tzHour, int tzMinute)
  689       {
  690           if (!((tzSign == 0 && tzHour == 0 && tzMinute == 0) ||
  691                 ((tzSign == -1 || tzSign == 1) &&
  692                  (tzHour >= 0 && tzMinute >= 0) &&
  693                  (tzHour == 14 && tzMinute == 0 || tzHour < 14 && tzMinute < 60))))
  694               throw new IllegalArgumentException("time zone must be between -14:00 and +14:00");
  695   
  696           if (!hasTimeZone() || !hasTime())
  697               throw new IllegalStateException("cannot normalize time zone without both time and timezone");
  698   
  699           if (!(hasDay() == hasMonth() && hasDay() == hasYear()))
  700               throw new IllegalStateException("cannot do date math without a complete date");
  701   
  702           int hshift = tzSign * tzHour - _tzsign * _tzh;
  703           int mshift = tzSign * tzMinute - _tzsign * _tzm;
  704   
  705           _tzsign = tzSign;
  706           _tzh = tzHour;
  707           _tzm = tzMinute;
  708           addDuration(1, 0, 0, 0, hshift, mshift, 0, null);
  709       }
  710   
  711       /**
  712        * Normalizes to a time zone specified by a number of offset minutes rather
  713        * than sign/hour/minute; for example, normalizeToTimeZone(-60) is the
  714        * same as normalizeToTimeZone(-1, 1, 0).
  715        */
  716       public void normalizeToTimeZone(int tzTotalMinutes)
  717       {
  718           if (tzTotalMinutes < -14 * 60 || tzTotalMinutes > 14 * 60)
  719               throw new IllegalArgumentException("time zone out of range (-840 to 840 minutes). (" + tzTotalMinutes + ")");
  720           
  721           int tzSign = tzTotalMinutes < 0 ? -1 : tzTotalMinutes > 0 ? 1 : 0;
  722           tzTotalMinutes *= tzSign;
  723           int tzH = tzTotalMinutes / 60;
  724           int tzM = tzTotalMinutes - tzH * 60;
  725           
  726           normalizeToTimeZone(tzSign, tzH, tzM);
  727       }
  728   
  729   
  730       /**
  731        * Adds a given duration to the date/time.
  732        * 
  733        * @param duration the duration to add
  734        */
  735       public void addGDuration(GDurationSpecification duration)
  736       {
  737           addDuration(duration.getSign(), duration.getYear(), duration.getMonth(), duration.getDay(),
  738                       duration.getHour(), duration.getMinute(), duration.getSecond(), duration.getFraction());
  739       }
  740   
  741       /**
  742        * Subtracts a given duration from the date/time.
  743        * 
  744        * @param duration the duration to subtract
  745        */
  746       public void subtractGDuration(GDurationSpecification duration)
  747       {
  748           addDuration(-duration.getSign(), duration.getYear(), duration.getMonth(), duration.getDay(),
  749                       duration.getHour(), duration.getMinute(), duration.getSecond(), duration.getFraction());
  750       }
  751   
  752   
  753       /**
  754        * Normalizes the date by carrying over to the year any months outside 1..12
  755        * and carrying over to the month any days outside 1..(days-in-month).
  756        */
  757       private void _normalizeDate()
  758       {
  759           if (_M < 1 || _M > 12 || _D < 1 || _D > _maxDayInMonthFor(_CY, _M))
  760           {
  761               // fix months first
  762               int temp = _M;
  763               _M = _modulo(temp, 1, 13);
  764               _CY = _CY + (int)_fQuotient(temp, 1, 13);
  765   
  766               // then pull days out
  767               int extradays = _D - 1;
  768               _D = 1;
  769   
  770               // then use the julian date function to fix
  771               setJulianDate(getJulianDate() + extradays);
  772           }
  773       }
  774   
  775       /**
  776        * Normalizes time so that fractions are 0..1(exc), seconds/minutes 0..59,
  777        * and hours 0..24. Returns the number of days to carry over from normalizing
  778        * away more than 24 hours.
  779        */
  780       private long _normalizeTime()
  781       {
  782           long carry = 0;
  783           long temp;
  784   
  785           // fractions
  786           if (_fs != null && (_fs.signum() < 0 || _fs.compareTo(GDate._one) >= 0))
  787           {
  788               BigDecimal bdcarry = _fs.setScale(0, BigDecimal.ROUND_FLOOR);
  789               _fs = _fs.subtract(bdcarry);
  790               carry = bdcarry.longValue();
  791           }
  792   
  793           if (carry != 0 || _s < 0 || _s > 59 || _m < 0 || _m > 50 || _h < 0 || _h > 23)
  794           {
  795               // seconds
  796               temp = _s + carry;
  797               carry = _fQuotient(temp, 60);
  798               _s = _mod(temp, 60, carry);
  799   
  800               // minutes
  801               temp = _m + carry;
  802               carry = _fQuotient(temp, 60);
  803               _m = _mod(temp, 60, carry);
  804   
  805               // hours
  806               temp = _h + carry;
  807               carry = _fQuotient(temp, 24);
  808               _h = _mod(temp, 24, carry);
  809           }
  810   
  811           return carry;
  812       }
  813   
  814       /**
  815        * Adds a given duration to the date/time.
  816        * 
  817        * @param sign +1 to add, -1 to subtract
  818        * @param year the number of years to add
  819        * @param month the number of months to add
  820        * @param day the number of days to add
  821        * @param hour the number of hours to add
  822        * @param minute the number of minutes to add
  823        * @param second the number of seconds to add
  824        * @param fraction the number of fractional seconds to add (may be null)
  825        */
  826       public void addDuration(int sign, int year, int month, int day,
  827                               int hour, int minute, int second, BigDecimal fraction)
  828       {
  829           boolean timemath = hour != 0 || minute != 0 || second != 0 || fraction != null && fraction.signum() != 0;
  830           if (timemath && !hasTime())
  831               throw new IllegalStateException("cannot do time math without a complete time");
  832           boolean datemath = hasDay() && (day != 0 || timemath);
  833           if (datemath && !hasDate())
  834               throw new IllegalStateException("cannot do date math without a complete date");
  835   
  836           int temp;
  837   
  838           // months + years are easy
  839           if (month != 0 || year != 0)
  840           {
  841               // Prepare the _D to be pegged before changing month
  842               if (hasDay())
  843                   _normalizeDate();
  844   
  845               // Add months and years
  846               temp = _M + sign * month;
  847               _M = _modulo(temp, 1, 13);
  848               _CY = _CY + sign * year + (int)_fQuotient(temp, 1, 13);
  849   
  850               // In new month, day may need to be pegged before proceeding
  851               if (hasDay())
  852               {
  853                   assert(_D >= 1);
  854                   temp = _maxDayInMonthFor(_CY, _M);
  855                   if (_D > temp)
  856                       _D = temp;
  857               }
  858           }
  859   
  860           long carry = 0;
  861   
  862           if (timemath)
  863           {
  864               // fractions
  865               if (fraction != null && fraction.signum() != 0)
  866               {
  867                   if (_fs.signum() == 0 && sign == 1)
  868                       _fs = fraction;
  869                   else
  870                       _fs = (sign == 1) ? _fs.add(fraction) : _fs.subtract(fraction);
  871               }
  872   
  873               // seconds, minutes, hours
  874               _s += sign * second;
  875               _m += sign * minute;
  876               _h += sign * hour;
  877   
  878               // normalize time
  879               carry = _normalizeTime();
  880           }
  881   
  882           if (datemath)
  883           {
  884               // days: may require renormalization
  885               _D += sign * day + carry;
  886               _normalizeDate();
  887           }
  888       }
  889   
  890       /**
  891        * Given {year,month} computes maximum
  892        * number of days for given month
  893        */
  894       private static int _maxDayInMonthFor(int year, int month)
  895       {
  896           if (month == 4 || month == 6 || month == 9 || month == 11)
  897               return 30;
  898   
  899           if (month == 2)
  900               return (_isLeapYear(year) ? 29 : 28);
  901   
  902           return 31;
  903       }
  904   
  905       /**
  906        * Given {year,month} computes maximum
  907        * number of days for given month
  908        */
  909       private static int _maxDayInMonth(int month)
  910       {
  911           if (month == 4 || month == 6 || month == 9 || month == 11)
  912               return 30;
  913   
  914           if (month == 2)
  915               return 29;
  916   
  917           return 31;
  918       }
  919   
  920       /**
  921        * Returns the Julian date corresponding to this Gregorian date.
  922        * The Julian date (JD) is a continuous count of days from
  923        * 1 January 4713 BC.
  924        */
  925       public final int getJulianDate()
  926       {
  927           return julianDateForGDate(this);
  928       }
  929   
  930   
  931       /**
  932        * Sets the Gregorian date based on the given Julian date.
  933        * The Julian date (JD) is a continuous count of days from
  934        * 1 January 4713 BC.
  935        * 
  936        * @param julianday the julian day number
  937        */
  938       public void setJulianDate(int julianday)
  939       {
  940           if (julianday < 0)
  941               throw new IllegalArgumentException("date before year -4713");
  942   
  943           int temp;
  944           int qepoc;
  945   
  946           // from http://aa.usno.navy.mil/faq/docs/JD_Formula.html
  947           temp = julianday + 68569;
  948           qepoc = 4 * temp / 146097;
  949           temp = temp - (146097 * qepoc + 3) / 4;
  950           _CY = 4000 * (temp + 1) / 1461001;
  951           temp = temp - 1461 * _CY / 4 + 31;
  952           _M = 80 * temp / 2447;
  953           _D = temp - 2447 * _M / 80;
  954           temp = _M / 11;
  955           _M = _M + 2 - 12 * temp;
  956           _CY = 100 * (qepoc - 49) + _CY + temp;
  957   
  958           _bits |= HAS_DAY | HAS_MONTH | HAS_YEAR;
  959       }
  960   
  961   
  962       /**
  963        * Sets the current time and date based on a java.util.Date instance.
  964        * <p>
  965        * The timezone offset used is based on the default TimeZone. (The
  966        * default TimeZone is consulted to incorporate daylight savings offsets
  967        * if applicable for the current date as well as the base timezone offset.)
  968        * <p>
  969        * If you wish to normalize the timezone, e.g., to UTC, follow this with
  970        * a call to normalizeToTimeZone.
  971        * 
  972        * @param date the Date object to copy
  973        */
  974       public void setDate(Date date)
  975       {
  976           // Default timezone
  977           TimeZone dtz = TimeZone.getDefault();
  978           int offset = dtz.getOffset(date.getTime());
  979           int offsetsign = 1;
  980           if (offset < 0)
  981           {
  982               offsetsign = -1;
  983               offset = -offset;
  984           }
  985           int offsetmin = offset / (1000 * 60);
  986           int offsethr = offsetmin / 60;
  987           offsetmin = offsetmin - offsethr * 60;
  988   
  989           setTimeZone(offsetsign, offsethr, offsetmin);
  990           
  991           // paranoia: tz.getOffset can return fractions of minutes, but we must round
  992           int roundedoffset = offsetsign * (offsethr * 60 + offsetmin) * 60 * 1000;
  993   
  994           // midnight
  995           setTime(0, 0, 0, GDate._zero);
  996   
  997           // Set to January 1, 1970.
  998           // setJulianDate(2440588);
  999           _bits |= HAS_DAY | HAS_MONTH | HAS_YEAR;
 1000           _CY = 1970;
 1001           _M = 1;
 1002           _D = 1;
 1003   
 1004           // Add a duration representing the number of milliseconds
 1005           addGDuration(new GDuration(1, 0, 0, 0, 0, 0, 0,
 1006                   BigDecimal.valueOf(date.getTime() + roundedoffset, 3)));
 1007   
 1008           // special case: ss.000 -> ss
 1009           if (_fs.signum() == 0)
 1010               _fs = GDate._zero;
 1011       }
 1012       
 1013       /**
 1014        * Copies a GDateSpecification, completely replacing the current
 1015        * information in this GDateBuilder.
 1016        * 
 1017        * @param gdate the GDateSpecification to copy
 1018        */ 
 1019       public void setGDate(GDateSpecification gdate)
 1020       {
 1021           _bits = gdate.getFlags() & (HAS_TIMEZONE | HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME);
 1022           int year = gdate.getYear();
 1023           _CY = (year > 0 ? year : year + 1);
 1024           _M = gdate.getMonth();
 1025           _D = gdate.getDay();
 1026           _h = gdate.getHour();
 1027           _m = gdate.getMinute();
 1028           _s = gdate.getSecond();
 1029           _fs = gdate.getFraction();
 1030           _tzsign = gdate.getTimeZoneSign();
 1031           _tzh = gdate.getTimeZoneHour();
 1032           _tzm = gdate.getTimeZoneMinute();
 1033       }
 1034   
 1035   
 1036       /**
 1037        * Retrieves the value of the current time as an {@link XmlCalendar}.
 1038        * <p>
 1039        * {@link XmlCalendar} is a subclass of {@link java.util.GregorianCalendar}
 1040        * which is slightly customized to match XML schema date rules.
 1041        * <p>
 1042        * The returned {@link XmlCalendar} has only those time and date fields
 1043        * set that are reflected in the GDate object.  Because of the way the
 1044        * {@link java.util.Calendar} contract works, any information in the isSet() vanishes
 1045        * as soon as you view any unset field using get() methods.
 1046        * This means that if it is important to understand which date fields
 1047        * are set, you must call isSet() first before get().
 1048        */
 1049       public XmlCalendar getCalendar()
 1050       {
 1051           return new XmlCalendar(this);
 1052       }
 1053   
 1054       /**
 1055        * Retrieves the value of the current time as a java.util.Date
 1056        * instance.
 1057        */
 1058       public Date getDate()
 1059       {
 1060           return dateForGDate(this);
 1061       }
 1062   
 1063       /* package */ static int julianDateForGDate(GDateSpecification date)
 1064       {
 1065           if (!date.hasDate())
 1066               throw new IllegalStateException("cannot do date math without a complete date");
 1067   
 1068           // from http://aa.usno.navy.mil/faq/docs/JD_Formula.html
 1069           int day = date.getDay();
 1070           int month = date.getMonth();
 1071           int year = date.getYear();
 1072           year = (year > 0 ? year : year + 1);
 1073           int result = day-32075+1461*(year+4800+(month-14)/12)/4+
 1074               367*(month-2-(month-14)/12*12)/12-3*((year+4900+(month-14)/12)/100)/4;
 1075   
 1076           if (result < 0)
 1077               throw new IllegalStateException("date too far in the past (year allowed to -4713)");
 1078   
 1079           return result;
 1080       }
 1081   
 1082       /* package */ static Date dateForGDate(GDateSpecification date)
 1083       {
 1084           long jDate = julianDateForGDate(date);
 1085           long to1970Date = jDate - 2440588;
 1086           long to1970Ms = 1000 * 60 * 60 * 24 * to1970Date;
 1087   
 1088           to1970Ms += date.getMillisecond();
 1089           to1970Ms += date.getSecond() * 1000;
 1090           to1970Ms += date.getMinute() * 60 * 1000;
 1091           to1970Ms += date.getHour() * 60 * 60 * 1000;
 1092           if (date.hasTimeZone())
 1093           {
 1094               to1970Ms -= (date.getTimeZoneMinute() * date.getTimeZoneSign()) * 60 * 1000;
 1095               to1970Ms -= (date.getTimeZoneHour() * date.getTimeZoneSign()) * 60 * 60 * 1000;
 1096           }
 1097           else
 1098           {
 1099               TimeZone def = TimeZone.getDefault();
 1100               int offset = def.getOffset(to1970Ms);
 1101               to1970Ms -= offset;
 1102           }
 1103   
 1104           return new Date(to1970Ms);
 1105       }
 1106   
 1107       /**
 1108        * True for leap years.
 1109        */
 1110       private static boolean _isLeapYear(int year)
 1111       {
 1112           // BUGBUG: Julian calendar?
 1113           return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
 1114       }
 1115   
 1116       /**
 1117        * fQuotient(a, b) = the greatest integer less than or equal to a/b
 1118        */
 1119       private static final long _fQuotient(long a, int b)
 1120       {
 1121           if ((a < 0) == (b < 0))
 1122               return a / b;
 1123   
 1124           return -((b - a - 1) / b);
 1125       }
 1126   
 1127       /**
 1128        * modulo(a, b) = a - fQuotient(a,b)*b
 1129        */
 1130       private static int _mod(long a, int b, long quotient)
 1131       {
 1132           return (int)(a - quotient*b) ;
 1133       }
 1134   
 1135       /**
 1136        * modulo(a - low, high - low) + low
 1137        */
 1138       private static final int _modulo(long temp, int low, int high)
 1139       {
 1140           long a = temp - low;
 1141           int b = high - low;
 1142           return (_mod(a, b, _fQuotient(a, b)) + low) ;
 1143       }
 1144   
 1145       /**
 1146        * Quotient(a - low, high - low)
 1147        */
 1148       private static final long _fQuotient(long temp, int low, int high)
 1149       {
 1150           return _fQuotient(temp - low, high - low);
 1151       }
 1152   
 1153       /**
 1154        * Sets to the first possible moment that matches the given
 1155        * specification.
 1156        */
 1157       private void _setToFirstMoment()
 1158       {
 1159           // 1584 was the first leap year during which the Gregorian
 1160           // calendar was in use: seems like the most reasonable "first"
 1161           // year to use in absence of a year.
 1162   
 1163           if (!hasYear())
 1164               setYear(1584);
 1165   
 1166           if (!hasMonth())
 1167               setMonth(1);
 1168   
 1169           if (!hasDay())
 1170               setDay(1);
 1171   
 1172           if (!hasTime())
 1173               setTime(0, 0, 0, GDate._zero);
 1174       }
 1175   
 1176       /**
 1177        * Comparison to another GDate.
 1178        * <ul>
 1179        * <li>Returns -1 if this < date. (less-than)
 1180        * <li>Returns 0 if this == date. (equal)
 1181        * <li>Returns 1 if this > date. (greater-than)
 1182        * <li>Returns 2 if this <> date. (incomparable)
 1183        * </ul>
 1184        * Two instances are incomparable if they have different amounts
 1185        * of information.
 1186        * 
 1187        * @param datespec the date to compare against
 1188        */
 1189       public final int compareToGDate(GDateSpecification datespec)
 1190       {
 1191           return compareGDate(this, datespec);
 1192       }
 1193   
 1194   
 1195       /* package */ static final int compareGDate(GDateSpecification tdate, GDateSpecification datespec)
 1196       {
 1197           // same amount of information: looks good
 1198           int bitdiff = tdate.getFlags() ^ datespec.getFlags();
 1199   
 1200           easy: if ((bitdiff & (HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME | HAS_TIMEZONE)) == 0)
 1201           {
 1202               // If the other date needs to be normalized to
 1203               // our timezone, make a clone and do so if possible
 1204               if (tdate.hasTimeZone() &&
 1205                   (datespec.getTimeZoneHour() != tdate.getTimeZoneHour() ||
 1206                    datespec.getTimeZoneMinute() != tdate.getTimeZoneMinute() ||
 1207                    datespec.getTimeZoneSign() != tdate.getTimeZoneSign()))
 1208               {
 1209                   datespec = new GDateBuilder(datespec);
 1210   
 1211                   int flags = tdate.getFlags() & (HAS_YEAR | HAS_MONTH | HAS_DAY);
 1212                   if (flags != 0 && flags != (HAS_YEAR | HAS_MONTH | HAS_DAY) || !tdate.hasTime())
 1213                   {
 1214                       // in these cases we'll need to fill in fields
 1215                       ((GDateBuilder)datespec)._setToFirstMoment();
 1216                       tdate = new GDateBuilder(tdate);
 1217                       ((GDateBuilder)tdate)._setToFirstMoment();
 1218                   }
 1219   
 1220                   ((GDateBuilder)datespec).normalizeToTimeZone(tdate.getTimeZoneSign(), tdate.getTimeZoneHour(), tdate.getTimeZoneMinute());
 1221               }
 1222   
 1223               // compare by field
 1224               return fieldwiseCompare(tdate, datespec);
 1225           }
 1226   
 1227           // different amounts of information (except timezone): not comparable
 1228           if ((bitdiff & (HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME)) != 0)
 1229               return 2;
 1230   
 1231           // The schema spec says we should try to compare with-timezone and
 1232           // without-timezone specifications... Well, OK, sure, if they say so.
 1233   
 1234           // We don't have a timezone but the other does: reverse the call
 1235           if (!tdate.hasTimeZone())
 1236           {
 1237               int result = compareGDate(datespec, tdate);
 1238               return result == 2 ? 2 : -result;
 1239           }
 1240   
 1241           // Now tdate is guaranteed to have a timezone and datespec not.
 1242   
 1243           // To muck with the times, make clones
 1244           GDateBuilder pdate = new GDateBuilder(tdate);
 1245   
 1246           // To cover the one uncovered case: if one date is 02/28 and the
 1247           // other date is 03/01, shift days closer by one to simulate being
 1248           // the last day of the month within a leap year
 1249           if ((tdate.getFlags() & (HAS_YEAR | HAS_MONTH | HAS_DAY)) == (HAS_MONTH | HAS_DAY))
 1250           {
 1251               if (tdate.getDay() == 28 && tdate.getMonth() == 2)
 1252               {
 1253                   if (datespec.getDay() == 01 && datespec.getMonth() == 3)
 1254                   {
 1255                       pdate.setDay(29);
 1256                   }
 1257               }
 1258               else if (datespec.getDay() == 28 && datespec.getMonth() == 2)
 1259               {
 1260                   if (tdate.getDay() == 01 && tdate.getMonth() == 3)
 1261                   {
 1262                       pdate.setMonth(02);
 1263                       pdate.setDay(29);
 1264                   }
 1265               }
 1266           }
 1267   
 1268           // For timespans, compare by first instant of time
 1269           // possible. Therefore, fill in Midnight, January 1, 1584 (a leap year)
 1270           // in absence of other information.
 1271           pdate._setToFirstMoment();
 1272   
 1273           // P < Q if P < (Q with time zone +14:00)
 1274           GDateBuilder qplusdate = new GDateBuilder(datespec);
 1275           qplusdate._setToFirstMoment();
 1276           qplusdate.setTimeZone(1, 14, 0);
 1277           qplusdate.normalizeToTimeZone(tdate.getTimeZoneSign(), tdate.getTimeZoneHour(), tdate.getTimeZoneMinute());
 1278           if (fieldwiseCompare(pdate, qplusdate) == -1)
 1279               return -1;
 1280   
 1281           // P > Q if P > (Q with time zone -14:00)
 1282           GDateBuilder qminusdate = qplusdate;
 1283           qminusdate.setGDate(datespec);
 1284           qminusdate._setToFirstMoment();
 1285           qminusdate.setTimeZone(-1, 14, 0);
 1286           qminusdate.normalizeToTimeZone(tdate.getTimeZoneSign(), tdate.getTimeZoneHour(), tdate.getTimeZoneMinute());
 1287           if (fieldwiseCompare(pdate, qminusdate) == 1)
 1288               return 1;
 1289   
 1290           // P <> Q otherwise
 1291           return 2;
 1292       }
 1293   
 1294       /**
 1295        * Does a simple most-significant-digit-first comparison,
 1296        * ignoring any timezone or has/doesn't have issues.
 1297        * The data must have been digested first.
 1298        */
 1299       private static int fieldwiseCompare(GDateSpecification tdate, GDateSpecification date)
 1300       {
 1301           if (tdate.hasYear())
 1302           {
 1303               int CY = date.getYear();
 1304               int TCY = tdate.getYear();
 1305               if (TCY < CY) return -1;
 1306               if (TCY > CY) return 1;
 1307           }
 1308           if (tdate.hasMonth())
 1309           {
 1310               int M = date.getMonth();
 1311               int TM = tdate.getMonth();
 1312               if (TM < M) return -1;
 1313               if (TM > M) return 1;
 1314           }
 1315           if (tdate.hasDay())
 1316           {
 1317               int D = date.getDay();
 1318               int TD = tdate.getDay();
 1319               if (TD < D) return -1;
 1320               if (TD > D) return 1;
 1321           }
 1322           if (tdate.hasTime())
 1323           {
 1324               int h = date.getHour();
 1325               int th = tdate.getHour();
 1326               if (th < h) return -1;
 1327               if (th > h) return 1;
 1328               int m = date.getMinute();
 1329               int tm = tdate.getMinute();
 1330               if (tm < m) return -1;
 1331               if (tm > m) return 1;
 1332               int s = date.getSecond();
 1333               int ts = tdate.getSecond();
 1334               if (ts < s) return -1;
 1335               if (ts > s) return 1;
 1336               BigDecimal fs = date.getFraction();
 1337               BigDecimal tfs = tdate.getFraction();
 1338               if (tfs == null && fs == null) return 0;
 1339               return (tfs == null ? GDate._zero : tfs).compareTo(fs == null ? GDate._zero : fs);
 1340           }
 1341   
 1342           return 0;
 1343       }
 1344   
 1345       /**
 1346        * Returns the builtin type code for the shape of the information
 1347        * contained in this instance, or 0 if the
 1348        * instance doesn't contain information corresponding to a
 1349        * Schema type.
 1350        * <p> 
 1351        * Value will be equal to
 1352        * {@link SchemaType#BTC_NOT_BUILTIN},
 1353        * {@link SchemaType#BTC_G_YEAR},
 1354        * {@link SchemaType#BTC_G_YEAR_MONTH},
 1355        * {@link SchemaType#BTC_G_MONTH},
 1356        * {@link SchemaType#BTC_G_MONTH_DAY},
 1357        * {@link SchemaType#BTC_G_DAY},
 1358        * {@link SchemaType#BTC_DATE},
 1359        * {@link SchemaType#BTC_DATE_TIME}, or
 1360        * {@link SchemaType#BTC_TIME}.
 1361        */
 1362       public final int getBuiltinTypeCode()
 1363       {
 1364           return btcForFlags(_bits);
 1365       }
 1366   
 1367       /* package */ static int btcForFlags(int flags)
 1368       {
 1369           switch (flags & (HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME))
 1370           {
 1371               case HAS_YEAR:
 1372                   return SchemaType.BTC_G_YEAR;
 1373               case HAS_YEAR | HAS_MONTH:
 1374                   return SchemaType.BTC_G_YEAR_MONTH;
 1375               case HAS_MONTH:
 1376                   return SchemaType.BTC_G_MONTH;
 1377               case HAS_MONTH | HAS_DAY:
 1378                   return SchemaType.BTC_G_MONTH_DAY;
 1379               case HAS_DAY:
 1380                   return SchemaType.BTC_G_DAY;
 1381               case HAS_YEAR | HAS_MONTH | HAS_DAY:
 1382                   return SchemaType.BTC_DATE;
 1383               case HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME:
 1384                   return SchemaType.BTC_DATE_TIME;
 1385               case HAS_TIME:
 1386                   return SchemaType.BTC_TIME;
 1387               default:
 1388                   return SchemaType.BTC_NOT_BUILTIN;
 1389           }
 1390       }
 1391   
 1392       /**
 1393        * Clears the fields in this GDateBuilder that are not applicable
 1394        * for the given SchemaType date code.  The code should be
 1395        * {@link SchemaType#BTC_G_YEAR},
 1396        * {@link SchemaType#BTC_G_YEAR_MONTH},
 1397        * {@link SchemaType#BTC_G_MONTH},
 1398        * {@link SchemaType#BTC_G_MONTH_DAY},
 1399        * {@link SchemaType#BTC_G_DAY},
 1400        * {@link SchemaType#BTC_DATE},
 1401        * {@link SchemaType#BTC_DATE_TIME}, or
 1402        * {@link SchemaType#BTC_TIME}.
 1403        * 
 1404        * @param typeCode the type code to apply
 1405        */
 1406       public void setBuiltinTypeCode(int typeCode)
 1407       {
 1408           switch (typeCode)
 1409           {
 1410               case SchemaType.BTC_G_YEAR:
 1411                   //HAS_YEAR
 1412                   clearMonth();
 1413                   clearDay();
 1414                   clearTime();
 1415                   return;
 1416               case SchemaType.BTC_G_YEAR_MONTH:
 1417                   //HAS_YEAR | HAS_MONTH
 1418                   clearDay();
 1419                   clearTime();
 1420                   return;
 1421               case SchemaType.BTC_G_MONTH:
 1422                   //HAS_MONTH
 1423                   clearYear();
 1424                   clearDay();
 1425                   clearTime();
 1426                   return;
 1427               case SchemaType.BTC_G_MONTH_DAY:
 1428                   //HAS_MONTH | HAS_DAY
 1429                   clearYear();
 1430                   clearTime();
 1431                   return;
 1432               case SchemaType.BTC_G_DAY:
 1433                   //HAS_DAY
 1434                   clearYear();
 1435                   clearMonth();
 1436                   clearTime();
 1437                   return;
 1438               case SchemaType.BTC_DATE:
 1439                   //HAS_YEAR | HAS_MONTH | HAS_DAY
 1440                   clearTime();
 1441                   return;
 1442               case SchemaType.BTC_DATE_TIME:
 1443                   //HAS_YEAR | HAS_MONTH | HAS_DAY | HAS_TIME
 1444                   return;
 1445               case SchemaType.BTC_TIME:
 1446                   //HAS_TIME
 1447                   clearYear();
 1448                   clearMonth();
 1449                   clearDay();
 1450                   return;
 1451               default:
 1452                   throw new IllegalArgumentException("codeType must be one of SchemaType BTC_  DATE TIME related types.");
 1453           }
 1454       }
 1455   
 1456   
 1457       /* package */ static final BigInteger TEN = BigInteger.valueOf(10);
 1458   
 1459       /**
 1460        * The canonical string representation. Specific moments or
 1461        * times-of-day in a specified timezone are normalized to
 1462        * UTC time to produce a canonical string form for them.
 1463        * Other recurring time specifications keep their timezone
 1464        * information.
 1465        */
 1466       public String canonicalString()
 1467       {
 1468           boolean needNormalize =
 1469               (hasTimeZone() && getTimeZoneSign() != 0 && hasTime() &&
 1470               ((hasDay() == hasMonth() && hasDay() == hasYear())));
 1471   
 1472           if (!needNormalize && getFraction()!=null && getFraction().scale() > 0)
 1473           {
 1474               BigInteger bi = getFraction().unscaledValue();
 1475               needNormalize = (bi.mod(TEN).signum() == 0);
 1476           }
 1477   
 1478           if (!needNormalize)
 1479               return toString();
 1480   
 1481           GDateBuilder cdate = new GDateBuilder(this);
 1482           cdate.normalize();
 1483           return cdate.toString();
 1484       }
 1485   
 1486       /**
 1487        * The natural string representation. This represents the information
 1488        * that is available, including timezone. For types that correspond
 1489        * to defined schema types (schemaBuiltinTypeCode() > 0),
 1490        * this provides the natural lexical representation.
 1491        * <p>
 1492        * When both time and timezone are specified, this string is not
 1493        * the canonical representation unless the timezone is UTC (Z)
 1494        * (since the same moment in time can be expressed in different
 1495        * timezones). To get a canonical string, use the canonicalString()
 1496        * method.
 1497        */
 1498       public final String toString()
 1499       {
 1500           return GDate.formatGDate(this);
 1501       }
 1502   
 1503   }

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