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