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