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