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