1 /*
   2  * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.org.apache.xerces.internal.jaxp.datatype;
  27 
  28 import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter;
  29 import java.io.IOException;
  30 import java.io.ObjectInputStream;
  31 import java.io.Serializable;
  32 import java.math.BigDecimal;
  33 import java.math.BigInteger;
  34 import java.math.RoundingMode;
  35 import java.util.Calendar;
  36 import java.util.Date;
  37 import java.util.GregorianCalendar;
  38 import java.util.Locale;
  39 import java.util.TimeZone;
  40 import javax.xml.datatype.DatatypeConstants;
  41 import javax.xml.datatype.Duration;
  42 import javax.xml.datatype.XMLGregorianCalendar;
  43 import javax.xml.namespace.QName;
  44 import jdk.xml.internal.SecuritySupport;
  45 
  46 /**
  47  * <p>Representation for W3C XML Schema 1.0 date/time datatypes.
  48  * Specifically, these date/time datatypes are
  49  * {@link DatatypeConstants#DATETIME dateTime},
  50  * {@link DatatypeConstants#TIME time},
  51  * {@link DatatypeConstants#DATE date},
  52  * {@link DatatypeConstants#GYEARMONTH gYearMonth},
  53  * {@link DatatypeConstants#GMONTHDAY gMonthDay},
  54  * {@link DatatypeConstants#GYEAR gYear},
  55  * {@link DatatypeConstants#GMONTH gMonth} and
  56  * {@link DatatypeConstants#GDAY gDay}
  57  * defined in the XML Namespace
  58  * <code>"http://www.w3.org/2001/XMLSchema"</code>.
  59  * These datatypes are normatively defined in
  60  * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">W3C XML Schema 1.0 Part 2, Section 3.2.7-14</a>.</p>
  61  *
  62  * <p>The table below defines the mapping between XML Schema 1.0
  63  * date/time datatype fields and this class' fields. It also summarizes
  64  * the value constraints for the date and time fields defined in
  65  * <a href="http://www.w3.org/TR/xmlschema-2/#isoformats">W3C XML Schema 1.0 Part 2, Appendix D,
  66  * <i>ISO 8601 Date and Time Formats</i></a>.</p>
  67  *
  68  * <a name="datetimefieldsmapping"/>
  69  * <table border="2" rules="all" cellpadding="2">
  70  *   <thead>
  71  *     <tr>
  72  *       <th align="center" colspan="3">
  73  *         Date/time datatype field mapping between XML Schema 1.0 and Java representation
  74  *       </th>
  75  *     </tr>
  76  *   </thead>
  77  *   <tbody>
  78  *     <tr>
  79  *       <th>XML Schema 1.0<br/>
  80  *           datatype<br/>
  81  *            field</th>
  82  *       <th>Related<br/>XMLGregorianCalendar<br/>Accessor(s)</th>
  83  *       <th>Value Range</th>
  84  *     </tr>
  85  *     <a name="datetimefield-year"/>
  86  *     <tr>
  87  *       <td> year </td>
  88  *       <td> {@link #getYear()} + {@link #getEon()} or<br/>
  89  *            {@link #getEonAndYear}
  90  *       </td>
  91  *       <td> <code>getYear()</code> is a value between -(10^9-1) to (10^9)-1
  92  *            or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
  93  *            {@link #getEon()} is high order year value in billion of years.<br/>
  94  *            <code>getEon()</code> has values greater than or equal to (10^9) or less than or equal to -(10^9).
  95  *            A value of null indicates field is undefined.</br>
  96  *            Given that <a href="http://www.w3.org/2001/05/xmlschema-errata#e2-63">XML Schema 1.0 errata</a> states that the year zero
  97  *            will be a valid lexical value in a future version of XML Schema,
  98  *            this class allows the year field to be set to zero. Otherwise,
  99  *            the year field value is handled exactly as described
 100  *            in the errata and [ISO-8601-1988]. Note that W3C XML Schema 1.0
 101  *            validation does not allow for the year field to have a value of zero.
 102  *            </td>
 103  *     </tr>
 104  *     <a name="datetimefield-month"/>
 105  *     <tr>
 106  *       <td> month </td>
 107  *       <td> {@link #getMonth()} </td>
 108  *       <td> 1 to 12 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
 109  *     </tr>
 110  *     <a name="datetimefield-day"/>
 111  *     <tr>
 112  *       <td> day </td>
 113  *       <td> {@link #getDay()} </td>
 114  *       <td> Independent of month, max range is 1 to 31 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
 115  *            The normative value constraint stated relative to month
 116  *            field's value is in <a href="http://www.w3.org/TR/xmlschema-2/#isoformats">W3C XML Schema 1.0 Part 2, Appendix D</a>.
 117  *       </td>
 118  *     </tr>
 119  *     <a name="datetimefield-hour"/>
 120  *     <tr>
 121  *       <td> hour </td>
 122  *       <td> {@link #getHour()} </td>
 123  *       <td>
 124  *         0 to 23 or {@link DatatypeConstants#FIELD_UNDEFINED}.
 125  *         An hour value of 24 is allowed to be set in the lexical space provided the minute and second
 126  *         field values are zero. However, an hour value of 24 is not allowed in value space and will be
 127  *         transformed to represent the value of the first instance of the following day as per
 128  *         <a href="http://www.w3.org/TR/xmlschema-2/#built-in-primitive-datatypes">
 129  *         XML Schema Part 2: Datatypes Second Edition, 3.2 Primitive datatypes</a>.
 130  *       </td>
 131  *     </tr>
 132  *     <a name="datetimefield-minute"/>
 133  *     <tr>
 134  *       <td> minute </td>
 135  *       <td> {@link #getMinute()} </td>
 136  *       <td> 0 to 59 or {@link DatatypeConstants#FIELD_UNDEFINED} </td>
 137  *     </tr>
 138  *     <a name="datetimefield-second"/>
 139  *     <tr>
 140  *       <td>second</td>
 141  *       <td>
 142  *         {@link #getSecond()} + {@link #getMillisecond()}/1000 or<br/>
 143  *         {@link #getSecond()} + {@link #getFractionalSecond()}
 144  *       </td>
 145  *       <td>
 146  *         {@link #getSecond()} from 0 to 60 or {@link DatatypeConstants#FIELD_UNDEFINED}.<br/>
 147  *         <i>(Note: 60 only allowable for leap second.)</i><br/>
 148  *         {@link #getFractionalSecond()} allows for infinite precision over the range from 0.0 to 1.0 when
 149  *         the {@link #getSecond()} is defined.<br/>
 150  *         <code>FractionalSecond</code> is optional and has a value of <code>null</code> when it is undefined.<br />
 151  *            {@link #getMillisecond()} is the convenience
 152  *            millisecond precision of value of {@link #getFractionalSecond()}.
 153  *       </td>
 154  *     </tr>
 155  *     <tr id="datetimefield-timezone">
 156  *       <td> timezone </td>
 157  *       <td> {@link #getTimezone()} </td>
 158  *       <td> Number of minutes or {@link DatatypeConstants#FIELD_UNDEFINED}.
 159  *         Value range from -14 hours (-14 * 60 minutes) to 14 hours (14 * 60 minutes).
 160  *       </td>
 161  *     </tr>
 162  *   </tbody>
 163  *  </table>
 164  *
 165  * <p>All maximum value space constraints listed for the fields in the table
 166  * above are checked by factory methods, setter methods and parse methods of
 167  * this class. <code>IllegalArgumentException</code> is thrown when
 168  * parameter's value is outside the maximum value constraint for the field.
 169  * Validation checks, for example, whether days in month should be
 170  * limited to 29, 30 or 31 days, that are dependent on the values of other
 171  * fields are not checked by these methods.
 172  * </p>
 173  *
 174  * <p>The following operations are defined for this class:
 175  * <ul>
 176  *   <li>factory methods to create instances</li>
 177  *   <li>accessors/mutators for independent date/time fields</li>
 178  *   <li>conversion between this class and W3C XML Schema 1.0 lexical representation</li>
 179  *   <li>conversion between this class and <code>java.util.GregorianCalendar</code></li>
 180  *   <li>partial order relation comparator method, {@link #compare(XMLGregorianCalendar)}</li>
 181  *   <li>{@link #equals(Object)} defined relative to {@link #compare(XMLGregorianCalendar)}.</li>
 182  *   <li> addition operation with {@link javax.xml.datatype.Duration}.
 183  * instance as defined in <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">
 184  * W3C XML Schema 1.0 Part 2, Appendix E, <i>Adding durations to dateTimes</i></a>.</li>
 185  * </ul>
 186  * </p>
 187  *
 188  * @author Kohsuke Kawaguchi
 189  * @author Joseph Fialli
 190  * @author Sunitha Reddy
 191  * @see javax.xml.datatype.Duration
 192  * @since 1.5
 193  * @LastModified: June 2018
 194  */
 195 
 196 public class XMLGregorianCalendarImpl
 197         extends XMLGregorianCalendar
 198         implements Serializable, Cloneable {
 199 
 200     /** Backup values **/
 201     transient private BigInteger orig_eon;
 202     transient private int orig_year = DatatypeConstants.FIELD_UNDEFINED;
 203     transient private int orig_month = DatatypeConstants.FIELD_UNDEFINED;
 204     transient private int orig_day = DatatypeConstants.FIELD_UNDEFINED;
 205     transient private int orig_hour = DatatypeConstants.FIELD_UNDEFINED;
 206     transient private int orig_minute = DatatypeConstants.FIELD_UNDEFINED;
 207     transient private int orig_second = DatatypeConstants.FIELD_UNDEFINED;
 208     transient private BigDecimal orig_fracSeconds;
 209     transient private int orig_timezone = DatatypeConstants.FIELD_UNDEFINED;
 210 
 211     /**
 212      * <p>Eon of this <code>XMLGregorianCalendar</code>.</p>
 213      */
 214     private BigInteger eon = null;
 215 
 216     /**
 217      * <p>Year of this <code>XMLGregorianCalendar</code>.</p>
 218      */
 219     private int year = DatatypeConstants.FIELD_UNDEFINED;
 220 
 221     /**
 222      * <p>Month of this <code>XMLGregorianCalendar</code>.</p>
 223      */
 224     private int month = DatatypeConstants.FIELD_UNDEFINED;
 225 
 226     /**
 227      * <p>Day of this <code>XMLGregorianCalendar</code>.</p>
 228      */
 229     private int day = DatatypeConstants.FIELD_UNDEFINED;
 230 
 231     /**
 232      * <p>Timezone of this <code>XMLGregorianCalendar</code> in minutes.</p>
 233      */
 234     private int timezone = DatatypeConstants.FIELD_UNDEFINED;
 235 
 236     /**
 237      * <p>Hour of this <code>XMLGregorianCalendar</code>.</p>
 238      */
 239     private int hour = DatatypeConstants.FIELD_UNDEFINED;
 240 
 241     /**
 242      * <p>Minute of this <code>XMLGregorianCalendar</code>.</p>
 243      */
 244     private int minute = DatatypeConstants.FIELD_UNDEFINED;
 245 
 246     /**
 247      * <p>Second of this <code>XMLGregorianCalendar</code>.</p>
 248      */
 249     private int second = DatatypeConstants.FIELD_UNDEFINED ;
 250 
 251     /**
 252      * <p>Fractional second of this <code>XMLGregorianCalendar</code>.</p>
 253      */
 254     private BigDecimal fractionalSecond = null;
 255 
 256     /**
 257      * <p>BigInteger constant; representing a billion.</p>
 258      */
 259     private static final BigInteger BILLION_B = new BigInteger("1000000000");
 260 
 261     /**
 262      * <p>int constant; representing a billion.</p>
 263      */
 264     private static final int BILLION_I = 1000000000;
 265 
 266     /**
 267      *   <p>Obtain a pure Gregorian Calendar by calling
 268      *   GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE). </p>
 269      */
 270     private static final Date PURE_GREGORIAN_CHANGE =
 271         new Date(Long.MIN_VALUE);
 272 
 273     /**
 274      * Year index for MIN_ and MAX_FIELD_VALUES.
 275      */
 276     private static final int YEAR   = 0;
 277 
 278     /**
 279      * Month index for MIN_ and MAX_FIELD_VALUES.
 280      */
 281     private static final int MONTH  = 1;
 282 
 283     /**
 284      * Day index for MIN_ and MAX_FIELD_VALUES.
 285      */
 286     private static final int DAY    = 2;
 287 
 288     /**
 289      * Hour index for MIN_ and MAX_FIELD_VALUES.
 290      */
 291     private static final int HOUR   = 3;
 292 
 293     /**
 294      * Minute index for MIN_ and MAX_FIELD_VALUES.
 295      */
 296     private static final int MINUTE = 4;
 297 
 298     /**
 299      * Second index for MIN_ and MAX_FIELD_VALUES.
 300      */
 301     private static final int SECOND = 5;
 302 
 303     /**
 304      * Second index for MIN_ and MAX_FIELD_VALUES.
 305      */
 306     private static final int MILLISECOND = 6;
 307 
 308     /**
 309      * Timezone index for MIN_ and MAX_FIELD_VALUES
 310      */
 311     private static final int TIMEZONE = 7;
 312 
 313 
 314     /**
 315      * field names indexed by YEAR..TIMEZONE.
 316      */
 317     private static final String FIELD_NAME[] = {
 318         "Year",
 319         "Month",
 320         "Day",
 321         "Hour",
 322         "Minute",
 323         "Second",
 324         "Millisecond",
 325         "Timezone"
 326     };
 327 
 328     /**
 329      * <p>Stream Unique Identifier.</p>
 330      *
 331      * <p>TODO: Serialization should use the XML string representation as
 332      * the serialization format to ensure future compatibility.</p>
 333      */
 334     private static final long serialVersionUID = 1L;
 335 
 336     /**
 337      * <p>Use as a template for default field values when
 338      * converting to a {@link GregorianCalendar}, set to a leap
 339      * year date of January 1, 0400 at midnight.</p>
 340      *
 341      * <p>Fields that are optional for an <code>xsd:dateTime</code> instances are defaulted to not being set to any value.
 342      * <code>XMLGregorianCalendar</code> fields millisecond, fractional second and timezone return the value indicating
 343      * that the field is not set, {@link DatatypeConstants#FIELD_UNDEFINED} for millisecond and timezone
 344      * and <code>null</code> for fractional second.</p>
 345      *
 346      * @see #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
 347      */
 348     public static final XMLGregorianCalendar LEAP_YEAR_DEFAULT =
 349                 createDateTime(
 350                         400,  //year
 351                 DatatypeConstants.JANUARY,  //month
 352                         1,  // day
 353                     0,  // hour
 354                     0,  // minute
 355                     0,  // second
 356                     DatatypeConstants.FIELD_UNDEFINED,  // milliseconds
 357                     DatatypeConstants.FIELD_UNDEFINED // timezone
 358                 );
 359 
 360     // Constructors
 361 
 362     /**
 363      * Constructs a new XMLGregorianCalendar object.
 364      *
 365      * String parsing documented by {@link #parse(String)}.
 366      *
 367      * Returns a non-null valid XMLGregorianCalendar object that holds the
 368      * value indicated by the lexicalRepresentation parameter.
 369      *
 370      * @param lexicalRepresentation
 371      *      Lexical representation of one the eight
 372      *      XML Schema date/time datatypes.
 373      * @throws IllegalArgumentException
 374      *      If the given string does not conform as documented in
 375      *      {@link #parse(String)}.
 376      * @throws NullPointerException
 377      *      If the given string is null.
 378      */
 379     protected XMLGregorianCalendarImpl(String lexicalRepresentation)
 380             throws IllegalArgumentException {
 381 
 382         // compute format string for this lexical representation.
 383         String format;
 384         String lexRep = lexicalRepresentation;
 385         final int NOT_FOUND = -1;
 386         int lexRepLength = lexRep.length();
 387 
 388         // current parser needs a format string,
 389         // use following heuristics to figure out what xml schema date/time
 390         // datatype this lexical string could represent.
 391         // Fix 4971612: invalid SCCS macro substitution in data string,
 392         //   no %{alpha}% to avoid SCCS maco substitution
 393         if (lexRep.indexOf('T') != NOT_FOUND) {
 394             // found Date Time separater, must be xsd:DateTime
 395             format = "%Y-%M-%DT%h:%m:%s" + "%z";
 396         } else if (lexRepLength >= 3 && lexRep.charAt(2) == ':') {
 397             // found ":", must be xsd:Time
 398             format = "%h:%m:%s" + "%z";
 399         } else if (lexRep.startsWith("--")) {
 400             // check for gDay || gMonth || gMonthDay
 401             if (lexRepLength >= 3 && lexRep.charAt(2) == '-') {
 402                 // gDay, ---DD(z?)
 403                 format = "---%D" + "%z";
 404             } else if (lexRepLength == 4     // --MM
 405                     || lexRepLength == 5     // --MMZ
 406                     || lexRepLength == 10) { // --MMSHH:MM
 407                 // gMonth, --MM(z?),
 408                 // per XML Schema Errata, used to be --MM--(z?)
 409                 format = "--%M" + "%z";
 410             } else {
 411                 // gMonthDay, --MM-DD(z?), (or invalid lexicalRepresentation)
 412                 // length should be:
 413                 //  7: --MM-DD
 414                 //  8: --MM-DDZ
 415                 // 13: --MM-DDSHH:MM
 416                 format = "--%M-%D" + "%z";
 417             }
 418         } else {
 419             // check for Date || GYear | GYearMonth
 420             int countSeparator = 0;
 421 
 422             // start at index 1 to skip potential negative sign for year.
 423 
 424 
 425             int timezoneOffset = lexRep.indexOf(':');
 426             if (timezoneOffset != NOT_FOUND) {
 427 
 428                 // found timezone, strip it off for distinguishing
 429                 // between Date, GYear and GYearMonth so possible
 430                 // negative sign in timezone is not mistaken as
 431                 // a separator.
 432                 lexRepLength -= 6;
 433             }
 434 
 435             for (int i = 1; i < lexRepLength; i++) {
 436                 if (lexRep.charAt(i) == '-') {
 437                     countSeparator++;
 438                 }
 439             }
 440             if (countSeparator == 0) {
 441                 // GYear
 442                 format = "%Y" + "%z";
 443             } else if (countSeparator == 1) {
 444                 // GYearMonth
 445                 format = "%Y-%M" + "%z";
 446             } else {
 447                 // Date or invalid lexicalRepresentation
 448                 // Fix 4971612: invalid SCCS macro substitution in data string
 449                 format = "%Y-%M-%D" + "%z";
 450             }
 451         }
 452         Parser p = new Parser(format, lexRep);
 453         p.parse();
 454 
 455         // check for validity
 456         if (!isValid()) {
 457             throw new IllegalArgumentException(
 458                     DatatypeMessageFormatter.formatMessage(null, "InvalidXGCRepresentation", new Object[]{lexicalRepresentation})
 459                     //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value."
 460             );
 461         }
 462 
 463         save();
 464     }
 465 
 466     /**
 467      * save original values
 468      */
 469     private void save() {
 470         orig_eon = eon;
 471         orig_year = year;
 472         orig_month = month;
 473         orig_day = day;
 474         orig_hour = hour;
 475         orig_minute = minute;
 476         orig_second = second;
 477         orig_fracSeconds = fractionalSecond;
 478         orig_timezone = timezone;
 479     }
 480 
 481     /**
 482      * <p>Create an instance with all date/time datatype fields set to
 483      * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.</p>
 484      */
 485     public XMLGregorianCalendarImpl() {
 486 
 487         // field initializers already do the correct initialization.
 488     }
 489 
 490     /**
 491      * <p>Private constructor allowing for complete value spaces allowed by
 492      * W3C XML Schema 1.0 recommendation for xsd:dateTime and related
 493      * builtin datatypes. Note that <code>year</code> parameter supports
 494      * arbitrarily large numbers and fractionalSecond has infinite
 495      * precision.</p>
 496      *
 497      * @param year of <code>XMLGregorianCalendar</code> to be created.
 498      * @param month of <code>XMLGregorianCalendar</code> to be created.
 499      * @param day of <code>XMLGregorianCalendar</code> to be created.
 500      * @param hour of <code>XMLGregorianCalendar</code> to be created.
 501      * @param minute of <code>XMLGregorianCalendar</code> to be created.
 502      * @param second of <code>XMLGregorianCalendar</code> to be created.
 503      * @param fractionalSecond of <code>XMLGregorianCalendar</code> to be created.
 504      * @param timezone of <code>XMLGregorianCalendar</code> to be created.
 505      *
 506      */
 507     protected XMLGregorianCalendarImpl(
 508         BigInteger year,
 509         int month,
 510         int day,
 511         int hour,
 512         int minute,
 513         int second,
 514         BigDecimal fractionalSecond,
 515         int timezone) {
 516 
 517         setYear(year);
 518         setMonth(month);
 519         setDay(day);
 520         setTime(hour, minute, second, fractionalSecond);
 521         setTimezone(timezone);
 522 
 523         // check for validity
 524         if (!isValid()) {
 525 
 526             throw new IllegalArgumentException(
 527                 DatatypeMessageFormatter.formatMessage(null,
 528                     "InvalidXGCValue-fractional",
 529                     new Object[] { year, month, day,
 530                     hour, minute, second,
 531                     fractionalSecond, timezone})
 532                         );
 533         }
 534 
 535         save();
 536     }
 537 
 538     /**
 539      * <p>Private constructor of value spaces that a
 540      * <code>java.util.GregorianCalendar</code> instance would need to convert to an
 541      * <code>XMLGregorianCalendar</code> instance.</p>
 542      *
 543      * <p><code>XMLGregorianCalendar eon</code> and
 544      * <code>fractionalSecond</code> are set to <code>null</code></p>
 545      *
 546      * @param year of <code>XMLGregorianCalendar</code> to be created.
 547      * @param month of <code>XMLGregorianCalendar</code> to be created.
 548      * @param day of <code>XMLGregorianCalendar</code> to be created.
 549      * @param hour of <code>XMLGregorianCalendar</code> to be created.
 550      * @param minute of <code>XMLGregorianCalendar</code> to be created.
 551      * @param second of <code>XMLGregorianCalendar</code> to be created.
 552      * @param millisecond of <code>XMLGregorianCalendar</code> to be created.
 553      * @param timezone of <code>XMLGregorianCalendar</code> to be created.
 554      */
 555     private XMLGregorianCalendarImpl(
 556         int year,
 557         int month,
 558         int day,
 559         int hour,
 560         int minute,
 561         int second,
 562         int millisecond,
 563         int timezone) {
 564 
 565         setYear(year);
 566         setMonth(month);
 567         setDay(day);
 568         setTime(hour, minute, second);
 569         setTimezone(timezone);
 570         BigDecimal realMilliseconds = null;
 571         if (millisecond != DatatypeConstants.FIELD_UNDEFINED) {
 572             realMilliseconds = BigDecimal.valueOf(millisecond, 3);
 573         }
 574         setFractionalSecond(realMilliseconds);
 575 
 576         if (!isValid()) {
 577 
 578             throw new IllegalArgumentException(
 579                 DatatypeMessageFormatter.formatMessage(null,
 580                 "InvalidXGCValue-milli",
 581                 new Object[] { year, month, day,
 582                 hour, minute, second,
 583                 millisecond, timezone})
 584                         );
 585         }
 586 
 587         save();
 588     }
 589 
 590         /**
 591          * <p>Convert a <code>java.util.GregorianCalendar</code> to XML Schema 1.0
 592          * representation.</p>
 593          *
 594          * <table border="2" rules="all" cellpadding="2">
 595          *   <thead>
 596          *     <tr>
 597          *       <th align="center" colspan="2">
 598          *          Field by Field Conversion from
 599          *          <code>java.util.GregorianCalendar</code> to this class
 600          *       </th>
 601          *     </tr>
 602          *   </thead>
 603          *   <tbody>
 604          *     <tr>
 605          *        <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
 606          *        <th><code>java.util.GregorianCalendar</code> field</th>
 607          *     </tr>
 608          *     <tr>
 609          *       <th>{@link #setYear(int)}</th>
 610          *       <th><code>ERA == GregorianCalendar.BC ? -YEAR : YEAR</code></th>
 611          *     </tr>
 612          *     <tr>
 613          *       <th>{@link #setMonth(int)}</th>
 614          *       <th><code>MONTH + 1</code></th>
 615          *     </tr>
 616          *     <tr>
 617          *       <th>{@link #setDay(int)}</th>
 618          *       <th><code>DAY_OF_MONTH</code></th>
 619          *     </tr>
 620          *     <tr>
 621          *       <th>{@link #setTime(int,int,int, BigDecimal)}</th>
 622          *       <th><code>HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND</code></th>
 623          *     </tr>
 624          *     <tr>
 625          *       <th>{@link #setTimezone(int)}<i>*</i></th>
 626          *       <th><code>(ZONE_OFFSET + DST_OFFSET) / (60*1000)</code><br/>
 627          *       <i>(in minutes)</i>
 628          *       </th>
 629          *     </tr>
 630          *   </tbody>
 631          * </table>
 632          * <p><i>*</i>conversion loss of information. It is not possible to represent
 633          * a <code>java.util.GregorianCalendar</code> daylight savings timezone id in the
 634          * XML Schema 1.0 date/time datatype representation.</p>
 635          *
 636          * <p>To compute the return value's <code>TimeZone</code> field,
 637          * <ul>
 638          * <li>when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
 639          * create a <code>java.util.TimeZone</code> with a custom timezone id
 640          * using the <code>this.getTimezone()</code>.</li>
 641          * <li>else use the <code>GregorianCalendar</code> default timezone value
 642          * for the host is defined as specified by
 643          * <code>java.util.TimeZone.getDefault()</code>.</li></p>
 644          *
 645          * @param cal <code>java.util.GregorianCalendar</code> used to create <code>XMLGregorianCalendar</code>
 646          */
 647     public XMLGregorianCalendarImpl(GregorianCalendar cal) {
 648 
 649         int year1 = cal.get(Calendar.YEAR);
 650         if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
 651             year1 = -year1;
 652         }
 653         this.setYear(year1);
 654 
 655         // Calendar.MONTH is zero based, XSD Date datatype's month field starts
 656         // with JANUARY as 1.
 657         this.setMonth(cal.get(Calendar.MONTH) + 1);
 658         this.setDay(cal.get(Calendar.DAY_OF_MONTH));
 659         this.setTime(
 660                 cal.get(Calendar.HOUR_OF_DAY),
 661                 cal.get(Calendar.MINUTE),
 662                 cal.get(Calendar.SECOND),
 663                 cal.get(Calendar.MILLISECOND));
 664 
 665         // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
 666         int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
 667         this.setTimezone(offsetInMinutes);
 668         save();
 669     }
 670 
 671     // Factories
 672 
 673     /**
 674      * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
 675      * All possible fields are specified for this factory method.</p>
 676      *
 677      * @param year represents both high-order eons and low-order year.
 678      * @param month of <code>dateTime</code>
 679      * @param day of <code>dateTime</code>
 680      * @param hours of <code>dateTime</code>
 681      * @param minutes of <code>dateTime</code>
 682      * @param seconds of <code>dateTime</code>
 683      * @param fractionalSecond value of null indicates optional field is absent.
 684      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 685      *
 686      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 687      *
 688      * @see DatatypeConstants#FIELD_UNDEFINED
 689      *
 690      * @throws IllegalArgumentException if any parameter is outside value
 691      * constraints for the field as specified in
 692      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
 693      */
 694     public static XMLGregorianCalendar createDateTime(
 695         BigInteger year,
 696         int month,
 697         int day,
 698         int hours,
 699         int minutes,
 700         int seconds,
 701         BigDecimal fractionalSecond,
 702         int timezone) {
 703 
 704         return new XMLGregorianCalendarImpl(
 705             year,
 706             month,
 707             day,
 708             hours,
 709             minutes,
 710             seconds,
 711             fractionalSecond,
 712             timezone);
 713     }
 714 
 715     /**
 716      * <p>Create a Java instance of XML Schema builtin datatype dateTime.</p>
 717      *
 718      * @param year represents both high-order eons and low-order year.
 719      * @param month of <code>dateTime</code>
 720      * @param day of <code>dateTime</code>
 721      * @param hour of <code>dateTime</code>
 722      * @param minute of <code>dateTime</code>
 723      * @param second of <code>dateTime</code>
 724      *
 725      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 726      *
 727      * @throws IllegalArgumentException if any parameter is outside value constraints for the field as specified in
 728      *   <a href="#datetimefieldmapping">date/time field mapping table</a>.
 729      *
 730      * @see DatatypeConstants#FIELD_UNDEFINED
 731      */
 732     public static XMLGregorianCalendar createDateTime(
 733         int year,
 734         int month,
 735         int day,
 736         int hour,
 737         int minute,
 738         int second) {
 739 
 740         return new XMLGregorianCalendarImpl(
 741             year,
 742             month,
 743             day,
 744             hour,
 745             minute,
 746             second,
 747             DatatypeConstants.FIELD_UNDEFINED,  //millisecond
 748                 DatatypeConstants.FIELD_UNDEFINED //timezone
 749         );
 750     }
 751 
 752     /**
 753      * <p>Create a Java representation of XML Schema builtin datatype <code>dateTime</code>.
 754      * All possible fields are specified for this factory method.</p>
 755      *
 756      * @param year represents low-order year.
 757      * @param month of <code>dateTime</code>
 758      * @param day of <code>dateTime</code>
 759      * @param hours of <code>dateTime</code>
 760      * @param minutes of <code>dateTime</code>
 761      * @param seconds of <code>dateTime</code>
 762      * @param milliseconds of <code>dateTime</code>. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 763      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 764      *
 765      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 766      *
 767      * @throws IllegalArgumentException if any parameter is outside value constraints for the field as specified in
 768      *   <a href="#datetimefieldmapping">date/time field mapping table</a>.
 769      *
 770      * @see DatatypeConstants#FIELD_UNDEFINED
 771      */
 772     public static XMLGregorianCalendar createDateTime(
 773         int year,
 774         int month,
 775         int day,
 776         int hours,
 777         int minutes,
 778         int seconds,
 779         int milliseconds,
 780         int timezone) {
 781 
 782         return new XMLGregorianCalendarImpl(
 783             year,
 784             month,
 785             day,
 786             hours,
 787             minutes,
 788             seconds,
 789             milliseconds,
 790             timezone);
 791     }
 792 
 793     /**
 794      * <p>Create a Java representation of XML Schema builtin datatype <code>date</code> or <code>g*</code>.</p>
 795      *
 796      * <p>For example, an instance of <code>gYear</code> can be created invoking this factory
 797      * with <code>month</code> and <code>day</code> parameters set to
 798      * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
 799      *
 800      * @param year of <code>XMLGregorianCalendar</code> to be created.
 801      * @param month of <code>XMLGregorianCalendar</code> to be created.
 802      * @param day of <code>XMLGregorianCalendar</code> to be created.
 803      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 804      *
 805      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 806      *
 807      * @see DatatypeConstants#FIELD_UNDEFINED
 808      *
 809      * @throws IllegalArgumentException if any parameter is outside value
 810      * constraints for the field as specified in
 811      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
 812      */
 813     public static XMLGregorianCalendar createDate(
 814         int year,
 815         int month,
 816         int day,
 817         int timezone) {
 818 
 819         return new XMLGregorianCalendarImpl(
 820             year,
 821             month,
 822             day,
 823             DatatypeConstants.FIELD_UNDEFINED, // hour
 824             DatatypeConstants.FIELD_UNDEFINED, // minute
 825             DatatypeConstants.FIELD_UNDEFINED, // second
 826                 DatatypeConstants.FIELD_UNDEFINED, // millisecond
 827             timezone);
 828     }
 829 
 830     /**
 831      * Create a Java instance of XML Schema builtin datatype <code>time</code>.
 832      * @param hours number of hours
 833      * @param minutes number of minutes
 834      * @param seconds number of seconds
 835      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 836      *
 837      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 838      *
 839      * @see DatatypeConstants#FIELD_UNDEFINED
 840      *
 841      * @throws IllegalArgumentException if any parameter is outside value
 842      * constraints for the field as specified in
 843      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
 844      */
 845     public static XMLGregorianCalendar createTime(
 846         int hours,
 847         int minutes,
 848         int seconds,
 849                 int timezone) {
 850 
 851                 return new XMLGregorianCalendarImpl(
 852                         DatatypeConstants.FIELD_UNDEFINED, // Year
 853                         DatatypeConstants.FIELD_UNDEFINED, // Month
 854                         DatatypeConstants.FIELD_UNDEFINED, // Day
 855                         hours,
 856                         minutes,
 857                         seconds,
 858                         DatatypeConstants.FIELD_UNDEFINED, //Millisecond
 859                         timezone);
 860     }
 861 
 862     /**
 863      * <p>Create a Java instance of XML Schema builtin datatype time.</p>
 864      *
 865      * @param hours number of hours
 866      * @param minutes number of minutes
 867      * @param seconds number of seconds
 868      * @param fractionalSecond value of <code>null</code> indicates that this optional field is not set.
 869      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 870      *
 871      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 872      *
 873      * @see DatatypeConstants#FIELD_UNDEFINED
 874      *
 875      * @throws IllegalArgumentException if any parameter is outside value
 876      * constraints for the field as specified in
 877      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
 878      */
 879     public static XMLGregorianCalendar createTime(
 880         int hours,
 881         int minutes,
 882         int seconds,
 883         BigDecimal fractionalSecond,
 884         int timezone) {
 885 
 886         return new XMLGregorianCalendarImpl(
 887             null,            // Year
 888             DatatypeConstants.FIELD_UNDEFINED, // month
 889             DatatypeConstants.FIELD_UNDEFINED, // day
 890             hours,
 891             minutes,
 892             seconds,
 893             fractionalSecond,
 894             timezone);
 895     }
 896 
 897     /**
 898      * <p>Create a Java instance of XML Schema builtin datatype time.</p>
 899      *
 900      * @param hours number of hours
 901      * @param minutes number of minutes
 902      * @param seconds number of seconds
 903      * @param milliseconds number of milliseconds
 904      * @param timezone offset in minutes. {@link DatatypeConstants#FIELD_UNDEFINED} indicates optional field is not set.
 905      *
 906      * @return <code>XMLGregorianCalendar</code> created from parameter values.
 907      *
 908      * @see DatatypeConstants#FIELD_UNDEFINED
 909      *
 910      * @throws IllegalArgumentException if any parameter is outside value
 911      * constraints for the field as specified in
 912      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
 913      */
 914     public static XMLGregorianCalendar createTime(
 915         int hours,
 916         int minutes,
 917         int seconds,
 918         int milliseconds,
 919         int timezone) {
 920 
 921         return new XMLGregorianCalendarImpl(
 922                 DatatypeConstants.FIELD_UNDEFINED, // year
 923                 DatatypeConstants.FIELD_UNDEFINED, // month
 924                 DatatypeConstants.FIELD_UNDEFINED, // day
 925                 hours,
 926                 minutes,
 927                 seconds,
 928                 milliseconds,
 929                 timezone);
 930     }
 931 
 932     // Accessors
 933 
 934     /**
 935      * <p>Return high order component for XML Schema 1.0 dateTime datatype field for
 936      * <code>year</code>.
 937      * <code>null</code> if this optional part of the year field is not defined.</p>
 938      *
 939      * <p>Value constraints for this value are summarized in
 940      * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
 941      * @return eon of this <code>XMLGregorianCalendar</code>. The value
 942      * returned is an integer multiple of 10^9.
 943      *
 944      * @see #getYear()
 945      * @see #getEonAndYear()
 946      */
 947     public BigInteger getEon() {
 948            return eon;
 949     }
 950 
 951     /**
 952      * <p>Return low order component for XML Schema 1.0 dateTime datatype field for
 953      * <code>year</code> or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
 954      *
 955      * <p>Value constraints for this value are summarized in
 956      * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
 957      *
 958      * @return year  of this <code>XMLGregorianCalendar</code>.
 959      *
 960      * @see #getEon()
 961      * @see #getEonAndYear()
 962      */
 963     public int getYear() {
 964            return year;
 965     }
 966 
 967     /**
 968      * <p>Return XML Schema 1.0 dateTime datatype field for
 969      * <code>year</code>.</p>
 970      *
 971      * <p>Value constraints for this value are summarized in
 972      * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
 973      *
 974      * @return sum of <code>eon</code> and <code>BigInteger.valueOf(year)</code>
 975      * when both fields are defined. When only <code>year</code> is defined,
 976      * return it. When both <code>eon</code> and <code>year</code> are not
 977      * defined, return <code>null</code>.
 978      *
 979      * @see #getEon()
 980      * @see #getYear()
 981      */
 982     public BigInteger getEonAndYear() {
 983 
 984                 // both are defined
 985                 if (year != DatatypeConstants.FIELD_UNDEFINED
 986                         && eon != null) {
 987 
 988                         return eon.add(BigInteger.valueOf((long) year));
 989                 }
 990 
 991                 // only year is defined
 992                 if (year != DatatypeConstants.FIELD_UNDEFINED
 993                         && eon == null) {
 994 
 995                         return BigInteger.valueOf((long) year);
 996                 }
 997 
 998         // neither are defined
 999         // or only eon is defined which is not valid without a year
1000                 return null;
1001     }
1002 
1003     /**
1004      * <p>Return number of month or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1005      *
1006      * <p>Value constraints for this value are summarized in
1007      * <a href="#datetimefield-month">month field of date/time field mapping table</a>.</p>
1008      *
1009      * @return year  of this <code>XMLGregorianCalendar</code>.
1010      *
1011      */
1012     public int getMonth() {
1013         return month;
1014     }
1015 
1016     /**
1017      * Return day in month or {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1018      *
1019      * <p>Value constraints for this value are summarized in
1020      * <a href="#datetimefield-day">day field of date/time field mapping table</a>.</p>
1021      *
1022      * @see #setDay(int)
1023      */
1024     public int getDay() {
1025         return day;
1026     }
1027 
1028     /**
1029      * Return timezone offset in minutes or
1030      * {@link DatatypeConstants#FIELD_UNDEFINED} if this optional field is not defined.
1031      *
1032      * <p>Value constraints for this value are summarized in
1033      * <a href="#datetimefield-timezone">timezone field of date/time field mapping table</a>.</p>
1034      *
1035      * @see #setTimezone(int)
1036      */
1037     public int getTimezone() {
1038         return timezone;
1039     }
1040 
1041     /**
1042      * Return hours or {@link DatatypeConstants#FIELD_UNDEFINED}.
1043      * Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1044      *
1045      * <p>Value constraints for this value are summarized in
1046      * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.</p>
1047      * @see #setTime(int, int, int)
1048      */
1049     public int getHour() {
1050         return hour;
1051     }
1052 
1053     /**
1054      * Return minutes or {@link DatatypeConstants#FIELD_UNDEFINED}.<\p>
1055      * Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1056      *
1057      * <p>Value constraints for this value are summarized in
1058      * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.</p>
1059      * @see #setTime(int, int, int)
1060      */
1061     public int getMinute() {
1062         return minute;
1063     }
1064 
1065     /**
1066      * <p>Return seconds or {@link DatatypeConstants#FIELD_UNDEFINED}.<\p>
1067      *
1068      * <p>Returns {@link DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
1069      * When this field is not defined, the optional xs:dateTime
1070      * fractional seconds field, represented by
1071      * {@link #getFractionalSecond()} and {@link #getMillisecond()},
1072      * must not be defined.</p>
1073      *
1074      * <p>Value constraints for this value are summarized in
1075      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1076      *
1077      * @return Second  of this <code>XMLGregorianCalendar</code>.
1078      *
1079      * @see #getFractionalSecond()
1080      * @see #getMillisecond()
1081      * @see #setTime(int, int, int)
1082      */
1083     public int getSecond() {
1084            return second;
1085     }
1086 
1087     /**
1088      * @return result of adding second and fractional second field
1089      */
1090     private BigDecimal getSeconds() {
1091         if (second == DatatypeConstants.FIELD_UNDEFINED) {
1092             return DECIMAL_ZERO;
1093         }
1094         BigDecimal result = BigDecimal.valueOf((long) second);
1095         if (fractionalSecond != null) {
1096             return result.add(fractionalSecond);
1097         } else {
1098             return result;
1099         }
1100     }
1101 
1102 
1103     /**
1104      * <p>Return millisecond precision of {@link #getFractionalSecond()}.<\p>
1105      *
1106      * <p>This method represents a convenience accessor to infinite
1107      * precision fractional second value returned by
1108      * {@link #getFractionalSecond()}. The returned value is the rounded
1109      * down to milliseconds value of
1110      * {@link #getFractionalSecond()}. When {@link #getFractionalSecond()}
1111      * returns <code>null</code>, this method must return
1112      * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1113      *
1114      * <p>Value constraints for this value are summarized in
1115      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1116      *
1117      * @return Millisecond  of this <code>XMLGregorianCalendar</code>.
1118      *
1119      * @see #getFractionalSecond()
1120      * @see #setTime(int, int, int)
1121      */
1122     public int getMillisecond() {
1123         if (fractionalSecond == null) {
1124             return DatatypeConstants.FIELD_UNDEFINED;
1125         } else {
1126             // TODO: Non-optimal solution for now.
1127             // Efficient implementation would only store as BigDecimal
1128             // when needed and millisecond otherwise.
1129             return fractionalSecond.movePointRight(3).intValue();
1130         }
1131     }
1132 
1133     /**
1134      * <p>Return fractional seconds.</p>
1135      *
1136      * <p><code>null</code> is returned when this optional field is not defined.</p>
1137      *
1138      * <p>Value constraints are detailed in
1139      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
1140      *
1141      * <p>This optional field can only have a defined value when the
1142      * xs:dateTime second field, represented by ({@link #getSecond()},
1143      * does not return {@link DatatypeConstants#FIELD_UNDEFINED}).</p>
1144      *
1145      * @return fractional seconds  of this <code>XMLGregorianCalendar</code>.
1146      *
1147      * @see #getSecond()
1148      * @see #setTime(int, int, int, BigDecimal)
1149      */
1150     public BigDecimal getFractionalSecond() {
1151            return fractionalSecond;
1152     }
1153 
1154     // setters
1155 
1156     /**
1157      * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
1158      *
1159      * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
1160      *
1161      * @param year value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1162      *
1163      * @throws IllegalArgumentException if <code>year</code> parameter is
1164      * outside value constraints for the field as specified in
1165      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1166      */
1167     public final void setYear(BigInteger year) {
1168         if (year == null) {
1169             this.eon = null;
1170             this.year = DatatypeConstants.FIELD_UNDEFINED;
1171         } else {
1172             BigInteger temp = year.remainder(BILLION_B);
1173             this.year = temp.intValue();
1174             setEon(year.subtract(temp));
1175         }
1176     }
1177 
1178     /**
1179      * <p>Set year of XSD <code>dateTime</code> year field.</p>
1180      *
1181      * <p>Unset this field by invoking the setter with a parameter value of
1182      * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1183      *
1184      * <p>Note: if the absolute value of the <code>year</code> parameter
1185      * is less than 10^9, the eon component of the XSD year field is set to
1186      * <code>null</code> by this method.</p>
1187      *
1188      * @param year value constraints are summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1189      *   If year is {@link DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
1190      */
1191     public final void setYear(int year) {
1192         if (year == DatatypeConstants.FIELD_UNDEFINED) {
1193             this.year = DatatypeConstants.FIELD_UNDEFINED;
1194             this.eon = null;
1195         }
1196         else if (Math.abs(year) < BILLION_I) {
1197             this.year = year;
1198             this.eon = null;
1199         } else {
1200             BigInteger theYear = BigInteger.valueOf((long) year);
1201             BigInteger remainder = theYear.remainder(BILLION_B);
1202             this.year = remainder.intValue();
1203             setEon(theYear.subtract(remainder));
1204         }
1205     }
1206 
1207     /**
1208      * <p>Set high order part of XSD <code>dateTime</code> year field.</p>
1209      *
1210      * <p>Unset this field by invoking the setter with a parameter value of
1211      * <code>null</code>.</p>
1212      *
1213      * @param eon value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
1214      */
1215     private void setEon(BigInteger eon) {
1216         if (eon != null && eon.compareTo(BigInteger.ZERO) == 0) {
1217             // Treat ZERO as field being undefined.
1218             this.eon = null;
1219         } else {
1220             this.eon = eon;
1221         }
1222     }
1223 
1224     /**
1225      * <p>Set month.</p>
1226      *
1227      * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1228      *
1229      * @param month value constraints summarized in <a href="#datetimefield-month">month field of date/time field mapping table</a>.
1230      *
1231      * @throws IllegalArgumentException if <code>month</code> parameter is
1232      * outside value constraints for the field as specified in
1233      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1234      */
1235     public final void setMonth(int month) {
1236         if(month<DatatypeConstants.JANUARY || DatatypeConstants.DECEMBER<month)
1237             if(month!=DatatypeConstants.FIELD_UNDEFINED)
1238                 invalidFieldValue(MONTH, month);
1239         this.month = month;
1240     }
1241 
1242     /**
1243      * <p>Set days in month.</p>
1244      *
1245      * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1246      *
1247      * @param day value constraints summarized in <a href="#datetimefield-day">day field of date/time field mapping table</a>.
1248      *
1249      * @throws IllegalArgumentException if <code>day</code> parameter is
1250      * outside value constraints for the field as specified in
1251      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1252      */
1253     public final void setDay(int day) {
1254         if(day<1 || 31<day)
1255             if(day!=DatatypeConstants.FIELD_UNDEFINED)
1256                 invalidFieldValue(DAY,day);
1257         this.day = day;
1258     }
1259 
1260     /**
1261      * <p>Set the number of minutes in the timezone offset.</p>
1262      *
1263      * <p>Unset this field by invoking the setter with a parameter value of {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
1264      *
1265      * @param offset value constraints summarized in <a href="#datetimefield-timezone">
1266      *   timezone field of date/time field mapping table</a>.
1267      *
1268      * @throws IllegalArgumentException if <code>offset</code> parameter is
1269      * outside value constraints for the field as specified in
1270      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1271      */
1272     public final void setTimezone(int offset) {
1273             if(offset<-14*60 || 14*60<offset)
1274             if(offset!=DatatypeConstants.FIELD_UNDEFINED)
1275                 invalidFieldValue(TIMEZONE,offset);
1276         this.timezone = offset;
1277     }
1278 
1279     /**
1280      * <p>Set time as one unit.</p>
1281      *
1282      * @param hour value constraints are summarized in
1283      * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1284      * @param minute value constraints are summarized in
1285      * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1286      * @param second value constraints are summarized in
1287      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1288      *
1289      * @see #setTime(int, int, int, BigDecimal)
1290      *
1291      * @throws IllegalArgumentException if any parameter is
1292      * outside value constraints for the field as specified in
1293      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1294      */
1295     public final void setTime(int hour, int minute, int second) {
1296         setTime(hour, minute, second, null);
1297     }
1298 
1299     private void invalidFieldValue(int field, int value) {
1300         throw new IllegalArgumentException(
1301             DatatypeMessageFormatter.formatMessage(null, "InvalidFieldValue",
1302                 new Object[]{ value, FIELD_NAME[field]})
1303         );
1304     }
1305 
1306     private void testHour() {
1307 
1308         // http://www.w3.org/2001/05/xmlschema-errata#e2-45
1309         if (getHour() == 24) {
1310             if (getMinute() != 0
1311                     || getSecond() != 0) {
1312                 invalidFieldValue(HOUR, getHour());
1313             }
1314             // while 0-24 is acceptable in the lexical space, 24 is not valid in value space
1315             // W3C XML Schema Part 2, Section 3.2.7.1
1316             setHour(0, false);
1317             add(new DurationImpl(true, 0, 0, 1, 0, 0, 0));
1318         }
1319     }
1320 
1321     public void setHour(int hour) {
1322 
1323         setHour(hour, true);
1324     }
1325 
1326     private void setHour(int hour, boolean validate) {
1327 
1328         if (hour < 0 || hour > 24) {
1329             if (hour != DatatypeConstants.FIELD_UNDEFINED) {
1330                 invalidFieldValue(HOUR, hour);
1331             }
1332         }
1333 
1334         this.hour = hour;
1335 
1336         if (validate) {
1337             testHour();
1338         }
1339     }
1340 
1341     public void setMinute(int minute) {
1342         if(minute<0 || 59<minute)
1343             if(minute!=DatatypeConstants.FIELD_UNDEFINED)
1344                 invalidFieldValue(MINUTE, minute);
1345         this.minute = minute;
1346     }
1347 
1348     public void setSecond(int second) {
1349         if(second<0 || 60<second)   // leap second allows for 60
1350             if(second!=DatatypeConstants.FIELD_UNDEFINED)
1351                 invalidFieldValue(SECOND, second);
1352         this.second  = second;
1353     }
1354 
1355     /**
1356      * <p>Set time as one unit, including the optional infinite precison
1357      * fractional seconds.</p>
1358      *
1359      * @param hour value constraints are summarized in
1360      * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1361      * @param minute value constraints are summarized in
1362      * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1363      * @param second value constraints are summarized in
1364      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1365      * @param fractional value of <code>null</code> indicates this optional
1366      *                   field is not set.
1367      *
1368      * @throws IllegalArgumentException if any parameter is
1369      * outside value constraints for the field as specified in
1370      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1371      */
1372     public final void setTime(
1373             int hour,
1374             int minute,
1375             int second,
1376             BigDecimal fractional) {
1377 
1378         setHour(hour, false);
1379 
1380         setMinute(minute);
1381         if (second != 60) {
1382             setSecond(second);
1383         } else if ((hour == 23 && minute == 59) || (hour == 0 && minute == 0)) {
1384             setSecond(second);
1385         } else {
1386             invalidFieldValue(SECOND, second);
1387         }
1388 
1389         setFractionalSecond(fractional);
1390 
1391         // must test hour after setting seconds
1392         testHour();
1393     }
1394 
1395 
1396     /**
1397      * <p>Set time as one unit, including optional milliseconds.</p>
1398      *
1399      * @param hour value constraints are summarized in
1400      * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
1401      * @param minute value constraints are summarized in
1402      * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
1403      * @param second value constraints are summarized in
1404      * <a href="#datetimefield-second">second field of date/time field mapping table</a>.
1405      * @param millisecond value of {@link DatatypeConstants#FIELD_UNDEFINED} indicates this
1406      *                    optional field is not set.
1407      *
1408      * @throws IllegalArgumentException if any parameter is
1409      * outside value constraints for the field as specified in
1410      * <a href="#datetimefieldmapping">date/time field mapping table</a>.
1411      */
1412     public final void setTime(int hour, int minute, int second, int millisecond) {
1413 
1414         setHour(hour, false);
1415 
1416         setMinute(minute);
1417         if (second != 60) {
1418             setSecond(second);
1419         } else if ((hour == 23 && minute == 59) || (hour == 0 && minute == 0)) {
1420             setSecond(second);
1421         } else {
1422             invalidFieldValue(SECOND, second);
1423         }
1424         setMillisecond(millisecond);
1425 
1426         // must test hour after setting seconds
1427         testHour();
1428     }
1429 
1430     // comparisons
1431     /**
1432      * <p>Compare two instances of W3C XML Schema 1.0 date/time datatypes
1433      * according to partial order relation defined in
1434      * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.3,
1435      * <i>Order relation on dateTime</i></a>.</p>
1436      *
1437      * <p><code>xsd:dateTime</code> datatype field mapping to accessors of
1438      * this class are defined in
1439      * <a href="#datetimefieldmapping">date/time field mapping table</a>.</p>
1440      *
1441      * @param rhs instance of <code>XMLGregorianCalendar</code> to compare
1442      *
1443      * @return the relationship between <code>lhs</code> and <code>rhs</code> as
1444      *   {@link DatatypeConstants#LESSER},
1445      *   {@link DatatypeConstants#EQUAL},
1446      *   {@link DatatypeConstants#GREATER} or
1447      *   {@link DatatypeConstants#INDETERMINATE}.
1448      *
1449      * @throws NullPointerException if <code>lhs</code> or <code>rhs</code>
1450      * parameters are null.
1451      */
1452     public int compare(XMLGregorianCalendar rhs) {
1453 
1454         XMLGregorianCalendar lhs = this;
1455 
1456         int result = DatatypeConstants.INDETERMINATE;
1457         XMLGregorianCalendarImpl P = (XMLGregorianCalendarImpl) lhs;
1458         XMLGregorianCalendarImpl Q = (XMLGregorianCalendarImpl) rhs;
1459 
1460         if (P.getTimezone() == Q.getTimezone()) {
1461             // Optimization:
1462             // both instances are in same timezone or
1463             // both are FIELD_UNDEFINED.
1464             // Avoid costly normalization of timezone to 'Z' time.
1465             return internalCompare(P, Q);
1466 
1467         } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED &&
1468                 Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1469 
1470             // Both instances have different timezones.
1471             // Normalize to UTC time and compare.
1472             P = (XMLGregorianCalendarImpl) P.normalize();
1473             Q = (XMLGregorianCalendarImpl) Q.normalize();
1474             return internalCompare(P, Q);
1475         } else if (P.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
1476 
1477             if (P.getTimezone() != 0) {
1478                 P = (XMLGregorianCalendarImpl) P.normalize();
1479             }
1480 
1481             // C. step 1
1482             XMLGregorianCalendar MinQ = Q.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1483             result = internalCompare(P, MinQ);
1484             if (result == DatatypeConstants.LESSER) {
1485                 return result;
1486             }
1487 
1488             // C. step 2
1489             XMLGregorianCalendar MaxQ = Q.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1490             result = internalCompare(P, MaxQ);
1491             if (result == DatatypeConstants.GREATER) {
1492                 return result;
1493             } else {
1494                 // C. step 3
1495                 return DatatypeConstants.INDETERMINATE;
1496             }
1497         } else { // Q.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
1498             // P has no timezone and Q does.
1499             if (Q.getTimezone() != 0) {
1500                 Q = (XMLGregorianCalendarImpl) Q.normalizeToTimezone(Q.getTimezone());
1501             }
1502 
1503             // D. step 1
1504             XMLGregorianCalendar MaxP = P.normalizeToTimezone(DatatypeConstants.MAX_TIMEZONE_OFFSET);
1505             result = internalCompare(MaxP, Q);
1506             if (result == DatatypeConstants.LESSER) {
1507                 return result;
1508             }
1509 
1510             // D. step 2
1511             XMLGregorianCalendar MinP = P.normalizeToTimezone(DatatypeConstants.MIN_TIMEZONE_OFFSET);
1512             result = internalCompare(MinP, Q);
1513             if (result == DatatypeConstants.GREATER) {
1514                 return result;
1515             } else {
1516                 // D. step 3
1517                 return DatatypeConstants.INDETERMINATE;
1518             }
1519         }
1520     }
1521 
1522     /**
1523      * <p>Normalize this instance to UTC.</p>
1524      *
1525      * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1526      * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1527      */
1528     public XMLGregorianCalendar normalize() {
1529 
1530         XMLGregorianCalendar normalized = normalizeToTimezone(timezone);
1531 
1532         // if timezone was undefined, leave it undefined
1533         if (getTimezone() == DatatypeConstants.FIELD_UNDEFINED) {
1534             normalized.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
1535         }
1536 
1537         // if milliseconds was undefined, leave it undefined
1538         if (getMillisecond() == DatatypeConstants.FIELD_UNDEFINED) {
1539             normalized.setMillisecond(DatatypeConstants.FIELD_UNDEFINED);
1540         }
1541 
1542         return normalized;
1543     }
1544 
1545         /**
1546          * <p>Normalize this instance to UTC.</p>
1547          *
1548          * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
1549          * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
1550          */
1551     private XMLGregorianCalendar normalizeToTimezone(int timezone) {
1552 
1553         int minutes = timezone;
1554         XMLGregorianCalendar result = (XMLGregorianCalendar) this.clone();
1555 
1556         // normalizing to UTC time negates the timezone offset before
1557         // addition.
1558         minutes = -minutes;
1559         Duration d = new DurationImpl(minutes >= 0, // isPositive
1560                 0, //years
1561                 0, //months
1562                 0, //days
1563                 0, //hours
1564                 minutes < 0 ? -minutes : minutes, // absolute
1565                 0  //seconds
1566         );
1567         result.add(d);
1568 
1569         // set to zulu UTC time.
1570         result.setTimezone(0);
1571         return result;
1572     }
1573 
1574     /**
1575      *
1576      *  <p>Implements Step B from http://www.w3.org/TR/xmlschema-2/#dateTime-order </p>
1577      * @param P calendar instance with normalized timezone offset or
1578      *          having same timezone as Q
1579      * @param Q calendar instance with normalized timezone offset or
1580      *          having same timezone as P
1581      *
1582      * @return result of comparing P and Q, value of
1583      *   {@link DatatypeConstants#EQUAL},
1584      *   {@link DatatypeConstants#LESSER},
1585      *   {@link DatatypeConstants#GREATER} or
1586      *   {@link DatatypeConstants#INDETERMINATE}.
1587      */
1588     private static int internalCompare(XMLGregorianCalendar P,
1589                                        XMLGregorianCalendar Q) {
1590 
1591         int result;
1592 
1593         // compare Year.
1594         if (P.getEon() == Q.getEon()) {
1595 
1596             // Eon field is only equal when null.
1597             // optimized case for comparing year not requiring eon field.
1598             result = compareField(P.getYear(), Q.getYear());
1599             if (result != DatatypeConstants.EQUAL) {
1600                 return result;
1601             }
1602         } else {
1603             result = compareField(P.getEonAndYear(), Q.getEonAndYear());
1604             if (result != DatatypeConstants.EQUAL) {
1605                 return result;
1606             }
1607         }
1608 
1609         result = compareField(P.getMonth(), Q.getMonth());
1610         if (result != DatatypeConstants.EQUAL) {
1611             return result;
1612         }
1613 
1614         result = compareField(P.getDay(), Q.getDay());
1615         if (result != DatatypeConstants.EQUAL) {
1616             return result;
1617         }
1618 
1619         result = compareField(P.getHour(), Q.getHour());
1620         if (result != DatatypeConstants.EQUAL) {
1621             return result;
1622         }
1623 
1624         result = compareField(P.getMinute(), Q.getMinute());
1625         if (result != DatatypeConstants.EQUAL) {
1626             return result;
1627         }
1628         result = compareField(P.getSecond(), Q.getSecond());
1629         if (result != DatatypeConstants.EQUAL) {
1630             return result;
1631         }
1632 
1633         result = compareField(P.getFractionalSecond(), Q.getFractionalSecond());
1634         return result;
1635     }
1636 
1637     /**
1638      * <p>Implement Step B from
1639      * http://www.w3.org/TR/xmlschema-2/#dateTime-order.</p>
1640      */
1641     private static int compareField(int Pfield, int Qfield) {
1642         if (Pfield == Qfield) {
1643 
1644             //fields are either equal in value or both undefined.
1645             // Step B. 1.1 AND optimized result of performing 1.1-1.4.
1646             return DatatypeConstants.EQUAL;
1647         } else {
1648             if (Pfield == DatatypeConstants.FIELD_UNDEFINED || Qfield == DatatypeConstants.FIELD_UNDEFINED) {
1649                 // Step B. 1.2
1650                 return DatatypeConstants.INDETERMINATE;
1651             } else {
1652                 // Step B. 1.3-4.
1653                 return (Pfield < Qfield ? DatatypeConstants.LESSER : DatatypeConstants.GREATER);
1654             }
1655         }
1656     }
1657 
1658     private static int compareField(BigInteger Pfield, BigInteger Qfield) {
1659         if (Pfield == null) {
1660             return (Qfield == null ? DatatypeConstants.EQUAL : DatatypeConstants.INDETERMINATE);
1661         }
1662         if (Qfield == null) {
1663             return DatatypeConstants.INDETERMINATE;
1664         }
1665         return Pfield.compareTo(Qfield);
1666     }
1667 
1668     private static int compareField(BigDecimal Pfield, BigDecimal Qfield) {
1669         // optimization. especially when both arguments are null.
1670         if (Pfield == Qfield) {
1671             return DatatypeConstants.EQUAL;
1672         }
1673 
1674         if (Pfield == null) {
1675             Pfield = DECIMAL_ZERO;
1676         }
1677 
1678         if (Qfield == null) {
1679             Qfield = DECIMAL_ZERO;
1680         }
1681 
1682         return Pfield.compareTo(Qfield);
1683     }
1684 
1685     /**
1686      * <p>Indicates whether parameter <code>obj</code> is "equal to" this one.</p>
1687      *
1688      * @param obj to compare.
1689      *
1690      * @return <code>true</code> when <code>compare(this,(XMLGregorianCalendar)obj) == EQUAL.</code>.
1691      */
1692     public boolean equals(Object obj) {
1693 
1694         if (obj == null || !(obj instanceof XMLGregorianCalendar)) {
1695             return false;
1696         }
1697         if (obj == this) {
1698             return true;
1699         }
1700         return compare((XMLGregorianCalendar) obj) == DatatypeConstants.EQUAL;
1701     }
1702 
1703     /**
1704      * <p>Returns a hash code consistent with the definition of the equals method.</p>
1705      *
1706      * @return hash code of this object.
1707      */
1708     public int hashCode() {
1709 
1710         // Following two dates compare to EQUALS since in different timezones.
1711         // 2000-01-15T12:00:00-05:00 == 2000-01-15T13:00:00-04:00
1712         //
1713         // Must ensure both instances generate same hashcode by normalizing
1714         // this to UTC timezone.
1715         int timezone = getTimezone();
1716         if (timezone == DatatypeConstants.FIELD_UNDEFINED) {
1717             timezone = 0;
1718         }
1719         XMLGregorianCalendar gc = this;
1720         if (timezone != 0) {
1721             gc = this.normalizeToTimezone(getTimezone());
1722         }
1723         return gc.getYear() + gc.getMonth() + gc.getDay() +
1724                 gc.getHour() + gc.getMinute() + gc.getSecond();
1725     }
1726 
1727 
1728     /**
1729      * <p>Constructs a new XMLGregorianCalendar object by
1730      * parsing its lexical string representation as defined in
1731      * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1732      * <i>Lexical Representation</i>.</a></p>
1733      *
1734      * <p>The string representation may not have any leading and trailing whitespaces.</p>
1735      *
1736      * <p>The parsing is done field by field so that
1737      * the following holds for any lexically correct string x:</p>
1738      * <pre>
1739      * new XMLGregorianCalendar(x).toXMLFormat().equals(x)
1740      * </pre>
1741      * Except for the noted lexical/canonical representation mismatches
1742      * listed in <a href="http://www.w3.org/2001/05/xmlschema-errata#e2-45">
1743      * XML Schema 1.0 errata, Section 3.2.7.2</a>.
1744      *
1745      * <p>Returns a non-null valid XMLGregorianCalendar object that holds the value
1746      * indicated by the lexicalRepresentation parameter.</p>
1747      *
1748      * @param lexicalRepresentation Lexical representation of one the 8 XML Schema calendar datatypes.
1749      *
1750      * @return <code>XMLGregorianCalendar</code> created from parsing <code>lexicalRepresentation</code> parameter.
1751      *
1752      * @throws IllegalArgumentException
1753      *      If the given string does not conform to the aforementioned
1754      *      specification.
1755      * @throws NullPointerException
1756      *      If the given string is null.
1757      */
1758     public static XMLGregorianCalendar parse(String lexicalRepresentation) {
1759 
1760                 return new XMLGregorianCalendarImpl(lexicalRepresentation);
1761     }
1762 
1763     /**
1764      * <p>Return the lexical representation of <code>this</code> instance.
1765      * The format is specified in
1766      * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
1767      * <i>Lexical Representation</i>".</a></p>
1768      *
1769      * <p>Specific target lexical representation format is determined by
1770      * {@link #getXMLSchemaType()}.</p>
1771      *
1772      * @return XML, as <code>String</code>, representation of this <code>XMLGregorianCalendar</code>
1773      *
1774      * @throws java.lang.IllegalStateException if the combination of set fields
1775      *    does not match one of the eight defined XML Schema builtin date/time datatypes.
1776      */
1777     public String toXMLFormat() {
1778 
1779         QName typekind = getXMLSchemaType();
1780 
1781         String formatString = null;
1782         // Fix 4971612: invalid SCCS macro substitution in data string
1783         //   no %{alpha}% to avoid SCCS macro substitution
1784         if (typekind == DatatypeConstants.DATETIME) {
1785             formatString = "%Y-%M-%DT%h:%m:%s" + "%z";
1786         } else if (typekind == DatatypeConstants.DATE) {
1787             formatString = "%Y-%M-%D" + "%z";
1788         } else if (typekind == DatatypeConstants.TIME) {
1789             formatString = "%h:%m:%s" + "%z";
1790         } else if (typekind == DatatypeConstants.GMONTH) {
1791             formatString = "--%M" + "%z";
1792         } else if (typekind == DatatypeConstants.GDAY) {
1793             formatString = "---%D" + "%z";
1794         } else if (typekind == DatatypeConstants.GYEAR) {
1795             formatString = "%Y" + "%z";
1796         } else if (typekind == DatatypeConstants.GYEARMONTH) {
1797             formatString = "%Y-%M" + "%z";
1798         } else if (typekind == DatatypeConstants.GMONTHDAY) {
1799             formatString = "--%M-%D" + "%z";
1800         }
1801         return format(formatString);
1802     }
1803 
1804     /**
1805      * <p>Return the name of the XML Schema date/time type that this instance
1806      * maps to. Type is computed based on fields that are set.</p>
1807      *
1808      * <table border="2" rules="all" cellpadding="2">
1809      *   <thead>
1810      *     <tr>
1811      *       <th align="center" colspan="7">
1812      *         Required fields for XML Schema 1.0 Date/Time Datatypes.<br/>
1813      *         <i>(timezone is optional for all date/time datatypes)</i>
1814      *       </th>
1815      *     </tr>
1816      *   </thead>
1817      *   <tbody>
1818      *     <tr>
1819      *       <td>Datatype</td>
1820      *       <td>year</td>
1821      *       <td>month</td>
1822      *       <td>day</td>
1823      *       <td>hour</td>
1824      *       <td>minute</td>
1825      *       <td>second</td>
1826      *     </tr>
1827      *     <tr>
1828      *       <td>{@link DatatypeConstants#DATETIME}</td>
1829      *       <td>X</td>
1830      *       <td>X</td>
1831      *       <td>X</td>
1832      *       <td>X</td>
1833      *       <td>X</td>
1834      *       <td>X</td>
1835      *     </tr>
1836      *     <tr>
1837      *       <td>{@link DatatypeConstants#DATE}</td>
1838      *       <td>X</td>
1839      *       <td>X</td>
1840      *       <td>X</td>
1841      *       <td></td>
1842      *       <td></td>
1843      *       <td></td>
1844      *     </tr>
1845      *     <tr>
1846      *       <td>{@link DatatypeConstants#TIME}</td>
1847      *       <td></td>
1848      *       <td></td>
1849      *       <td></td>
1850      *       <td>X</td>
1851      *       <td>X</td>
1852      *       <td>X</td>
1853      *     </tr>
1854      *     <tr>
1855      *       <td>{@link DatatypeConstants#GYEARMONTH}</td>
1856      *       <td>X</td>
1857      *       <td>X</td>
1858      *       <td></td>
1859      *       <td></td>
1860      *       <td></td>
1861      *       <td></td>
1862      *     </tr>
1863      *     <tr>
1864      *       <td>{@link DatatypeConstants#GMONTHDAY}</td>
1865      *       <td></td>
1866      *       <td>X</td>
1867      *       <td>X</td>
1868      *       <td></td>
1869      *       <td></td>
1870      *       <td></td>
1871      *     </tr>
1872      *     <tr>
1873      *       <td>{@link DatatypeConstants#GYEAR}</td>
1874      *       <td>X</td>
1875      *       <td></td>
1876      *       <td></td>
1877      *       <td></td>
1878      *       <td></td>
1879      *       <td></td>
1880      *     </tr>
1881      *     <tr>
1882      *       <td>{@link DatatypeConstants#GMONTH}</td>
1883      *       <td></td>
1884      *       <td>X</td>
1885      *       <td></td>
1886      *       <td></td>
1887      *       <td></td>
1888      *       <td></td>
1889      *     </tr>
1890      *     <tr>
1891      *       <td>{@link DatatypeConstants#GDAY}</td>
1892      *       <td></td>
1893      *       <td></td>
1894      *       <td>X</td>
1895      *       <td></td>
1896      *       <td></td>
1897      *       <td></td>
1898      *     </tr>
1899      *   </tbody>
1900      * </table>
1901      *
1902      * @throws java.lang.IllegalStateException if the combination of set fields
1903      *    does not match one of the eight defined XML Schema builtin
1904      *    date/time datatypes.
1905      * @return One of the following class constants:
1906      *   {@link DatatypeConstants#DATETIME},
1907      *   {@link DatatypeConstants#TIME},
1908      *   {@link DatatypeConstants#DATE},
1909      *   {@link DatatypeConstants#GYEARMONTH},
1910      *   {@link DatatypeConstants#GMONTHDAY},
1911      *   {@link DatatypeConstants#GYEAR},
1912      *   {@link DatatypeConstants#GMONTH} or
1913      *   {@link DatatypeConstants#GDAY}.
1914      */
1915     public QName getXMLSchemaType() {
1916 
1917         int mask =
1918             (year != DatatypeConstants.FIELD_UNDEFINED ?   0x20 : 0 )|
1919             (month != DatatypeConstants.FIELD_UNDEFINED ?  0x10 : 0 )|
1920             (day != DatatypeConstants.FIELD_UNDEFINED ?    0x08 : 0 )|
1921             (hour != DatatypeConstants.FIELD_UNDEFINED ?   0x04 : 0 )|
1922             (minute != DatatypeConstants.FIELD_UNDEFINED ? 0x02 : 0 )|
1923             (second != DatatypeConstants.FIELD_UNDEFINED ? 0x01 : 0 );
1924 
1925         switch(mask) {
1926         case 0x3F:
1927                 return DatatypeConstants.DATETIME;
1928         case 0x38:
1929                 return DatatypeConstants.DATE;
1930         case 0x07:
1931                 return DatatypeConstants.TIME;
1932         case 0x30:
1933                 return DatatypeConstants.GYEARMONTH;
1934         case 0x18:
1935                 return DatatypeConstants.GMONTHDAY;
1936         case 0x20:
1937                 return DatatypeConstants.GYEAR;
1938         case 0x10:
1939                 return DatatypeConstants.GMONTH;
1940         case 0x08:
1941                 return DatatypeConstants.GDAY;
1942         default:
1943             throw new IllegalStateException(
1944                 this.getClass().getName()
1945                 + "#getXMLSchemaType() :"
1946                 + DatatypeMessageFormatter.formatMessage(null, "InvalidXGCFields", null)
1947             );
1948         }
1949     }
1950 
1951 
1952     /**
1953      * Validate instance by <code>getXMLSchemaType()</code> constraints.
1954      * @return true if data values are valid.
1955      */
1956     public final boolean isValid() {
1957         // since setters do not allow for invalid values,
1958         // (except for exceptional case of year field of zero),
1959         // no need to check for anything except for constraints
1960         // between fields.
1961 
1962         // check if days in month is valid. Can be dependent on leap year.
1963         if (month != DatatypeConstants.FIELD_UNDEFINED && day != DatatypeConstants.FIELD_UNDEFINED) {
1964             if (year != DatatypeConstants.FIELD_UNDEFINED) {
1965                 if (eon == null) {
1966                     if (day > maximumDayInMonthFor(year, month)) {
1967                         return false;
1968                     }
1969                 }
1970                 else if (day > maximumDayInMonthFor(getEonAndYear(), month)) {
1971                     return false;
1972                 }
1973             }
1974             // Use 2000 as a default since it's a leap year.
1975             else if (day > maximumDayInMonthFor(2000, month)) {
1976                 return false;
1977             }
1978         }
1979 
1980         // http://www.w3.org/2001/05/xmlschema-errata#e2-45
1981         if (hour == 24 && (minute != 0 || second != 0 ||
1982                 (fractionalSecond != null && fractionalSecond.compareTo(DECIMAL_ZERO) != 0))) {
1983             return false;
1984         }
1985 
1986         // XML Schema 1.0 specification defines year value of zero as
1987         // invalid. Allow this class to set year field to zero
1988         // since XML Schema 1.0 errata states that lexical zero will
1989         // be allowed in next version and treated as 1 B.C.E.
1990         if (eon == null && year == 0) {
1991             return false;
1992         }
1993         return true;
1994     }
1995 
1996     /**
1997      * <p>Add <code>duration</code> to this instance.<\p>
1998      *
1999      * <p>The computation is specified in
2000      * <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
2001      * <i>Adding durations to dateTimes</i>></a>.
2002      * <a href="#datetimefieldsmapping">date/time field mapping table</a>
2003      * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
2004      * to this class' representation of those fields.</p>
2005      *
2006      * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
2007      *
2008      * @throws NullPointerException  when <code>duration</code> parameter is <code>null</code>.
2009      */
2010     public void add(Duration duration) {
2011 
2012         /*
2013            * Extracted from
2014            * http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes
2015            * to ensure implemented properly. See spec for definitions of methods
2016            * used in algorithm.
2017            *
2018            * Given a dateTime S and a duration D, specifies how to compute a
2019            * dateTime E where E is the end of the time period with start S and
2020            * duration D i.e. E = S + D.
2021            *
2022            * The following is the precise specification.
2023            * These steps must be followed in the same order.
2024            * If a field in D is not specified, it is treated as if it were zero.
2025            * If a field in S is not specified, it is treated in the calculation
2026            * as if it were the minimum allowed value in that field, however,
2027            * after the calculation is concluded, the corresponding field in
2028            * E is removed (set to unspecified).
2029            *
2030            * Months (may be modified additionally below)
2031                *  temp := S[month] + D[month]
2032                *  E[month] := modulo(temp, 1, 13)
2033                *  carry := fQuotient(temp, 1, 13)
2034            */
2035 
2036         boolean fieldUndefined[] = {
2037                 false,
2038                 false,
2039                 false,
2040                 false,
2041                 false,
2042                 false
2043         };
2044 
2045         int signum = duration.getSign();
2046 
2047         int startMonth = getMonth();
2048         if (startMonth == DatatypeConstants.FIELD_UNDEFINED) {
2049             startMonth = DatatypeConstants.JANUARY;
2050             fieldUndefined[MONTH] = true;
2051         }
2052 
2053         BigInteger dMonths = sanitize(duration.getField(DatatypeConstants.MONTHS), signum);
2054         BigInteger temp = BigInteger.valueOf((long) startMonth).add(dMonths);
2055         setMonth(temp.subtract(BigInteger.ONE).mod(TWELVE).intValue() + 1);
2056         BigInteger carry =
2057                 new BigDecimal(temp.subtract(BigInteger.ONE))
2058                         .divide(DECIMAL_TWELVE, RoundingMode.FLOOR).toBigInteger();
2059 
2060         /* Years (may be modified additionally below)
2061             *  E[year] := S[year] + D[year] + carry
2062             */
2063         BigInteger startYear = getEonAndYear();
2064         if (startYear == null) {
2065             fieldUndefined[YEAR] = true;
2066             startYear = BigInteger.ZERO;
2067         }
2068         BigInteger dYears = sanitize(duration.getField(DatatypeConstants.YEARS), signum);
2069         BigInteger endYear = startYear.add(dYears).add(carry);
2070         setYear(endYear);
2071 
2072         /* Zone
2073                *  E[zone] := S[zone]
2074            *
2075            * no-op since adding to this, not to a new end point.
2076            */
2077 
2078         /* Seconds
2079             *  temp := S[second] + D[second]
2080             *  E[second] := modulo(temp, 60)
2081             *  carry := fQuotient(temp, 60)
2082             */
2083         BigDecimal startSeconds;
2084         if (getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
2085             fieldUndefined[SECOND] = true;
2086             startSeconds = DECIMAL_ZERO;
2087         } else {
2088             // seconds + fractionalSeconds
2089             startSeconds = getSeconds();
2090         }
2091 
2092         // Duration seconds is SECONDS + FRACTIONALSECONDS.
2093         BigDecimal dSeconds = DurationImpl.sanitize((BigDecimal) duration.getField(DatatypeConstants.SECONDS), signum);
2094         BigDecimal tempBD = startSeconds.add(dSeconds);
2095         BigDecimal fQuotient =
2096                 new BigDecimal(new BigDecimal(tempBD.toBigInteger()).divide(DECIMAL_SIXTY, RoundingMode.FLOOR).toBigInteger());
2097         BigDecimal endSeconds = tempBD.subtract(fQuotient.multiply(DECIMAL_SIXTY));
2098 
2099         carry = fQuotient.toBigInteger();
2100         setSecond(endSeconds.intValue());
2101         BigDecimal tempFracSeconds = endSeconds.subtract(new BigDecimal(BigInteger.valueOf((long) getSecond())));
2102         if (tempFracSeconds.compareTo(DECIMAL_ZERO) < 0) {
2103             setFractionalSecond(DECIMAL_ONE.add(tempFracSeconds));
2104             if (getSecond() == 0) {
2105                 setSecond(59);
2106                 carry = carry.subtract(BigInteger.ONE);
2107             } else {
2108                 setSecond(getSecond() - 1);
2109             }
2110         } else {
2111             setFractionalSecond(tempFracSeconds);
2112         }
2113 
2114         /* Minutes
2115                *  temp := S[minute] + D[minute] + carry
2116                *  E[minute] := modulo(temp, 60)
2117                *  carry := fQuotient(temp, 60)
2118            */
2119         int startMinutes = getMinute();
2120         if (startMinutes == DatatypeConstants.FIELD_UNDEFINED) {
2121             fieldUndefined[MINUTE] = true;
2122             startMinutes = 0;
2123         }
2124         BigInteger dMinutes = sanitize(duration.getField(DatatypeConstants.MINUTES), signum);
2125 
2126         temp = BigInteger.valueOf(startMinutes).add(dMinutes).add(carry);
2127         setMinute(temp.mod(SIXTY).intValue());
2128         carry = new BigDecimal(temp).divide(DECIMAL_SIXTY, RoundingMode.FLOOR).toBigInteger();
2129 
2130         /* Hours
2131                *  temp := S[hour] + D[hour] + carry
2132                *  E[hour] := modulo(temp, 24)
2133                *  carry := fQuotient(temp, 24)
2134            */
2135         int startHours = getHour();
2136         if (startHours == DatatypeConstants.FIELD_UNDEFINED) {
2137             fieldUndefined[HOUR] = true;
2138             startHours = 0;
2139         }
2140         BigInteger dHours = sanitize(duration.getField(DatatypeConstants.HOURS), signum);
2141 
2142         temp = BigInteger.valueOf(startHours).add(dHours).add(carry);
2143         setHour(temp.mod(TWENTY_FOUR).intValue(), false);
2144         carry = new BigDecimal(temp).divide(DECIMAL_TWENTY_FOUR,
2145                 RoundingMode.FLOOR).toBigInteger();
2146 
2147         /* Days
2148            *  if S[day] > maximumDayInMonthFor(E[year], E[month])
2149            *       + tempDays := maximumDayInMonthFor(E[year], E[month])
2150            *  else if S[day] < 1
2151            *       + tempDays := 1
2152            *  else
2153            *       + tempDays := S[day]
2154            *  E[day] := tempDays + D[day] + carry
2155            *  START LOOP
2156            *       + IF E[day] < 1
2157            *             # E[day] := E[day] +
2158             *                 maximumDayInMonthFor(E[year], E[month] - 1)
2159            *             # carry := -1
2160            *       + ELSE IF E[day] > maximumDayInMonthFor(E[year], E[month])
2161            *             # E[day] :=
2162             *                    E[day] - maximumDayInMonthFor(E[year], E[month])
2163            *             # carry := 1
2164            *       + ELSE EXIT LOOP
2165            *       + temp := E[month] + carry
2166            *       + E[month] := modulo(temp, 1, 13)
2167            *       + E[year] := E[year] + fQuotient(temp, 1, 13)
2168            *       + GOTO START LOOP
2169            */
2170         BigInteger tempDays;
2171         int startDay = getDay();
2172         if (startDay == DatatypeConstants.FIELD_UNDEFINED) {
2173             fieldUndefined[DAY] = true;
2174             startDay = 1;
2175         }
2176         BigInteger dDays = sanitize(duration.getField(DatatypeConstants.DAYS), signum);
2177         int maxDayInMonth = maximumDayInMonthFor(getEonAndYear(), getMonth());
2178         if (startDay > maxDayInMonth) {
2179             tempDays = BigInteger.valueOf(maxDayInMonth);
2180         } else if (startDay < 1) {
2181             tempDays = BigInteger.ONE;
2182         } else {
2183             tempDays = BigInteger.valueOf(startDay);
2184         }
2185         BigInteger endDays = tempDays.add(dDays).add(carry);
2186         int monthCarry;
2187         int intTemp;
2188         while (true) {
2189             if (endDays.compareTo(BigInteger.ONE) < 0) {
2190                 // calculate days in previous month, watch for month roll over
2191                 BigInteger mdimf = null;
2192                 if (month >= 2) {
2193                     mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear(),
2194                             getMonth() - 1));
2195                 } else {
2196                     // roll over to December of previous year
2197                     mdimf = BigInteger.valueOf(maximumDayInMonthFor(getEonAndYear()
2198                             .subtract(BigInteger.ONE), 12));
2199                 }
2200                 endDays = endDays.add(mdimf);
2201                 monthCarry = -1;
2202             } else if (endDays.compareTo(BigInteger.valueOf(
2203                     maximumDayInMonthFor(getEonAndYear(), getMonth()))) > 0) {
2204                 endDays = endDays.add(BigInteger.valueOf(
2205                         -maximumDayInMonthFor(getEonAndYear(), getMonth())));
2206                 monthCarry = 1;
2207             } else {
2208                 break;
2209             }
2210 
2211             intTemp = getMonth() + monthCarry;
2212             int endMonth = (intTemp - 1) % (13 - 1);
2213             int quotient;
2214             if (endMonth < 0) {
2215                 endMonth = (13 - 1) + endMonth + 1;
2216                 quotient = BigDecimal.valueOf(intTemp - 1)
2217                         .divide(DECIMAL_TWELVE, RoundingMode.UP).intValue();
2218             } else {
2219                 quotient = (intTemp - 1) / (13 - 1);
2220                 endMonth += 1;
2221             }
2222             setMonth(endMonth);
2223             if (quotient != 0) {
2224                 setYear(getEonAndYear().add(BigInteger.valueOf(quotient)));
2225             }
2226         }
2227         setDay(endDays.intValue());
2228 
2229         // set fields that where undefined before this addition, back to undefined.
2230         for (int i = YEAR; i <= SECOND; i++) {
2231             if (fieldUndefined[i]) {
2232                 switch (i) {
2233                 case YEAR:
2234                     setYear(DatatypeConstants.FIELD_UNDEFINED);
2235                     break;
2236                 case MONTH:
2237                     setMonth(DatatypeConstants.FIELD_UNDEFINED);
2238                     break;
2239                 case DAY:
2240                     setDay(DatatypeConstants.FIELD_UNDEFINED);
2241                     break;
2242                 case HOUR:
2243                     setHour(DatatypeConstants.FIELD_UNDEFINED, false);
2244                     break;
2245                 case MINUTE:
2246                     setMinute(DatatypeConstants.FIELD_UNDEFINED);
2247                     break;
2248                 case SECOND:
2249                     setSecond(DatatypeConstants.FIELD_UNDEFINED);
2250                     setFractionalSecond(null);
2251                     break;
2252                 }
2253             }
2254         }
2255     }
2256 
2257     private static final BigInteger FOUR = BigInteger.valueOf(4);
2258     private static final BigInteger HUNDRED = BigInteger.valueOf(100);
2259     private static final BigInteger FOUR_HUNDRED = BigInteger.valueOf(400);
2260     private static final BigInteger SIXTY = BigInteger.valueOf(60);
2261     private static final BigInteger TWENTY_FOUR = BigInteger.valueOf(24);
2262     private static final BigInteger TWELVE = BigInteger.valueOf(12);
2263     private static final BigDecimal DECIMAL_ZERO = BigDecimal.valueOf(0);
2264     private static final BigDecimal DECIMAL_ONE = BigDecimal.valueOf(1);
2265     private static final BigDecimal DECIMAL_TWELVE = BigDecimal.valueOf(12);
2266     private static final BigDecimal DECIMAL_TWENTY_FOUR = BigDecimal.valueOf(24);
2267     private static final BigDecimal DECIMAL_SIXTY = BigDecimal.valueOf(60);
2268 
2269 
2270     private static class DaysInMonth {
2271         private static final int [] table = { 0,  // XML Schema months start at 1.
2272             31, 28, 31, 30, 31, 30,
2273             31, 31, 30, 31, 30, 31};
2274     }
2275 
2276     private static int maximumDayInMonthFor(BigInteger year, int month) {
2277         if (month != DatatypeConstants.FEBRUARY) {
2278             return DaysInMonth.table[month];
2279         } else {
2280             if (year.mod(FOUR_HUNDRED).equals(BigInteger.ZERO) ||
2281                     (!year.mod(HUNDRED).equals(BigInteger.ZERO) &&
2282                             year.mod(FOUR).equals(BigInteger.ZERO))) {
2283                 // is a leap year.
2284                 return 29;
2285             } else {
2286                 return DaysInMonth.table[month];
2287             }
2288         }
2289     }
2290 
2291     private static int maximumDayInMonthFor(int year, int month) {
2292         if (month != DatatypeConstants.FEBRUARY) {
2293             return DaysInMonth.table[month];
2294         } else {
2295             if (((year % 400) == 0) ||
2296                     (((year % 100) != 0) && ((year % 4) == 0))) {
2297                 // is a leap year.
2298                 return 29;
2299             } else {
2300                 return DaysInMonth.table[DatatypeConstants.FEBRUARY];
2301             }
2302         }
2303     }
2304 
2305     /**
2306      * <p>Convert <code>this</code> to <code>java.util.GregorianCalendar</code>.</p>
2307      *
2308      * <p>When <code>this</code> instance has an undefined field, this
2309      * conversion relies on the <code>java.util.GregorianCalendar</code> default
2310      * for its corresponding field. A notable difference between
2311      * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
2312      * is that Timezone value is optional for date/time datatypes and it is
2313      * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
2314      * for <code>java.util.TimeZone.getDefault()</code> on how the default
2315      * is determined. To explicitly specify the <code>TimeZone</code>
2316      * instance, see
2317      * {@link #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)}.</p>
2318      *
2319      * <table border="2" rules="all" cellpadding="2">
2320      *   <thead>
2321      *     <tr>
2322      *       <th align="center" colspan="2">
2323      *          Field by Field Conversion from this class to
2324      *          <code>java.util.GregorianCalendar</code>
2325      *       </th>
2326      *     </tr>
2327      *   </thead>
2328      *   <tbody>
2329      *     <tr>
2330      *        <th><code>java.util.GregorianCalendar</code> field</th>
2331      *        <th><code>javax.xml.datatype.XMLGregorianCalendar</code> field</th>
2332      *     </tr>
2333      *     <tr>
2334      *       <th><code>ERA</code></th>
2335      *       <th>{@link #getEonAndYear()}<code>.signum() < 0 ? GregorianCalendar.BC : GregorianCalendar.AD</code></th>
2336      *     </tr>
2337      *     <tr>
2338      *       <th><code>YEAR</code></th>
2339      *       <th>{@link #getEonAndYear()}<code>.abs().intValue()</code><i>*</i></th>
2340      *     </tr>
2341      *     <tr>
2342      *       <th><code>MONTH</code></th>
2343      *       <th>{@link #getMonth()}<code> - 1</code></th>
2344      *     </tr>
2345      *     <tr>
2346      *       <th><code>DAY_OF_MONTH</code></th>
2347      *       <th>{@link #getDay()}</th>
2348      *     </tr>
2349      *     <tr>
2350      *       <th><code>AM_PM</code></th>
2351      *       <th>{@link #getHour()} < 12 : Calendar.AM : Calendar.PM</th>
2352      *     </tr>
2353      *     <tr>
2354      *       <th><code>HOUR_OF_DAY</code></th>
2355      *       <th>{@link #getHour()}</th>
2356      *     </tr>
2357      *     <tr>
2358      *       <th><code>MINUTE</code></th>
2359      *       <th>{@link #getMinute()}</th>
2360      *     </tr>
2361      *     <tr>
2362      *       <th><code>SECOND</code></th>
2363      *       <th>{@link #getSecond()}</th>
2364      *     </tr>
2365      *     <tr>
2366      *       <th><code>MILLISECOND</code></th>
2367      *       <th>get millisecond order from {@link #getFractionalSecond()}<i>*</i> </th>
2368      *     </tr>
2369      *     <tr>
2370      *       <th><code>GregorianCalendar.setTimeZone(TimeZone)</code></th>
2371      *       <th>{@link #getTimezone()} formatted into Custom timezone id</th>
2372      *     </tr>
2373      *   </tbody>
2374      * </table>
2375      * <i>*</i> designates possible loss of precision during the conversion due
2376      * to source datatype having higer precison than target datatype.
2377      *
2378      * <p>To ensure consistency in conversion implementations, the new
2379      * <code>GregorianCalendar</code> should be instantiated in following
2380      * manner.
2381      * <ul>
2382      *   <li>Using <code>timeZone</code> value as defined above, create a new
2383      * <code>java.util.GregorianCalendar(timeZone,Locale.getDefault())</code>.
2384      *   </li>
2385      *   <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2386      *   <li>Obtain a pure Gregorian Calendar by invoking
2387      *   <code>GregorianCalendar.setGregorianChange(
2388      *   new Date(Long.MIN_VALUE))</code>.</li>
2389      *   <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2390      *       MINUTE, SECOND and MILLISECOND are set using the method
2391      *       <code>Calendar.set(int,int)</code></li>
2392      * </ul>
2393      * </p>
2394      *
2395      * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, XMLGregorianCalendar)
2396      */
2397     public java.util.GregorianCalendar toGregorianCalendar() {
2398 
2399         GregorianCalendar result = null;
2400         final int DEFAULT_TIMEZONE_OFFSET = DatatypeConstants.FIELD_UNDEFINED;
2401         TimeZone tz = getTimeZone(DEFAULT_TIMEZONE_OFFSET);
2402         /** Use the following instead for JDK7 only:
2403          * Locale locale = Locale.getDefault(Locale.Category.FORMAT);
2404          */
2405         Locale locale = getDefaultLocale();
2406 
2407         result = new GregorianCalendar(tz, locale);
2408         result.clear();
2409         result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2410 
2411         // if year( and eon) are undefined, leave default Calendar values
2412         if (year != DatatypeConstants.FIELD_UNDEFINED) {
2413             if (eon == null) {
2414                 result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2415                 result.set(Calendar.YEAR, Math.abs(year));
2416             }
2417             else {
2418                 BigInteger eonAndYear = getEonAndYear();
2419                 result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2420                 result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2421             }
2422         }
2423 
2424         // only set month if it is set
2425         if (month != DatatypeConstants.FIELD_UNDEFINED) {
2426             // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2427             result.set(Calendar.MONTH, month - 1);
2428         }
2429 
2430         // only set day if it is set
2431         if (day != DatatypeConstants.FIELD_UNDEFINED) {
2432             result.set(Calendar.DAY_OF_MONTH, day);
2433         }
2434 
2435         // only set hour if it is set
2436         if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2437             result.set(Calendar.HOUR_OF_DAY, hour);
2438         }
2439 
2440         // only set minute if it is set
2441         if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2442             result.set(Calendar.MINUTE, minute);
2443         }
2444 
2445         // only set second if it is set
2446         if (second != DatatypeConstants.FIELD_UNDEFINED) {
2447             result.set(Calendar.SECOND, second);
2448         }
2449 
2450         // only set millisend if it is set
2451         if (fractionalSecond != null) {
2452             result.set(Calendar.MILLISECOND, getMillisecond());
2453         }
2454 
2455         return result;
2456     }
2457 
2458     /**
2459      *
2460      * @return default locale
2461      */
2462     private Locale getDefaultLocale() {
2463 
2464         String lang = SecuritySupport.getSystemProperty("user.language.format");
2465         String country = SecuritySupport.getSystemProperty("user.country.format");
2466         String variant = SecuritySupport.getSystemProperty("user.variant.format");
2467         Locale locale = null;
2468         if (lang != null) {
2469             if (country != null) {
2470                 if (variant != null) {
2471                     locale = new Locale(lang, country, variant);
2472                 } else {
2473                     locale = new Locale(lang, country);
2474                 }
2475             } else {
2476                 locale = new Locale(lang);
2477             }
2478         }
2479         if (locale == null) {
2480             locale = Locale.getDefault();
2481         }
2482         return locale;
2483     }
2484 
2485     /**
2486      * <p>Convert <code>this</code> along with provided parameters
2487      * to <code>java.util.GregorianCalendar</code> instance.</p>
2488      *
2489      * <p> Since XML Schema 1.0 date/time datetypes has no concept of
2490      * timezone ids or daylight savings timezone ids, this conversion operation
2491      * allows the user to explicitly specify one with
2492      * <code>timezone</code> parameter.</p>
2493      *
2494      * <p>To compute the return value's <code>TimeZone</code> field,
2495      * <ul>
2496      * <li>when parameter <code>timeZone</code> is non-null,
2497      * it is the timezone field.</li>
2498      * <li>else when <code>this.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2499      * create a <code>java.util.TimeZone</code> with a custom timezone id
2500      * using the <code>this.getTimezone()</code>.</li>
2501      * <li>else when <code>defaults.getTimezone() != DatatypeConstants.FIELD_UNDEFINED</code>,
2502      * create a <code>java.util.TimeZone</code> with a custom timezone id
2503      * using <code>defaults.getTimezone()</code>.</li>
2504      * <li>else use the <code>GregorianCalendar</code> default timezone value
2505      * for the host is definedas specified by
2506      * <code>java.util.TimeZone.getDefault()</code>.</li></p>
2507      *
2508      * <p>To ensure consistency in conversion implementations, the new
2509      * <code>GregorianCalendar</code> should be instantiated in following
2510      * manner.
2511      * <ul>
2512      *   <li>Create a new <code>java.util.GregorianCalendar(TimeZone,
2513      *       Locale)</code> with TimeZone set as specified above and the
2514      *       <code>Locale</code> parameter.
2515      *   </li>
2516      *   <li>Initialize all GregorianCalendar fields by calling {(@link GegorianCalendar#clear()}.</li>
2517      *   <li>Obtain a pure Gregorian Calendar by invoking
2518      *   <code>GregorianCalendar.setGregorianChange(
2519      *   new Date(Long.MIN_VALUE))</code>.</li>
2520      *   <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
2521      *       MINUTE, SECOND and MILLISECOND are set using the method
2522      *       <code>Calendar.set(int,int)</code></li>
2523      * </ul>
2524      *
2525      * @param timezone provide Timezone. <code>null</code> is a legal value.
2526      * @param aLocale  provide explicit Locale. Use default GregorianCalendar locale if
2527      *                 value is <code>null</code>.
2528      * @param defaults provide default field values to use when corresponding
2529      *                 field for this instance is DatatypeConstants.FIELD_UNDEFINED or null.
2530      *                 If <code>defaults</code>is <code>null</code> or a field
2531      *                 within the specified <code>defaults</code> is undefined,
2532      *                 just use <code>java.util.GregorianCalendar</code> defaults.
2533      * @return a java.util.GregorianCalendar conversion of this instance.
2534      *
2535      * @see #LEAP_YEAR_DEFAULT
2536      */
2537     public GregorianCalendar toGregorianCalendar(TimeZone timezone,
2538                                                  Locale aLocale,
2539                                                  XMLGregorianCalendar defaults) {
2540         GregorianCalendar result = null;
2541         TimeZone tz = timezone;
2542         if (tz == null) {
2543             int defaultZoneoffset = DatatypeConstants.FIELD_UNDEFINED;
2544             if (defaults != null) {
2545                 defaultZoneoffset = defaults.getTimezone();
2546             }
2547             tz = getTimeZone(defaultZoneoffset);
2548         }
2549         if (aLocale == null) {
2550             aLocale = Locale.getDefault();
2551         }
2552         result = new GregorianCalendar(tz, aLocale);
2553         result.clear();
2554         result.setGregorianChange(PURE_GREGORIAN_CHANGE);
2555 
2556         // if year( and eon) are undefined, leave default Calendar values
2557         if (year != DatatypeConstants.FIELD_UNDEFINED) {
2558             if (eon == null) {
2559                 result.set(Calendar.ERA, year < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2560                 result.set(Calendar.YEAR, Math.abs(year));
2561             }
2562             else {
2563                 final BigInteger eonAndYear = getEonAndYear();
2564                 result.set(Calendar.ERA, eonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2565                 result.set(Calendar.YEAR, eonAndYear.abs().intValue());
2566             }
2567         } else {
2568             // use default if set
2569             if (defaults != null) {
2570                 final int defaultYear = defaults.getYear();
2571                 if (defaultYear != DatatypeConstants.FIELD_UNDEFINED) {
2572                     if (defaults.getEon() == null) {
2573                         result.set(Calendar.ERA, defaultYear < 0 ? GregorianCalendar.BC : GregorianCalendar.AD);
2574                         result.set(Calendar.YEAR, Math.abs(defaultYear));
2575                     }
2576                     else {
2577                         final BigInteger defaultEonAndYear = defaults.getEonAndYear();
2578                         result.set(Calendar.ERA, defaultEonAndYear.signum() == -1 ? GregorianCalendar.BC : GregorianCalendar.AD);
2579                         result.set(Calendar.YEAR, defaultEonAndYear.abs().intValue());
2580                     }
2581                 }
2582             }
2583         }
2584 
2585         // only set month if it is set
2586         if (month != DatatypeConstants.FIELD_UNDEFINED) {
2587             // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2588             result.set(Calendar.MONTH, month - 1);
2589         } else {
2590             // use default if set
2591             final int defaultMonth = (defaults != null) ? defaults.getMonth() : DatatypeConstants.FIELD_UNDEFINED;
2592             if (defaultMonth != DatatypeConstants.FIELD_UNDEFINED) {
2593                 // Calendar.MONTH is zero based while XMLGregorianCalendar month field is not.
2594                 result.set(Calendar.MONTH, defaultMonth - 1);
2595             }
2596         }
2597 
2598         // only set day if it is set
2599         if (day != DatatypeConstants.FIELD_UNDEFINED) {
2600             result.set(Calendar.DAY_OF_MONTH, day);
2601         } else {
2602             // use default if set
2603             final int defaultDay = (defaults != null) ? defaults.getDay() : DatatypeConstants.FIELD_UNDEFINED;
2604             if (defaultDay != DatatypeConstants.FIELD_UNDEFINED) {
2605                 result.set(Calendar.DAY_OF_MONTH, defaultDay);
2606             }
2607         }
2608 
2609         // only set hour if it is set
2610         if (hour != DatatypeConstants.FIELD_UNDEFINED) {
2611             result.set(Calendar.HOUR_OF_DAY, hour);
2612         } else {
2613             // use default if set
2614             int defaultHour = (defaults != null) ? defaults.getHour() : DatatypeConstants.FIELD_UNDEFINED;
2615             if (defaultHour != DatatypeConstants.FIELD_UNDEFINED) {
2616                 result.set(Calendar.HOUR_OF_DAY, defaultHour);
2617             }
2618         }
2619 
2620         // only set minute if it is set
2621         if (minute != DatatypeConstants.FIELD_UNDEFINED) {
2622             result.set(Calendar.MINUTE, minute);
2623         } else {
2624             // use default if set
2625             final int defaultMinute = (defaults != null) ? defaults.getMinute() : DatatypeConstants.FIELD_UNDEFINED;
2626             if (defaultMinute != DatatypeConstants.FIELD_UNDEFINED) {
2627                 result.set(Calendar.MINUTE, defaultMinute);
2628             }
2629         }
2630 
2631         // only set second if it is set
2632         if (second != DatatypeConstants.FIELD_UNDEFINED) {
2633             result.set(Calendar.SECOND, second);
2634         } else {
2635             // use default if set
2636             final int defaultSecond = (defaults != null) ? defaults.getSecond() : DatatypeConstants.FIELD_UNDEFINED;
2637             if (defaultSecond != DatatypeConstants.FIELD_UNDEFINED) {
2638                 result.set(Calendar.SECOND, defaultSecond);
2639             }
2640         }
2641 
2642         // only set millisend if it is set
2643         if (fractionalSecond != null) {
2644             result.set(Calendar.MILLISECOND, getMillisecond());
2645         } else {
2646             // use default if set
2647             final BigDecimal defaultFractionalSecond = (defaults != null) ? defaults.getFractionalSecond() : null;
2648             if (defaultFractionalSecond != null) {
2649                 result.set(Calendar.MILLISECOND, defaults.getMillisecond());
2650             }
2651         }
2652 
2653         return result;
2654     }
2655 
2656     /**
2657      * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
2658      *
2659      * <p>If timezone field is defined for this instance,
2660      * returns TimeZone initialized with custom timezone id
2661      * of zoneoffset. If timezone field is undefined,
2662      * try the defaultZoneoffset that was passed in.
2663      * If defaultZoneoffset is DatatypeConstants.FIELD_UNDEFINED, return
2664      * default timezone for this host.
2665      * (Same default as java.util.GregorianCalendar).</p>
2666      *
2667      * @param defaultZoneoffset default zoneoffset if this zoneoffset is
2668      * {@link DatatypeConstants#FIELD_UNDEFINED}.
2669      *
2670      * @return TimeZone for this.
2671      */
2672     public TimeZone getTimeZone(int defaultZoneoffset) {
2673         TimeZone result = null;
2674         int zoneoffset = getTimezone();
2675 
2676         if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2677             zoneoffset = defaultZoneoffset;
2678         }
2679         if (zoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
2680             result = TimeZone.getDefault();
2681         } else {
2682             // zoneoffset is in minutes. Convert to custom timezone id format.
2683             char sign = zoneoffset < 0 ? '-' : '+';
2684             if (sign == '-') {
2685                 zoneoffset = -zoneoffset;
2686             }
2687             int hour = zoneoffset / 60;
2688             int minutes = zoneoffset - (hour * 60);
2689 
2690             // Javadoc for java.util.TimeZone documents max length
2691             // for customTimezoneId is 8 when optional ':' is not used.
2692             // Format is
2693             // "GMT" ('-'|''+') (digit digit?) (digit digit)?
2694             //                   hour          minutes
2695             StringBuffer customTimezoneId = new StringBuffer(8);
2696             customTimezoneId.append("GMT");
2697             customTimezoneId.append(sign);
2698             customTimezoneId.append(hour);
2699             if (minutes != 0) {
2700                 if (minutes < 10) {
2701                     customTimezoneId.append('0');
2702                 }
2703                 customTimezoneId.append(minutes);
2704             }
2705             result = TimeZone.getTimeZone(customTimezoneId.toString());
2706         }
2707         return result;
2708     }
2709 
2710     /**
2711      * <p>Creates and returns a copy of this object.</p>
2712      *
2713      * @return copy of this <code>Object</code>
2714      */
2715    public Object clone() {
2716         // Both this.eon and this.fractionalSecond are instances
2717         // of immutable classes, so they do not need to be cloned.
2718        return new XMLGregorianCalendarImpl(getEonAndYear(),
2719                         this.month, this.day,
2720                         this.hour, this.minute, this.second,
2721                         this.fractionalSecond,
2722                         this.timezone);
2723     }
2724 
2725     /**
2726      * <p>Unset all fields to undefined.</p>
2727      *
2728      * <p>Set all int fields to {@link DatatypeConstants#FIELD_UNDEFINED} and reference fields
2729      * to null.</p>
2730      */
2731     public void clear() {
2732         eon = null;
2733         year = DatatypeConstants.FIELD_UNDEFINED;
2734         month = DatatypeConstants.FIELD_UNDEFINED;
2735         day = DatatypeConstants.FIELD_UNDEFINED;
2736         timezone = DatatypeConstants.FIELD_UNDEFINED;  // in minutes
2737         hour = DatatypeConstants.FIELD_UNDEFINED;
2738         minute = DatatypeConstants.FIELD_UNDEFINED;
2739         second = DatatypeConstants.FIELD_UNDEFINED;
2740         fractionalSecond = null;
2741     }
2742 
2743     public void setMillisecond(int millisecond) {
2744         if (millisecond == DatatypeConstants.FIELD_UNDEFINED) {
2745             fractionalSecond = null;
2746         } else {
2747             if(millisecond<0 || 999<millisecond)
2748                 if(millisecond!=DatatypeConstants.FIELD_UNDEFINED)
2749                     invalidFieldValue(MILLISECOND, millisecond);
2750             fractionalSecond = BigDecimal.valueOf(millisecond, 3);
2751         }
2752     }
2753 
2754     public final void setFractionalSecond(BigDecimal fractional) {
2755         if (fractional != null) {
2756             if ((fractional.compareTo(DECIMAL_ZERO) < 0) ||
2757                     (fractional.compareTo(DECIMAL_ONE) > 0)) {
2758                 throw new IllegalArgumentException(DatatypeMessageFormatter.formatMessage(null,
2759                         "InvalidFractional", new Object[]{fractional.toString()}));
2760             }
2761         }
2762         this.fractionalSecond = fractional;
2763     }
2764 
2765     private final class Parser {
2766         private final String format;
2767         private final String value;
2768 
2769         private final int flen;
2770         private final int vlen;
2771 
2772         private int fidx;
2773         private int vidx;
2774 
2775         private Parser(String format, String value) {
2776             this.format = format;
2777             this.value = value;
2778             this.flen = format.length();
2779             this.vlen = value.length();
2780         }
2781 
2782         /**
2783          * <p>Parse a formated <code>String</code> into an <code>XMLGregorianCalendar</code>.</p>
2784          *
2785          * <p>If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value,
2786          * an <code>IllegalArgumentException</code> is thrown.</p>
2787          *
2788          * @throws IllegalArgumentException If <code>String</code> is not formated as a legal <code>XMLGregorianCalendar</code> value.
2789          */
2790         public void parse() throws IllegalArgumentException {
2791             while (fidx < flen) {
2792                 char fch = format.charAt(fidx++);
2793 
2794                 if (fch != '%') { // not a meta character
2795                     skip(fch);
2796                     continue;
2797                 }
2798 
2799                 // seen meta character. we don't do error check against the format
2800                 switch (format.charAt(fidx++)) {
2801                     case 'Y' : // year
2802                         parseYear();
2803                         break;
2804 
2805                     case 'M' : // month
2806                         setMonth(parseInt(2, 2));
2807                         break;
2808 
2809                     case 'D' : // days
2810                         setDay(parseInt(2, 2));
2811                         break;
2812 
2813                     case 'h' : // hours
2814                         setHour(parseInt(2, 2), false);
2815                         break;
2816 
2817                     case 'm' : // minutes
2818                         setMinute(parseInt(2, 2));
2819                         break;
2820 
2821                     case 's' : // parse seconds.
2822                         setSecond(parseInt(2, 2));
2823 
2824                         if (peek() == '.') {
2825                             setFractionalSecond(parseBigDecimal());
2826                         }
2827                         break;
2828 
2829                     case 'z' : // time zone. missing, 'Z', or [+-]nn:nn
2830                         char vch = peek();
2831                         if (vch == 'Z') {
2832                             vidx++;
2833                             setTimezone(0);
2834                         } else if (vch == '+' || vch == '-') {
2835                             vidx++;
2836                             int h = parseInt(2, 2);
2837                             skip(':');
2838                             int m = parseInt(2, 2);
2839                             setTimezone((h * 60 + m) * (vch == '+' ? 1 : -1));
2840                         }
2841 
2842                         break;
2843 
2844                     default :
2845                         // illegal meta character. impossible.
2846                         throw new InternalError();
2847                 }
2848             }
2849 
2850             if (vidx != vlen) {
2851                 // some tokens are left in the input
2852                 throw new IllegalArgumentException(value); //,vidx);
2853             }
2854             testHour();
2855         }
2856 
2857         private char peek() throws IllegalArgumentException {
2858             if (vidx == vlen) {
2859                 return (char) -1;
2860             }
2861             return value.charAt(vidx);
2862         }
2863 
2864         private char read() throws IllegalArgumentException {
2865             if (vidx == vlen) {
2866                 throw new IllegalArgumentException(value); //,vidx);
2867             }
2868             return value.charAt(vidx++);
2869         }
2870 
2871         private void skip(char ch) throws IllegalArgumentException {
2872             if (read() != ch) {
2873                 throw new IllegalArgumentException(value); //,vidx-1);
2874             }
2875         }
2876 
2877         private int parseInt(int minDigits, int maxDigits)
2878             throws IllegalArgumentException {
2879 
2880             int n = 0;
2881             char ch;
2882             int vstart = vidx;
2883             while (isDigit(ch=peek()) && (vidx - vstart) < maxDigits) {
2884                 vidx++;
2885                 n = n*10 + ch-'0';
2886             }
2887             if ((vidx - vstart) < minDigits) {
2888                 // we are expecting more digits
2889                 throw new IllegalArgumentException(value); //,vidx);
2890             }
2891 
2892             return n;
2893         }
2894 
2895         private void parseYear()
2896             throws IllegalArgumentException {
2897             int vstart = vidx;
2898             int sign = 0;
2899 
2900             // skip leading negative, if it exists
2901             if (peek() == '-') {
2902                 vidx++;
2903                 sign = 1;
2904             }
2905             while (isDigit(peek())) {
2906                 vidx++;
2907             }
2908             final int digits = vidx - vstart - sign;
2909             if (digits < 4) {
2910                 // we are expecting more digits
2911                 throw new IllegalArgumentException(value); //,vidx);
2912             }
2913             final String yearString = value.substring(vstart, vidx);
2914             if (digits < 10) {
2915                 setYear(Integer.parseInt(yearString));
2916             }
2917             else {
2918                 setYear(new BigInteger(yearString));
2919             }
2920         }
2921 
2922         private BigDecimal parseBigDecimal()
2923                 throws IllegalArgumentException {
2924             int vstart = vidx;
2925 
2926             if (peek() == '.') {
2927                 vidx++;
2928             } else {
2929                 throw new IllegalArgumentException(value);
2930             }
2931             while (isDigit(peek())) {
2932                 vidx++;
2933             }
2934             return new BigDecimal(value.substring(vstart, vidx));
2935         }
2936     }
2937 
2938     private static boolean isDigit(char ch) {
2939         return '0' <= ch && ch <= '9';
2940     }
2941 
2942     /**
2943      * Prints this object according to the format specification.
2944      *
2945      * <p>
2946      * StringBuffer -> StringBuilder change had a very visible impact.
2947      * It almost cut the execution time to half.
2948      * Diff from Xerces:
2949      * Xerces use StringBuffer due to the requirement to support
2950      * JDKs older than JDK 1.5
2951      */
2952     private String format( String format ) {
2953         StringBuilder buf = new StringBuilder();
2954         int fidx=0,flen=format.length();
2955 
2956         while(fidx<flen) {
2957             char fch = format.charAt(fidx++);
2958             if(fch!='%') {// not a meta char
2959                 buf.append(fch);
2960                 continue;
2961             }
2962 
2963             switch(format.charAt(fidx++)) {
2964                 case 'Y':
2965                     if (eon == null) {
2966                         int absYear = year;
2967                         if (absYear < 0) {
2968                             buf.append('-');
2969                             absYear = -year;
2970                         }
2971                         printNumber(buf, absYear, 4);
2972                     }
2973                     else {
2974                         printNumber(buf, getEonAndYear(), 4);
2975                     }
2976                     break;
2977                 case 'M':
2978                     printNumber(buf,getMonth(),2);
2979                     break;
2980                 case 'D':
2981                     printNumber(buf,getDay(),2);
2982                     break;
2983                 case 'h':
2984                     printNumber(buf,getHour(),2);
2985                     break;
2986                 case 'm':
2987                     printNumber(buf,getMinute(),2);
2988                     break;
2989                 case 's':
2990                     printNumber(buf,getSecond(),2);
2991                     if (getFractionalSecond() != null) {
2992                         //Xerces uses a custom method toString instead of
2993                         //toPlainString() since it needs to support JDKs older than 1.5
2994                         String frac = getFractionalSecond().toPlainString();
2995                         //skip leading zero.
2996                         buf.append(frac.substring(1, frac.length()));
2997                     }
2998                     break;
2999                 case 'z':
3000                     int offset = getTimezone();
3001                     if (offset == 0) {
3002                         buf.append('Z');
3003                     }
3004                     else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
3005                         if (offset < 0) {
3006                             buf.append('-');
3007                             offset *= -1;
3008                         }
3009                         else {
3010                             buf.append('+');
3011                         }
3012                         printNumber(buf,offset/60,2);
3013                         buf.append(':');
3014                         printNumber(buf,offset%60,2);
3015                     }
3016                     break;
3017                 default:
3018                     throw new InternalError();  // impossible
3019             }
3020         }
3021 
3022         return buf.toString();
3023     }
3024 
3025     /**
3026      * Prints an integer as a String.
3027      *
3028      * @param out
3029      *      The formatted string will be appended into this buffer.
3030      * @param number
3031      *      The integer to be printed.
3032      * @param nDigits
3033      *      The field will be printed by using at least this
3034      *      number of digits. For example, 5 will be printed as "0005"
3035      *      if nDigits==4.
3036      */
3037     private void printNumber( StringBuilder out, int number, int nDigits ) {
3038         String s = String.valueOf(number);
3039         for (int i = s.length(); i < nDigits; i++) {
3040             out.append('0');
3041         }
3042         out.append(s);
3043     }
3044 
3045     /**
3046      * Prints an BigInteger as a String.
3047      *
3048      * @param out
3049      *      The formatted string will be appended into this buffer.
3050      * @param number
3051      *      The integer to be printed.
3052      * @param nDigits
3053      *      The field will be printed by using at least this
3054      *      number of digits. For example, 5 will be printed as "0005"
3055      *      if nDigits==4.
3056      */
3057     private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
3058         String s = number.toString();
3059         for (int i=s.length(); i < nDigits; i++) {
3060             out.append('0');
3061         }
3062         out.append(s);
3063     }
3064 
3065     /**
3066      * Compute <code>value*signum</code> where value==null is treated as
3067      * value==0.
3068      * @return non-null {@link BigInteger}.
3069      */
3070     static BigInteger sanitize(Number value, int signum) {
3071         if (signum == 0 || value == null) {
3072             return BigInteger.ZERO;
3073         }
3074         return (signum <  0)? ((BigInteger)value).negate() : (BigInteger)value;
3075     }
3076 
3077     /** <p><code>reset()</code> is designed to allow the reuse of existing
3078      * <code>XMLGregorianCalendar</code>s thus saving resources associated
3079      *  with the creation of new <code>XMLGregorianCalendar</code>s.</p>
3080      */
3081     public void reset() {
3082         eon = orig_eon;
3083         year = orig_year;
3084         month = orig_month;
3085         day = orig_day;
3086         hour = orig_hour;
3087         minute = orig_minute;
3088         second = orig_second;
3089         fractionalSecond = orig_fracSeconds;
3090         timezone = orig_timezone;
3091     }
3092 
3093     /** Deserialize Calendar. */
3094     private void readObject(ObjectInputStream ois)
3095         throws ClassNotFoundException, IOException {
3096 
3097         // perform default deseralization
3098         ois.defaultReadObject();
3099 
3100         // initialize orig_* fields
3101         save();
3102 
3103     } // readObject(ObjectInputStream)
3104 }