/* * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.org.apache.xerces.internal.jaxp.datatype; import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import jdk.xml.internal.SecuritySupport; /** *

Representation for W3C XML Schema 1.0 date/time datatypes. * Specifically, these date/time datatypes are * {@link DatatypeConstants#DATETIME dateTime}, * {@link DatatypeConstants#TIME time}, * {@link DatatypeConstants#DATE date}, * {@link DatatypeConstants#GYEARMONTH gYearMonth}, * {@link DatatypeConstants#GMONTHDAY gMonthDay}, * {@link DatatypeConstants#GYEAR gYear}, * {@link DatatypeConstants#GMONTH gMonth} and * {@link DatatypeConstants#GDAY gDay} * defined in the XML Namespace * "http://www.w3.org/2001/XMLSchema". * These datatypes are normatively defined in * W3C XML Schema 1.0 Part 2, Section 3.2.7-14.

* *

The table below defines the mapping between XML Schema 1.0 * date/time datatype fields and this class' fields. It also summarizes * the value constraints for the date and time fields defined in * W3C XML Schema 1.0 Part 2, Appendix D, * ISO 8601 Date and Time Formats.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Date/time datatype field mapping between XML Schema 1.0 and Java representation *
XML Schema 1.0
* datatype
* field
Related
XMLGregorianCalendar
Accessor(s)
Value Range
year {@link #getYear()} + {@link #getEon()} or
* {@link #getEonAndYear} *
getYear() is a value between -(10^9-1) to (10^9)-1 * or {@link DatatypeConstants#FIELD_UNDEFINED}.
* {@link #getEon()} is high order year value in billion of years.
* getEon() has values greater than or equal to (10^9) or less than or equal to -(10^9). * A value of null indicates field is undefined.
* Given that XML Schema 1.0 errata states that the year zero * will be a valid lexical value in a future version of XML Schema, * this class allows the year field to be set to zero. Otherwise, * the year field value is handled exactly as described * in the errata and [ISO-8601-1988]. Note that W3C XML Schema 1.0 * validation does not allow for the year field to have a value of zero. *
month {@link #getMonth()} 1 to 12 or {@link DatatypeConstants#FIELD_UNDEFINED}
day {@link #getDay()} Independent of month, max range is 1 to 31 or {@link DatatypeConstants#FIELD_UNDEFINED}.
* The normative value constraint stated relative to month * field's value is in W3C XML Schema 1.0 Part 2, Appendix D. *
hour {@link #getHour()} * 0 to 23 or {@link DatatypeConstants#FIELD_UNDEFINED}. * An hour value of 24 is allowed to be set in the lexical space provided the minute and second * field values are zero. However, an hour value of 24 is not allowed in value space and will be * transformed to represent the value of the first instance of the following day as per * * XML Schema Part 2: Datatypes Second Edition, 3.2 Primitive datatypes. *
minute {@link #getMinute()} 0 to 59 or {@link DatatypeConstants#FIELD_UNDEFINED}
second * {@link #getSecond()} + {@link #getMillisecond()}/1000 or
* {@link #getSecond()} + {@link #getFractionalSecond()} *
* {@link #getSecond()} from 0 to 60 or {@link DatatypeConstants#FIELD_UNDEFINED}.
* (Note: 60 only allowable for leap second.)
* {@link #getFractionalSecond()} allows for infinite precision over the range from 0.0 to 1.0 when * the {@link #getSecond()} is defined.
* FractionalSecond is optional and has a value of null when it is undefined.
* {@link #getMillisecond()} is the convenience * millisecond precision of value of {@link #getFractionalSecond()}. *
timezone {@link #getTimezone()} Number of minutes or {@link DatatypeConstants#FIELD_UNDEFINED}. * Value range from -14 hours (-14 * 60 minutes) to 14 hours (14 * 60 minutes). *
* *

All maximum value space constraints listed for the fields in the table * above are checked by factory methods, setter methods and parse methods of * this class. IllegalArgumentException is thrown when * parameter's value is outside the maximum value constraint for the field. * Validation checks, for example, whether days in month should be * limited to 29, 30 or 31 days, that are dependent on the values of other * fields are not checked by these methods. *

* *

The following operations are defined for this class: *

*

* * @author Kohsuke Kawaguchi * @author Joseph Fialli * @author Sunitha Reddy * @see javax.xml.datatype.Duration * @since 1.5 * @LastModified: Aug 2020 */ public class XMLGregorianCalendarImpl extends XMLGregorianCalendar implements Serializable, Cloneable { /** Backup values **/ transient private BigInteger orig_eon; transient private int orig_year = DatatypeConstants.FIELD_UNDEFINED; transient private int orig_month = DatatypeConstants.FIELD_UNDEFINED; transient private int orig_day = DatatypeConstants.FIELD_UNDEFINED; transient private int orig_hour = DatatypeConstants.FIELD_UNDEFINED; transient private int orig_minute = DatatypeConstants.FIELD_UNDEFINED; transient private int orig_second = DatatypeConstants.FIELD_UNDEFINED; transient private BigDecimal orig_fracSeconds; transient private int orig_timezone = DatatypeConstants.FIELD_UNDEFINED; /** *

Eon of this XMLGregorianCalendar.

*/ private BigInteger eon = null; /** *

Year of this XMLGregorianCalendar.

*/ private int year = DatatypeConstants.FIELD_UNDEFINED; /** *

Month of this XMLGregorianCalendar.

*/ private int month = DatatypeConstants.FIELD_UNDEFINED; /** *

Day of this XMLGregorianCalendar.

*/ private int day = DatatypeConstants.FIELD_UNDEFINED; /** *

Timezone of this XMLGregorianCalendar in minutes.

*/ private int timezone = DatatypeConstants.FIELD_UNDEFINED; /** *

Hour of this XMLGregorianCalendar.

*/ private int hour = DatatypeConstants.FIELD_UNDEFINED; /** *

Minute of this XMLGregorianCalendar.

*/ private int minute = DatatypeConstants.FIELD_UNDEFINED; /** *

Second of this XMLGregorianCalendar.

*/ private int second = DatatypeConstants.FIELD_UNDEFINED ; /** *

Fractional second of this XMLGregorianCalendar.

*/ private BigDecimal fractionalSecond = null; /** *

BigInteger constant; representing a billion.

*/ private static final BigInteger BILLION_B = new BigInteger("1000000000"); /** *

int constant; representing a billion.

*/ private static final int BILLION_I = 1000000000; /** *

Obtain a pure Gregorian Calendar by calling * GregorianCalendar.setChange(PURE_GREGORIAN_CHANGE).

*/ private static final Date PURE_GREGORIAN_CHANGE = new Date(Long.MIN_VALUE); /** * Year index for MIN_ and MAX_FIELD_VALUES. */ private static final int YEAR = 0; /** * Month index for MIN_ and MAX_FIELD_VALUES. */ private static final int MONTH = 1; /** * Day index for MIN_ and MAX_FIELD_VALUES. */ private static final int DAY = 2; /** * Hour index for MIN_ and MAX_FIELD_VALUES. */ private static final int HOUR = 3; /** * Minute index for MIN_ and MAX_FIELD_VALUES. */ private static final int MINUTE = 4; /** * Second index for MIN_ and MAX_FIELD_VALUES. */ private static final int SECOND = 5; /** * Second index for MIN_ and MAX_FIELD_VALUES. */ private static final int MILLISECOND = 6; /** * Timezone index for MIN_ and MAX_FIELD_VALUES */ private static final int TIMEZONE = 7; /** * field names indexed by YEAR..TIMEZONE. */ private static final String FIELD_NAME[] = { "Year", "Month", "Day", "Hour", "Minute", "Second", "Millisecond", "Timezone" }; /** *

Stream Unique Identifier.

* *

TODO: Serialization should use the XML string representation as * the serialization format to ensure future compatibility.

*/ private static final long serialVersionUID = 1L; /** *

Use as a template for default field values when * converting to a {@link GregorianCalendar}, set to a leap * year date of January 1, 0400 at midnight.

* *

Fields that are optional for an xsd:dateTime instances are defaulted to not being set to any value. * XMLGregorianCalendar fields millisecond, fractional second and timezone return the value indicating * that the field is not set, {@link DatatypeConstants#FIELD_UNDEFINED} for millisecond and timezone * and null for fractional second.

* * @see #toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar) */ public static final XMLGregorianCalendar LEAP_YEAR_DEFAULT = createDateTime( 400, //year DatatypeConstants.JANUARY, //month 1, // day 0, // hour 0, // minute 0, // second DatatypeConstants.FIELD_UNDEFINED, // milliseconds DatatypeConstants.FIELD_UNDEFINED // timezone ); // Constructors /** * Constructs a new XMLGregorianCalendar object. * * String parsing documented by {@link #parse(String)}. * * Returns a non-null valid XMLGregorianCalendar object that holds the * value indicated by the lexicalRepresentation parameter. * * @param lexicalRepresentation * Lexical representation of one the eight * XML Schema date/time datatypes. * @throws IllegalArgumentException * If the given string does not conform as documented in * {@link #parse(String)}. * @throws NullPointerException * If the given string is null. */ protected XMLGregorianCalendarImpl(String lexicalRepresentation) throws IllegalArgumentException { // compute format string for this lexical representation. String format; String lexRep = lexicalRepresentation; final int NOT_FOUND = -1; int lexRepLength = lexRep.length(); // current parser needs a format string, // use following heuristics to figure out what xml schema date/time // datatype this lexical string could represent. // Fix 4971612: invalid SCCS macro substitution in data string, // no %{alpha}% to avoid SCCS maco substitution if (lexRep.indexOf('T') != NOT_FOUND) { // found Date Time separater, must be xsd:DateTime format = "%Y-%M-%DT%h:%m:%s" + "%z"; } else if (lexRepLength >= 3 && lexRep.charAt(2) == ':') { // found ":", must be xsd:Time format = "%h:%m:%s" + "%z"; } else if (lexRep.startsWith("--")) { // check for gDay || gMonth || gMonthDay if (lexRepLength >= 3 && lexRep.charAt(2) == '-') { // gDay, ---DD(z?) format = "---%D" + "%z"; } else if (lexRepLength == 4 // --MM || lexRepLength == 5 // --MMZ || lexRepLength == 10) { // --MMSHH:MM // gMonth, --MM(z?), // per XML Schema Errata, used to be --MM--(z?) format = "--%M" + "%z"; } else { // gMonthDay, --MM-DD(z?), (or invalid lexicalRepresentation) // length should be: // 7: --MM-DD // 8: --MM-DDZ // 13: --MM-DDSHH:MM format = "--%M-%D" + "%z"; } } else { // check for Date || GYear | GYearMonth int countSeparator = 0; // start at index 1 to skip potential negative sign for year. int timezoneOffset = lexRep.indexOf(':'); if (timezoneOffset != NOT_FOUND) { // found timezone, strip it off for distinguishing // between Date, GYear and GYearMonth so possible // negative sign in timezone is not mistaken as // a separator. lexRepLength -= 6; } for (int i = 1; i < lexRepLength; i++) { if (lexRep.charAt(i) == '-') { countSeparator++; } } if (countSeparator == 0) { // GYear format = "%Y" + "%z"; } else if (countSeparator == 1) { // GYearMonth format = "%Y-%M" + "%z"; } else { // Date or invalid lexicalRepresentation // Fix 4971612: invalid SCCS macro substitution in data string format = "%Y-%M-%D" + "%z"; } } Parser p = new Parser(format, lexRep); p.parse(); // check for validity if (!isValid()) { throw new IllegalArgumentException( DatatypeMessageFormatter.formatMessage(null, "InvalidXGCRepresentation", new Object[]{lexicalRepresentation}) //"\"" + lexicalRepresentation + "\" is not a valid representation of an XML Gregorian Calendar value." ); } save(); } /** * save original values */ private void save() { orig_eon = eon; orig_year = year; orig_month = month; orig_day = day; orig_hour = hour; orig_minute = minute; orig_second = second; orig_fracSeconds = fractionalSecond; orig_timezone = timezone; } /** *

Create an instance with all date/time datatype fields set to * {@link DatatypeConstants#FIELD_UNDEFINED} or null respectively.

*/ public XMLGregorianCalendarImpl() { // field initializers already do the correct initialization. } /** *

Private constructor allowing for complete value spaces allowed by * W3C XML Schema 1.0 recommendation for xsd:dateTime and related * builtin datatypes. Note that year parameter supports * arbitrarily large numbers and fractionalSecond has infinite * precision.

* * @param year of XMLGregorianCalendar to be created. * @param month of XMLGregorianCalendar to be created. * @param day of XMLGregorianCalendar to be created. * @param hour of XMLGregorianCalendar to be created. * @param minute of XMLGregorianCalendar to be created. * @param second of XMLGregorianCalendar to be created. * @param fractionalSecond of XMLGregorianCalendar to be created. * @param timezone of XMLGregorianCalendar to be created. * */ protected XMLGregorianCalendarImpl( BigInteger year, int month, int day, int hour, int minute, int second, BigDecimal fractionalSecond, int timezone) { setYear(year); setMonth(month); setDay(day); setTime(hour, minute, second, fractionalSecond); setTimezone(timezone); // check for validity if (!isValid()) { throw new IllegalArgumentException( DatatypeMessageFormatter.formatMessage(null, "InvalidXGCValue-fractional", new Object[] { year, month, day, hour, minute, second, fractionalSecond, timezone}) ); } save(); } /** *

Private constructor of value spaces that a * java.util.GregorianCalendar instance would need to convert to an * XMLGregorianCalendar instance.

* *

XMLGregorianCalendar eon and * fractionalSecond are set to null

* * @param year of XMLGregorianCalendar to be created. * @param month of XMLGregorianCalendar to be created. * @param day of XMLGregorianCalendar to be created. * @param hour of XMLGregorianCalendar to be created. * @param minute of XMLGregorianCalendar to be created. * @param second of XMLGregorianCalendar to be created. * @param millisecond of XMLGregorianCalendar to be created. * @param timezone of XMLGregorianCalendar to be created. */ private XMLGregorianCalendarImpl( int year, int month, int day, int hour, int minute, int second, int millisecond, int timezone) { setYear(year); setMonth(month); setDay(day); setTime(hour, minute, second); setTimezone(timezone); BigDecimal realMilliseconds = null; if (millisecond != DatatypeConstants.FIELD_UNDEFINED) { realMilliseconds = BigDecimal.valueOf(millisecond, 3); } setFractionalSecond(realMilliseconds); if (!isValid()) { throw new IllegalArgumentException( DatatypeMessageFormatter.formatMessage(null, "InvalidXGCValue-milli", new Object[] { year, month, day, hour, minute, second, millisecond, timezone}) ); } save(); } /** *

Convert a java.util.GregorianCalendar to XML Schema 1.0 * representation.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Field by Field Conversion from * java.util.GregorianCalendar to this class *
javax.xml.datatype.XMLGregorianCalendar fieldjava.util.GregorianCalendar field
{@link #setYear(int)}ERA == GregorianCalendar.BC ? -YEAR : YEAR
{@link #setMonth(int)}MONTH + 1
{@link #setDay(int)}DAY_OF_MONTH
{@link #setTime(int,int,int, BigDecimal)}HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND
{@link #setTimezone(int)}*(ZONE_OFFSET + DST_OFFSET) / (60*1000)
* (in minutes) *
*

*conversion loss of information. It is not possible to represent * a java.util.GregorianCalendar daylight savings timezone id in the * XML Schema 1.0 date/time datatype representation.

* *

To compute the return value's TimeZone field, *