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