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