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