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