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