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