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