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