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