/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.ObjectStreamException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
Immutable representation of a time span as defined in
the W3C XML Schema 1.0 specification.
A Duration object represents a period of Gregorian time,
which consists of six fields (years, months, days, hours,
minutes, and seconds) plus a sign (+/-) field.
The first five fields have non-negative (>=0) integers or null
(which represents that the field is not set),
and the seconds field has a non-negative decimal or null.
A negative sign indicates a negative duration.
This class provides a number of methods that make it easy
to use for the duration datatype of XML Schema 1.0 with
the errata.
Order relationship
Duration objects only have partial order, where two values A and B
maybe either:
- A<B (A is shorter than B)
- A>B (A is longer than B)
- A==B (A and B are of the same duration)
- A<>B (Comparison between A and B is indeterminate)
For example, 30 days cannot be meaningfully compared to one month. The compare(Duration)
method implements this relationship.
See the Duration.isLongerThan(Duration)
method for details about the order relationship among Duration
objects.
Operations over Duration
This class provides a set of basic arithmetic operations, such
as addition, subtraction and multiplication.
Because durations don't have total order, an operation could
fail for some combinations of operations. For example, you cannot
subtract 15 days from 1 month. See the javadoc of those methods
for detailed conditions where this could happen.
Also, division of a duration by a number is not provided because the Duration
class can only deal with finite precision decimal numbers. For example, one cannot represent 1 sec divided by 3.
However, you could substitute a division by 3 with multiplying
by numbers such as 0.3 or 0.333.
Range of allowed values
Because some operations of Duration
rely on Calendar
even though Duration
can hold very large or very small values, some of the methods may not work correctly on such Duration
s. The impacted methods document their dependency on Calendar
.
Author: Kohsuke Kawaguchi, Joseph Fialli See Also:
/**
* <p>Immutable representation of a time span as defined in
* the W3C XML Schema 1.0 specification.</p>
*
* <p>A Duration object represents a period of Gregorian time,
* which consists of six fields (years, months, days, hours,
* minutes, and seconds) plus a sign (+/-) field.</p>
*
* <p>The first five fields have non-negative (>=0) integers or null
* (which represents that the field is not set),
* and the seconds field has a non-negative decimal or null.
* A negative sign indicates a negative duration.</p>
*
* <p>This class provides a number of methods that make it easy
* to use for the duration datatype of XML Schema 1.0 with
* the errata.</p>
*
* <h2>Order relationship</h2>
* <p>Duration objects only have partial order, where two values A and B
* maybe either:</p>
* <ol>
* <li>A<B (A is shorter than B)
* <li>A>B (A is longer than B)
* <li>A==B (A and B are of the same duration)
* <li>A<>B (Comparison between A and B is indeterminate)
* </ol>
* <p>For example, 30 days cannot be meaningfully compared to one month.
* The {@link #compare(Duration)} method implements this
* relationship.</p>
*
* <p>See the {@link #isLongerThan(Duration)} method for details about
* the order relationship among {@link Duration} objects.</p>
*
*
*
* <h2>Operations over Duration</h2>
* <p>This class provides a set of basic arithmetic operations, such
* as addition, subtraction and multiplication.
* Because durations don't have total order, an operation could
* fail for some combinations of operations. For example, you cannot
* subtract 15 days from 1 month. See the javadoc of those methods
* for detailed conditions where this could happen.</p>
*
* <p>Also, division of a duration by a number is not provided because
* the {@link Duration} class can only deal with finite precision
* decimal numbers. For example, one cannot represent 1 sec divided by 3.</p>
*
* <p>However, you could substitute a division by 3 with multiplying
* by numbers such as 0.3 or 0.333.</p>
*
*
*
* <h2>Range of allowed values</h2>
* <p>
* Because some operations of {@link Duration} rely on {@link Calendar}
* even though {@link Duration} can hold very large or very small values,
* some of the methods may not work correctly on such {@link Duration}s.
* The impacted methods document their dependency on {@link Calendar}.
*
*
* @author <a href="mailto:Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a>
* @author <a href="mailto:Joseph.Fialli@Sun.com">Joseph Fialli</a>
* @see XMLGregorianCalendar#add(Duration)
*/
class DurationImpl
extends Duration
implements Serializable {
Internal array of value Fields.
/**
* <p>Internal array of value Fields.</p>
*/
private static final DatatypeConstants.Field[] FIELDS = new DatatypeConstants.Field[]{
DatatypeConstants.YEARS,
DatatypeConstants.MONTHS,
DatatypeConstants.DAYS,
DatatypeConstants.HOURS,
DatatypeConstants.MINUTES,
DatatypeConstants.SECONDS
};
TimeZone for GMT.
/**
* TimeZone for GMT.
*/
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
BigDecimal value of 0.
/**
* <p>BigDecimal value of 0.</p>
*/
private static final BigDecimal ZERO = BigDecimal.valueOf(0);
BigInteger value of Integer's max value.
/**
* BigInteger value of Integer's max value.</p>
*/
private static final BigInteger MaxIntAsBigInt =
BigInteger.valueOf((long) Integer.MAX_VALUE);
Indicates the sign. -1, 0 or 1 if the duration is negative,
zero, or positive.
/**
* <p>Indicates the sign. -1, 0 or 1 if the duration is negative,
* zero, or positive.</p>
*/
protected int signum;
/**
* <p>Years of this <code>Duration</code>.</p>
*/
These were final since Duration is immutable. But new subclasses need
to be able to set after conversion. It won't break the immutable nature
of them since there's no other way to set new values to them
/**
* These were final since Duration is immutable. But new subclasses need
* to be able to set after conversion. It won't break the immutable nature
* of them since there's no other way to set new values to them
*/
protected BigInteger years;
Months of this Duration
.
/**
* <p>Months of this <code>Duration</code>.</p>
*/
protected BigInteger months;
Days of this Duration
.
/**
* <p>Days of this <code>Duration</code>.</p>
*/
protected BigInteger days;
Hours of this Duration
.
/**
* <p>Hours of this <code>Duration</code>.</p>
*/
protected BigInteger hours;
Minutes of this Duration
.
/**
* <p>Minutes of this <code>Duration</code>.</p>
*/
protected BigInteger minutes;
Seconds of this Duration
.
/**
* <p>Seconds of this <code>Duration</code>.</p>
*/
protected BigDecimal seconds;
Returns the sign of this duration in -1,0, or 1.
Returns:
-1 if this duration is negative, 0 if the duration is zero,
and 1 if the duration is postive.
/**
* Returns the sign of this duration in -1,0, or 1.
*
* @return
* -1 if this duration is negative, 0 if the duration is zero,
* and 1 if the duration is postive.
*/
public int getSign() {
return signum;
}
Determine the sign of the duration.
Params: - isPositive – Sign.
Returns: 1 if positive, -1 negative, or 0 if all fields are zero.
/**
* Determine the sign of the duration.
*
* @param isPositive Sign.
* @return 1 if positive, -1 negative, or 0 if all fields are zero.
*/
protected int calcSignum(boolean isPositive) {
if ((years == null || years.signum() == 0)
&& (months == null || months.signum() == 0)
&& (days == null || days.signum() == 0)
&& (hours == null || hours.signum() == 0)
&& (minutes == null || minutes.signum() == 0)
&& (seconds == null || seconds.signum() == 0)) {
return 0;
}
if (isPositive) {
return 1;
}
else {
return -1;
}
}
Constructs a new Duration object by specifying each field individually.
All the parameters are optional as long as at least one field is present.
If specified, parameters have to be zero or positive.
Params: - isPositive – Set to
false
to create a negative duration. When the length
of the duration is zero, this parameter will be ignored. - years – of this
Duration
- months – of this
Duration
- days – of this
Duration
- hours – of this
Duration
- minutes – of this
Duration
- seconds – of this
Duration
Throws: - IllegalArgumentException –
If years, months, days, hours, minutes and
seconds parameters are all
null
. Or if any
of those parameters are negative.
/**
* <p>Constructs a new Duration object by specifying each field individually.</p>
*
* <p>All the parameters are optional as long as at least one field is present.
* If specified, parameters have to be zero or positive.</p>
*
* @param isPositive Set to <code>false</code> to create a negative duration. When the length
* of the duration is zero, this parameter will be ignored.
* @param years of this <code>Duration</code>
* @param months of this <code>Duration</code>
* @param days of this <code>Duration</code>
* @param hours of this <code>Duration</code>
* @param minutes of this <code>Duration</code>
* @param seconds of this <code>Duration</code>
*
* @throws IllegalArgumentException
* If years, months, days, hours, minutes and
* seconds parameters are all <code>null</code>. Or if any
* of those parameters are negative.
*/
protected DurationImpl(
boolean isPositive,
BigInteger years,
BigInteger months,
BigInteger days,
BigInteger hours,
BigInteger minutes,
BigDecimal seconds) {
this.years = years;
this.months = months;
this.days = days;
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
this.signum = calcSignum(isPositive);
// sanity check
if (years == null
&& months == null
&& days == null
&& hours == null
&& minutes == null
&& seconds == null) {
throw new IllegalArgumentException(
//"all the fields are null"
DatatypeMessageFormatter.formatMessage(null, "AllFieldsNull", null)
);
}
testNonNegative(years, DatatypeConstants.YEARS);
testNonNegative(months, DatatypeConstants.MONTHS);
testNonNegative(days, DatatypeConstants.DAYS);
testNonNegative(hours, DatatypeConstants.HOURS);
testNonNegative(minutes, DatatypeConstants.MINUTES);
testNonNegative(seconds, DatatypeConstants.SECONDS);
}
Makes sure that the given number is non-negative. If it is not, throw IllegalArgumentException
.
Params: - n – Number to test.
- f – Field to test.
/**
* <p>Makes sure that the given number is non-negative. If it is not,
* throw {@link IllegalArgumentException}.</p>
*
* @param n Number to test.
* @param f Field to test.
*/
protected static void testNonNegative(BigInteger n, DatatypeConstants.Field f) {
if (n != null && n.signum() < 0) {
throw new IllegalArgumentException(
DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object[]{f.toString()})
);
}
}
Makes sure that the given number is non-negative. If it is not, throw IllegalArgumentException
.
Params: - n – Number to test.
- f – Field to test.
/**
* <p>Makes sure that the given number is non-negative. If it is not,
* throw {@link IllegalArgumentException}.</p>
*
* @param n Number to test.
* @param f Field to test.
*/
protected static void testNonNegative(BigDecimal n, DatatypeConstants.Field f) {
if (n != null && n.signum() < 0) {
throw new IllegalArgumentException(
DatatypeMessageFormatter.formatMessage(null, "NegativeField", new Object[]{f.toString()})
);
}
}
Constructs a new Duration object by specifying each field
individually.
This method is functionally equivalent to invoking another constructor by wrapping all non-zero parameters into BigInteger
and BigDecimal
. Zero value of int parameter is equivalent of null value of the corresponding field.
See Also:
/**
* <p>Constructs a new Duration object by specifying each field
* individually.</p>
*
* <p>This method is functionally equivalent to
* invoking another constructor by wrapping
* all non-zero parameters into {@link BigInteger} and {@link BigDecimal}.
* Zero value of int parameter is equivalent of null value of
* the corresponding field.</p>
*
* @see #DurationImpl(boolean, BigInteger, BigInteger, BigInteger, BigInteger,
* BigInteger, BigDecimal)
*/
protected DurationImpl(
final boolean isPositive,
final int years,
final int months,
final int days,
final int hours,
final int minutes,
final int seconds) {
this(
isPositive,
wrap(years),
wrap(months),
wrap(days),
wrap(hours),
wrap(minutes),
seconds != DatatypeConstants.FIELD_UNDEFINED ? new BigDecimal(String.valueOf(seconds)) : null);
}
TODO: Javadoc
Params: - i – int to convert to BigInteger.
Returns: BigInteger representation of int.
/**
* TODO: Javadoc
*
* @param i int to convert to BigInteger.
*
* @return BigInteger representation of int.
*/
protected static BigInteger wrap(final int i) {
// field may not be set
if (i == DatatypeConstants.FIELD_UNDEFINED) {
return null;
}
// int -> BigInteger
return BigInteger.valueOf(i);
}
Constructs a new Duration object by specifying the duration
in milliseconds.
Params: - durationInMilliSeconds –
The length of the duration in milliseconds.
/**
* <p>Constructs a new Duration object by specifying the duration
* in milliseconds.</p>
*
* @param durationInMilliSeconds
* The length of the duration in milliseconds.
*/
protected DurationImpl(final long durationInMilliSeconds) {
boolean is0x8000000000000000L = false;
long l = durationInMilliSeconds;
if (l > 0) {
signum = 1;
}
else if (l < 0) {
signum = -1;
if (l == 0x8000000000000000L) {
// negating 0x8000000000000000L causes an overflow
l++;
is0x8000000000000000L = true;
}
l *= -1;
}
else {
signum = 0;
}
// let GregorianCalendar do the heavy lifting
GregorianCalendar gregorianCalendar = new GregorianCalendar(GMT);
// duration is the offset from the Epoch
gregorianCalendar.setTimeInMillis(l);
// now find out how much each field has changed
long int2long = 0L;
// years
int2long = gregorianCalendar.get(Calendar.YEAR) - 1970;
this.years = BigInteger.valueOf(int2long);
// months
int2long = gregorianCalendar.get(Calendar.MONTH);
this.months = BigInteger.valueOf(int2long);
// days
int2long = gregorianCalendar.get(Calendar.DAY_OF_MONTH) - 1;
this.days = BigInteger.valueOf(int2long);
// hours
int2long = gregorianCalendar.get(Calendar.HOUR_OF_DAY);
this.hours = BigInteger.valueOf(int2long);
// minutes
int2long = gregorianCalendar.get(Calendar.MINUTE);
this.minutes = BigInteger.valueOf(int2long);
// seconds & milliseconds
int2long = (gregorianCalendar.get(Calendar.SECOND) * 1000)
+ gregorianCalendar.get(Calendar.MILLISECOND)
+ (is0x8000000000000000L ? 1 : 0);
this.seconds = BigDecimal.valueOf(int2long, 3);
}
Constructs a new Duration object by
parsing its string representation
"PnYnMnDTnHnMnS" as defined in XML Schema 1.0 section 3.2.6.1.
The string representation may not have any leading
and trailing whitespaces.
For example, this method parses strings like
"P1D" (1 day), "-PT100S" (-100 sec.), "P1DT12H" (1 days and 12 hours).
The parsing is done field by field so that
the following holds for any lexically correct string x:
new Duration(x).toString().equals(x)
Returns a non-null valid duration object that holds the value
indicated by the lexicalRepresentation parameter.
Params: - lexicalRepresentation –
Lexical representation of a duration.
Throws: - IllegalArgumentException –
If the given string does not conform to the aforementioned
specification.
- NullPointerException –
If the given string is null.
/**
* Constructs a new Duration object by
* parsing its string representation
* "PnYnMnDTnHnMnS" as defined in XML Schema 1.0 section 3.2.6.1.
*
* <p>
* The string representation may not have any leading
* and trailing whitespaces.
*
* <p>
* For example, this method parses strings like
* "P1D" (1 day), "-PT100S" (-100 sec.), "P1DT12H" (1 days and 12 hours).
*
* <p>
* The parsing is done field by field so that
* the following holds for any lexically correct string x:
* <pre>
* new Duration(x).toString().equals(x)
* </pre>
*
* Returns a non-null valid duration object that holds the value
* indicated by the lexicalRepresentation parameter.
*
* @param lexicalRepresentation
* Lexical representation of a duration.
* @throws IllegalArgumentException
* If the given string does not conform to the aforementioned
* specification.
* @throws NullPointerException
* If the given string is null.
*/
protected DurationImpl(String lexicalRepresentation)
throws IllegalArgumentException {
// only if I could use the JDK1.4 regular expression ....
if (lexicalRepresentation == null) {
throw new NullPointerException();
}
final String s = lexicalRepresentation;
boolean positive;
int[] idx = new int[1];
int length = s.length();
boolean timeRequired = false;
idx[0] = 0;
if (length != idx[0] && s.charAt(idx[0]) == '-') {
idx[0]++;
positive = false;
}
else {
positive = true;
}
if (length != idx[0] && s.charAt(idx[0]++) != 'P') {
throw new IllegalArgumentException(s); //,idx[0]-1);
}
// phase 1: chop the string into chunks
// (where a chunk is '<number><a symbol>'
//--------------------------------------
int dateLen = 0;
String[] dateParts = new String[3];
int[] datePartsIndex = new int[3];
while (length != idx[0]
&& isDigit(s.charAt(idx[0]))
&& dateLen < 3) {
datePartsIndex[dateLen] = idx[0];
dateParts[dateLen++] = parsePiece(s, idx);
}
if (length != idx[0]) {
if (s.charAt(idx[0]++) == 'T') {
timeRequired = true;
}
else {
throw new IllegalArgumentException(s); // ,idx[0]-1);
}
}
int timeLen = 0;
String[] timeParts = new String[3];
int[] timePartsIndex = new int[3];
while (length != idx[0]
&& isDigitOrPeriod(s.charAt(idx[0]))
&& timeLen < 3) {
timePartsIndex[timeLen] = idx[0];
timeParts[timeLen++] = parsePiece(s, idx);
}
if (timeRequired && timeLen == 0) {
throw new IllegalArgumentException(s); // ,idx[0]);
}
if (length != idx[0]) {
throw new IllegalArgumentException(s); // ,idx[0]);
}
if (dateLen == 0 && timeLen == 0) {
throw new IllegalArgumentException(s); // ,idx[0]);
}
// phase 2: check the ordering of chunks
//--------------------------------------
organizeParts(s, dateParts, datePartsIndex, dateLen, "YMD");
organizeParts(s, timeParts, timePartsIndex, timeLen, "HMS");
// parse into numbers
years = parseBigInteger(s, dateParts[0], datePartsIndex[0]);
months = parseBigInteger(s, dateParts[1], datePartsIndex[1]);
days = parseBigInteger(s, dateParts[2], datePartsIndex[2]);
hours = parseBigInteger(s, timeParts[0], timePartsIndex[0]);
minutes = parseBigInteger(s, timeParts[1], timePartsIndex[1]);
seconds = parseBigDecimal(s, timeParts[2], timePartsIndex[2]);
signum = calcSignum(positive);
}
TODO: Javadoc
Params: - ch – char to test.
Returns: true if ch is a digit, else false.
/**
* TODO: Javadoc
*
* @param ch char to test.
*
* @return true if ch is a digit, else false.
*/
private static boolean isDigit(char ch) {
return '0' <= ch && ch <= '9';
}
TODO: Javadoc
Params: - ch – to test.
Returns: true if ch is a digit or a period, else false.
/**
* TODO: Javadoc
*
* @param ch to test.
*
* @return true if ch is a digit or a period, else false.
*/
private static boolean isDigitOrPeriod(char ch) {
return isDigit(ch) || ch == '.';
}
TODO: Javadoc
Params: - whole – String to parse.
- idx – TODO: ???
Throws: - IllegalArgumentException – If whole cannot be parsed.
Returns: Result of parsing.
/**
* TODO: Javadoc
*
* @param whole String to parse.
* @param idx TODO: ???
*
* @return Result of parsing.
*
* @throws IllegalArgumentException If whole cannot be parsed.
*/
private static String parsePiece(String whole, int[] idx)
throws IllegalArgumentException {
int start = idx[0];
while (idx[0] < whole.length()
&& isDigitOrPeriod(whole.charAt(idx[0]))) {
idx[0]++;
}
if (idx[0] == whole.length()) {
throw new IllegalArgumentException(whole); // ,idx[0]);
}
idx[0]++;
return whole.substring(start, idx[0]);
}
TODO: Javadoc.
Params: - whole – TODO: ???
- parts – TODO: ???
- partsIndex – TODO: ???
- len – TODO: ???
- tokens – TODO: ???
Throws: - IllegalArgumentException – TODO: ???
/**
* TODO: Javadoc.
*
* @param whole TODO: ???
* @param parts TODO: ???
* @param partsIndex TODO: ???
* @param len TODO: ???
* @param tokens TODO: ???
*
* @throws IllegalArgumentException TODO: ???
*/
private static void organizeParts(
String whole,
String[] parts,
int[] partsIndex,
int len,
String tokens)
throws IllegalArgumentException {
int idx = tokens.length();
for (int i = len - 1; i >= 0; i--) {
if (parts[i] == null) {
throw new IllegalArgumentException(whole);
}
int nidx =
tokens.lastIndexOf(
parts[i].charAt(parts[i].length() - 1),
idx - 1);
if (nidx == -1) {
throw new IllegalArgumentException(whole);
// ,partsIndex[i]+parts[i].length()-1);
}
for (int j = nidx + 1; j < idx; j++) {
parts[j] = null;
}
idx = nidx;
parts[idx] = parts[i];
partsIndex[idx] = partsIndex[i];
}
for (idx--; idx >= 0; idx--) {
parts[idx] = null;
}
}
TODO: Javadoc
Params: - whole – TODO: ???.
- part – TODO: ???.
- index – TODO: ???.
Throws: - IllegalArgumentException – TODO: ???.
Returns: TODO: ???.
/**
* TODO: Javadoc
*
* @param whole TODO: ???.
* @param part TODO: ???.
* @param index TODO: ???.
*
* @return TODO: ???.
*
* @throws IllegalArgumentException TODO: ???.
*/
private static BigInteger parseBigInteger(
String whole,
String part,
int index)
throws IllegalArgumentException {
if (part == null) {
return null;
}
part = part.substring(0, part.length() - 1);
// try {
return new BigInteger(part);
// } catch( NumberFormatException e ) {
// throw new ParseException( whole, index );
// }
}
TODO: Javadoc.
Params: - whole – TODO: ???.
- part – TODO: ???.
- index – TODO: ???.
Throws: - IllegalArgumentException – TODO: ???.
Returns: TODO: ???.
/**
* TODO: Javadoc.
*
* @param whole TODO: ???.
* @param part TODO: ???.
* @param index TODO: ???.
*
* @return TODO: ???.
*
* @throws IllegalArgumentException TODO: ???.
*/
private static BigDecimal parseBigDecimal(
String whole,
String part,
int index)
throws IllegalArgumentException {
if (part == null) {
return null;
}
part = part.substring(0, part.length() - 1);
// NumberFormatException is IllegalArgumentException
// try {
return new BigDecimal(part);
// } catch( NumberFormatException e ) {
// throw new ParseException( whole, index );
// }
}
Four constants defined for the comparison of durations.
/**
* <p>Four constants defined for the comparison of durations.</p>
*/
private static final XMLGregorianCalendar[] TEST_POINTS = new XMLGregorianCalendar[] {
XMLGregorianCalendarImpl.parse("1696-09-01T00:00:00Z"),
XMLGregorianCalendarImpl.parse("1697-02-01T00:00:00Z"),
XMLGregorianCalendarImpl.parse("1903-03-01T00:00:00Z"),
XMLGregorianCalendarImpl.parse("1903-07-01T00:00:00Z")
};
Partial order relation comparison with this Duration
instance.
Comparison result must be in accordance with
W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2,
Order relation on duration.
Return:
DatatypeConstants.LESSER
if this Duration
is shorter than duration
parameter
DatatypeConstants.EQUAL
if this Duration
is equal to duration
parameter
DatatypeConstants.GREATER
if this Duration
is longer than duration
parameter
DatatypeConstants.INDETERMINATE
if a conclusive partial order relation cannot be determined
Params: - duration – to compare
Throws: - UnsupportedOperationException – If the underlying implementation
cannot reasonably process the request, e.g. W3C XML Schema allows for
arbitrarily large/small/precise values, the request may be beyond the
implementations capability.
- NullPointerException – if
duration
is null
.
See Also: Returns: the relationship between this
Duration
and duration
parameter as DatatypeConstants.LESSER
, DatatypeConstants.EQUAL
, DatatypeConstants.GREATER
or DatatypeConstants.INDETERMINATE
.
/**
* <p>Partial order relation comparison with this <code>Duration</code> instance.</p>
*
* <p>Comparison result must be in accordance with
* <a href="http://www.w3.org/TR/xmlschema-2/#duration-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2,
* <i>Order relation on duration</i></a>.</p>
*
* <p>Return:</p>
* <ul>
* <li>{@link DatatypeConstants#LESSER} if this <code>Duration</code> is shorter than <code>duration</code> parameter</li>
* <li>{@link DatatypeConstants#EQUAL} if this <code>Duration</code> is equal to <code>duration</code> parameter</li>
* <li>{@link DatatypeConstants#GREATER} if this <code>Duration</code> is longer than <code>duration</code> parameter</li>
* <li>{@link DatatypeConstants#INDETERMINATE} if a conclusive partial order relation cannot be determined</li>
* </ul>
*
* @param duration to compare
*
* @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
* {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER}
* or {@link DatatypeConstants#INDETERMINATE}.
*
* @throws UnsupportedOperationException If the underlying implementation
* cannot reasonably process the request, e.g. W3C XML Schema allows for
* arbitrarily large/small/precise values, the request may be beyond the
* implementations capability.
* @throws NullPointerException if <code>duration</code> is <code>null</code>.
*
* @see #isShorterThan(Duration)
* @see #isLongerThan(Duration)
*/
public int compare(Duration rhs) {
/** check if any field in the Durations is too large for the operation
* that uses XMLGregorianCalendar for comparison
*/
for (DatatypeConstants.Field field : FIELDS) {
checkMaxValue(getField(field), field);
checkMaxValue(rhs.getField(field), field);
}
return compareDates(this, rhs);
}
Check if a field exceeds the maximum value
Params: - field – the value of a field
- fieldType – type of the field, e.g. year, month, day, hour, minute or second.
/**
* Check if a field exceeds the maximum value
* @param field the value of a field
* @param fieldType type of the field, e.g. year, month, day, hour, minute or second.
*/
private void checkMaxValue(Number field, DatatypeConstants.Field fieldType) {
BigInteger fieldValue = null;
if (fieldType != DatatypeConstants.SECONDS) {
fieldValue = (BigInteger) field;
} else {
BigDecimal rhsSecondsAsBigDecimal = (BigDecimal) field;
if ( rhsSecondsAsBigDecimal != null ) {
fieldValue = rhsSecondsAsBigDecimal.toBigInteger();
}
}
if (fieldValue != null && fieldValue.compareTo(MaxIntAsBigInt) == 1) {
throw new UnsupportedOperationException(
DatatypeMessageFormatter.formatMessage(null, "TooLarge",
new Object[]{this.getClass().getName() + "#compare(Duration duration)"
+ fieldType, field.toString()})
);
}
}
Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
Params: - duration1 – Unnormalized duration
- duration2 – Unnormalized duration
Returns: INDETERMINATE if the order relationship between date1 and date2 is indeterminate.
EQUAL if the order relation between date1 and date2 is EQUAL.
If the strict parameter is true, return LESS_THAN if date1 is less than date2 and
return GREATER_THAN if date1 is greater than date2.
If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and
return GREATER_THAN if date1 is greater than OR equal to date2
/**
* Compares 2 given durations. (refer to W3C Schema Datatypes "3.2.6 duration")
*
* @param duration1 Unnormalized duration
* @param duration2 Unnormalized duration
* @return INDETERMINATE if the order relationship between date1 and date2 is indeterminate.
* EQUAL if the order relation between date1 and date2 is EQUAL.
* If the strict parameter is true, return LESS_THAN if date1 is less than date2 and
* return GREATER_THAN if date1 is greater than date2.
* If the strict parameter is false, return LESS_THAN if date1 is less than OR equal to date2 and
* return GREATER_THAN if date1 is greater than OR equal to date2
*/
private int compareDates(Duration duration1, Duration duration2) {
int resultA = DatatypeConstants.INDETERMINATE;
int resultB = DatatypeConstants.INDETERMINATE;
XMLGregorianCalendar tempA = (XMLGregorianCalendar)TEST_POINTS[0].clone();
XMLGregorianCalendar tempB = (XMLGregorianCalendar)TEST_POINTS[0].clone();
//long comparison algorithm is required
tempA.add(duration1);
tempB.add(duration2);
resultA = tempA.compare(tempB);
if ( resultA == DatatypeConstants.INDETERMINATE ) {
return DatatypeConstants.INDETERMINATE;
}
tempA = (XMLGregorianCalendar)TEST_POINTS[1].clone();
tempB = (XMLGregorianCalendar)TEST_POINTS[1].clone();
tempA.add(duration1);
tempB.add(duration2);
resultB = tempA.compare(tempB);
resultA = compareResults(resultA, resultB);
if (resultA == DatatypeConstants.INDETERMINATE) {
return DatatypeConstants.INDETERMINATE;
}
tempA = (XMLGregorianCalendar)TEST_POINTS[2].clone();
tempB = (XMLGregorianCalendar)TEST_POINTS[2].clone();
tempA.add(duration1);
tempB.add(duration2);
resultB = tempA.compare(tempB);
resultA = compareResults(resultA, resultB);
if (resultA == DatatypeConstants.INDETERMINATE) {
return DatatypeConstants.INDETERMINATE;
}
tempA = (XMLGregorianCalendar)TEST_POINTS[3].clone();
tempB = (XMLGregorianCalendar)TEST_POINTS[3].clone();
tempA.add(duration1);
tempB.add(duration2);
resultB = tempA.compare(tempB);
resultA = compareResults(resultA, resultB);
return resultA;
}
private int compareResults(int resultA, int resultB) {
if ( resultB == DatatypeConstants.INDETERMINATE ) {
return DatatypeConstants.INDETERMINATE;
}
else if ( resultA!=resultB) {
return DatatypeConstants.INDETERMINATE;
}
return resultA;
}
Returns a hash code consistent with the definition of the equals method.
See Also: - hashCode.hashCode()
/**
* Returns a hash code consistent with the definition of the equals method.
*
* @see Object#hashCode()
*/
public int hashCode() {
// component wise hash is not correct because 1day = 24hours
Calendar cal = TEST_POINTS[0].toGregorianCalendar();
this.addTo(cal);
return (int) getCalendarTimeInMillis(cal);
}
Returns a string representation of this duration object.
The result is formatter according to the XML Schema 1.0 spec and can be always parsed back later into the equivalent duration object by the DurationImpl(String)
constructor.
Formally, the following holds for any Duration
object x.
new Duration(x.toString()).equals(x)
Returns:
Always return a non-null valid String object.
/**
* Returns a string representation of this duration object.
*
* <p>
* The result is formatter according to the XML Schema 1.0
* spec and can be always parsed back later into the
* equivalent duration object by
* the {@link #DurationImpl(String)} constructor.
*
* <p>
* Formally, the following holds for any {@link Duration}
* object x.
* <pre>
* new Duration(x.toString()).equals(x)
* </pre>
*
* @return
* Always return a non-null valid String object.
*/
public String toString() {
StringBuffer buf = new StringBuffer();
if (signum < 0) {
buf.append('-');
}
buf.append('P');
if (years != null) {
buf.append(years).append('Y');
}
if (months != null) {
buf.append(months).append('M');
}
if (days != null) {
buf.append(days).append('D');
}
if (hours != null || minutes != null || seconds != null) {
buf.append('T');
if (hours != null) {
buf.append(hours).append('H');
}
if (minutes != null) {
buf.append(minutes).append('M');
}
if (seconds != null) {
buf.append(toString(seconds)).append('S');
}
}
return buf.toString();
}
Turns BigDecimal
to a string representation.
Due to a behavior change in the BigDecimal.toString()
method in JDK1.5, this had to be implemented here.
Params: - bd –
BigDecimal
to format as a String
Returns: String
representation of BigDecimal
/**
* <p>Turns {@link BigDecimal} to a string representation.</p>
*
* <p>Due to a behavior change in the {@link BigDecimal#toString()}
* method in JDK1.5, this had to be implemented here.</p>
*
* @param bd <code>BigDecimal</code> to format as a <code>String</code>
*
* @return <code>String</code> representation of <code>BigDecimal</code>
*/
private String toString(BigDecimal bd) {
String intString = bd.unscaledValue().toString();
int scale = bd.scale();
if (scale == 0) {
return intString;
}
/* Insert decimal point */
StringBuffer buf;
int insertionPoint = intString.length() - scale;
if (insertionPoint == 0) { /* Point goes right before intVal */
return "0." + intString;
}
else if (insertionPoint > 0) { /* Point goes inside intVal */
buf = new StringBuffer(intString);
buf.insert(insertionPoint, '.');
}
else { /* We must insert zeros between point and intVal */
buf = new StringBuffer(3 - insertionPoint + intString.length());
buf.append("0.");
for (int i = 0; i < -insertionPoint; i++) {
buf.append('0');
}
buf.append(intString);
}
return buf.toString();
}
Checks if a field is set.
A field of a duration object may or may not be present.
This method can be used to test if a field is present.
Params: - field –
one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
MINUTES, or SECONDS.)
Throws: - NullPointerException –
If the field parameter is null.
Returns:
true if the field is present. false if not.
/**
* Checks if a field is set.
*
* A field of a duration object may or may not be present.
* This method can be used to test if a field is present.
*
* @param field
* one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
* MINUTES, or SECONDS.)
* @return
* true if the field is present. false if not.
*
* @throws NullPointerException
* If the field parameter is null.
*/
public boolean isSet(DatatypeConstants.Field field) {
if (field == null) {
String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)" ;
throw new NullPointerException(
//"cannot be called with field == null"
DatatypeMessageFormatter.formatMessage(null, "FieldCannotBeNull", new Object[]{methodName})
);
}
if (field == DatatypeConstants.YEARS) {
return years != null;
}
if (field == DatatypeConstants.MONTHS) {
return months != null;
}
if (field == DatatypeConstants.DAYS) {
return days != null;
}
if (field == DatatypeConstants.HOURS) {
return hours != null;
}
if (field == DatatypeConstants.MINUTES) {
return minutes != null;
}
if (field == DatatypeConstants.SECONDS) {
return seconds != null;
}
String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field)";
throw new IllegalArgumentException(
DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object[]{methodName, field.toString()})
);
}
Gets the value of a field. Fields of a duration object may contain arbitrary large value. Therefore this method is designed to return a Number
object. In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned number will be a non-negative integer. In case of seconds, the returned number may be a non-negative decimal value. Params: - field –
one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
MINUTES, or SECONDS.)
Throws: - NullPointerException –
If the field parameter is null.
Returns: If the specified field is present, this method returns a non-null non-negative Number
object that represents its value. If it is not present, return null. For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method returns a BigInteger
object. For SECONDS, this method returns a BigDecimal
.
/**
* Gets the value of a field.
*
* Fields of a duration object may contain arbitrary large value.
* Therefore this method is designed to return a {@link Number} object.
*
* In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned
* number will be a non-negative integer. In case of seconds,
* the returned number may be a non-negative decimal value.
*
* @param field
* one of the six Field constants (YEARS,MONTHS,DAYS,HOURS,
* MINUTES, or SECONDS.)
* @return
* If the specified field is present, this method returns
* a non-null non-negative {@link Number} object that
* represents its value. If it is not present, return null.
* For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method
* returns a {@link BigInteger} object. For SECONDS, this
* method returns a {@link BigDecimal}.
*
* @throws NullPointerException
* If the field parameter is null.
*/
public Number getField(DatatypeConstants.Field field) {
if (field == null) {
String methodName = "javax.xml.datatype.Duration" + "#isSet(DatatypeConstants.Field field) " ;
throw new NullPointerException(
DatatypeMessageFormatter.formatMessage(null,"FieldCannotBeNull", new Object[]{methodName})
);
}
if (field == DatatypeConstants.YEARS) {
return years;
}
if (field == DatatypeConstants.MONTHS) {
return months;
}
if (field == DatatypeConstants.DAYS) {
return days;
}
if (field == DatatypeConstants.HOURS) {
return hours;
}
if (field == DatatypeConstants.MINUTES) {
return minutes;
}
if (field == DatatypeConstants.SECONDS) {
return seconds;
}
/**
throw new IllegalArgumentException(
"javax.xml.datatype.Duration"
+ "#(getSet(DatatypeConstants.Field field) called with an unknown field: "
+ field.toString()
);
*/
String methodName = "javax.xml.datatype.Duration" + "#(getSet(DatatypeConstants.Field field)";
throw new IllegalArgumentException(
DatatypeMessageFormatter.formatMessage(null,"UnknownField", new Object[]{methodName, field.toString()})
);
}
Obtains the value of the YEARS field as an integer value,
or 0 if not present.
This method is a convenience method around the getField(Field)
method.
Note that since this method returns int, this method will return an incorrect value for Duration
s with the year field that goes beyond the range of int.
Use getField(YEARS)
to avoid possible loss of precision.
Returns: If the YEARS field is present, return its value as an integer by using the Number.intValue()
method. If the YEARS field is not present, return 0.
/**
* Obtains the value of the YEARS field as an integer value,
* or 0 if not present.
*
* <p>
* This method is a convenience method around the
* {@link #getField(DatatypeConstants.Field)} method.
*
* <p>
* Note that since this method returns <tt>int</tt>, this
* method will return an incorrect value for {@link Duration}s
* with the year field that goes beyond the range of <tt>int</tt>.
* Use <code>getField(YEARS)</code> to avoid possible loss of precision.</p>
*
* @return
* If the YEARS field is present, return
* its value as an integer by using the {@link Number#intValue()}
* method. If the YEARS field is not present, return 0.
*/
public int getYears() {
return getInt(DatatypeConstants.YEARS);
}
Obtains the value of the MONTHS field as an integer value, or 0 if not present. This method works just like getYears()
except that this method works on the MONTHS field. Returns: Months of this Duration
.
/**
* Obtains the value of the MONTHS field as an integer value,
* or 0 if not present.
*
* This method works just like {@link #getYears()} except
* that this method works on the MONTHS field.
*
* @return Months of this <code>Duration</code>.
*/
public int getMonths() {
return getInt(DatatypeConstants.MONTHS);
}
Obtains the value of the DAYS field as an integer value, or 0 if not present. This method works just like getYears()
except that this method works on the DAYS field. Returns: Days of this Duration
.
/**
* Obtains the value of the DAYS field as an integer value,
* or 0 if not present.
*
* This method works just like {@link #getYears()} except
* that this method works on the DAYS field.
*
* @return Days of this <code>Duration</code>.
*/
public int getDays() {
return getInt(DatatypeConstants.DAYS);
}
Obtains the value of the HOURS field as an integer value, or 0 if not present. This method works just like getYears()
except that this method works on the HOURS field. Returns: Hours of this Duration
.
/**
* Obtains the value of the HOURS field as an integer value,
* or 0 if not present.
*
* This method works just like {@link #getYears()} except
* that this method works on the HOURS field.
*
* @return Hours of this <code>Duration</code>.
*
*/
public int getHours() {
return getInt(DatatypeConstants.HOURS);
}
Obtains the value of the MINUTES field as an integer value, or 0 if not present. This method works just like getYears()
except that this method works on the MINUTES field. Returns: Minutes of this Duration
.
/**
* Obtains the value of the MINUTES field as an integer value,
* or 0 if not present.
*
* This method works just like {@link #getYears()} except
* that this method works on the MINUTES field.
*
* @return Minutes of this <code>Duration</code>.
*
*/
public int getMinutes() {
return getInt(DatatypeConstants.MINUTES);
}
Obtains the value of the SECONDS field as an integer value, or 0 if not present. This method works just like getYears()
except that this method works on the SECONDS field. Returns: seconds in the integer value. The fraction of seconds
will be discarded (for example, if the actual value is 2.5,
this method returns 2)
/**
* Obtains the value of the SECONDS field as an integer value,
* or 0 if not present.
*
* This method works just like {@link #getYears()} except
* that this method works on the SECONDS field.
*
* @return seconds in the integer value. The fraction of seconds
* will be discarded (for example, if the actual value is 2.5,
* this method returns 2)
*/
public int getSeconds() {
return getInt(DatatypeConstants.SECONDS);
}
Return the requested field value as an int.
If field is not set, i.e. == null, 0 is returned.
Params: - field – To get value for.
Returns: int value of field or 0 if field is not set.
/**
* <p>Return the requested field value as an int.</p>
*
* <p>If field is not set, i.e. == null, 0 is returned.</p>
*
* @param field To get value for.
*
* @return int value of field or 0 if field is not set.
*/
private int getInt(DatatypeConstants.Field field) {
Number n = getField(field);
if (n == null) {
return 0;
}
else {
return n.intValue();
}
}
Returns the length of the duration in milli-seconds.
If the seconds field carries more digits than milli-second order,
those will be simply discarded (or in other words, rounded to zero.)
For example, for any Calendar value x,
new Duration("PT10.00099S").getTimeInMills(x) == 10000
.
new Duration("-PT10.00099S").getTimeInMills(x) == -10000
.
Note that this method uses the addTo(Calendar)
method, which may work incorectly with Duration
objects with very large values in its fields. See the addTo(Calendar)
method for details.
Params: - startInstant –
The length of a month/year varies. The
startInstant
is
used to disambiguate this variance. Specifically, this method
returns the difference between startInstant
and
startInstant+duration
Throws: - NullPointerException – if
startInstant
parameter
is null.
Returns: milliseconds between startInstant
and
startInstant
plus this Duration
/**
* <p>Returns the length of the duration in milli-seconds.</p>
*
* <p>If the seconds field carries more digits than milli-second order,
* those will be simply discarded (or in other words, rounded to zero.)
* For example, for any Calendar value <code>x<code>,</p>
* <pre>
* <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
* <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
* </pre>
*
* <p>
* Note that this method uses the {@link #addTo(Calendar)} method,
* which may work incorectly with {@link Duration} objects with
* very large values in its fields. See the {@link #addTo(Calendar)}
* method for details.
*
* @param startInstant
* The length of a month/year varies. The <code>startInstant</code> is
* used to disambiguate this variance. Specifically, this method
* returns the difference between <code>startInstant</code> and
* <code>startInstant+duration</code>
*
* @return milliseconds between <code>startInstant</code> and
* <code>startInstant</code> plus this <code>Duration</code>
*
* @throws NullPointerException if <code>startInstant</code> parameter
* is null.
*
*/
public long getTimeInMillis(final Calendar startInstant) {
Calendar cal = (Calendar) startInstant.clone();
addTo(cal);
return getCalendarTimeInMillis(cal) - getCalendarTimeInMillis(startInstant);
}
Returns the length of the duration in milli-seconds.
If the seconds field carries more digits than milli-second order,
those will be simply discarded (or in other words, rounded to zero.)
For example, for any Date
value x,
new Duration("PT10.00099S").getTimeInMills(x) == 10000
.
new Duration("-PT10.00099S").getTimeInMills(x) == -10000
.
Note that this method uses the addTo(Date)
method, which may work incorectly with Duration
objects with very large values in its fields. See the addTo(Date)
method for details.
Params: - startInstant –
The length of a month/year varies. The
startInstant
is
used to disambiguate this variance. Specifically, this method
returns the difference between startInstant
and
startInstant+duration
.
Throws: - NullPointerException –
If the startInstant parameter is null.
See Also: Returns: milliseconds between startInstant
and
startInstant
plus this Duration
/**
* <p>Returns the length of the duration in milli-seconds.</p>
*
* <p>If the seconds field carries more digits than milli-second order,
* those will be simply discarded (or in other words, rounded to zero.)
* For example, for any <code>Date</code> value <code>x<code>,</p>
* <pre>
* <code>new Duration("PT10.00099S").getTimeInMills(x) == 10000</code>.
* <code>new Duration("-PT10.00099S").getTimeInMills(x) == -10000</code>.
* </pre>
*
* <p>
* Note that this method uses the {@link #addTo(Date)} method,
* which may work incorectly with {@link Duration} objects with
* very large values in its fields. See the {@link #addTo(Date)}
* method for details.
*
* @param startInstant
* The length of a month/year varies. The <code>startInstant</code> is
* used to disambiguate this variance. Specifically, this method
* returns the difference between <code>startInstant</code> and
* <code>startInstant+duration</code>.
*
* @throws NullPointerException
* If the startInstant parameter is null.
*
* @return milliseconds between <code>startInstant</code> and
* <code>startInstant</code> plus this <code>Duration</code>
*
* @see #getTimeInMillis(Calendar)
*/
public long getTimeInMillis(final Date startInstant) {
Calendar cal = new GregorianCalendar();
cal.setTime(startInstant);
this.addTo(cal);
return getCalendarTimeInMillis(cal) - startInstant.getTime();
}
// /**
// * Returns an equivalent but "normalized" duration value.
// *
// * Intuitively, the normalization moves YEARS into
// * MONTHS (by x12) and moves DAYS, HOURS, and MINUTES fields
// * into SECONDS (by x86400, x3600, and x60 respectively.)
// *
// *
// * Formally, this method satisfies the following conditions:
// * <ul>
// * <li>x.normalize().equals(x)
// * <li>!x.normalize().isSet(Duration.YEARS)
// * <li>!x.normalize().isSet(Duration.DAYS)
// * <li>!x.normalize().isSet(Duration.HOURS)
// * <li>!x.normalize().isSet(Duration.MINUTES)
// * </ul>
// *
// * @return
// * always return a non-null valid value.
// */
// public Duration normalize() {
// return null;
// }
Converts the years and months fields into the days field
by using a specific time instant as the reference point.
For example, duration of one month normalizes to 31 days
given the start time instance "July 8th 2003, 17:40:32".
Formally, the computation is done as follows:
- The given Calendar object is cloned.
- The years, months and days fields will be added to the
Calendar
object by using the Calendar.add(int, int)
method. - The difference between two Calendars are computed in terms of days.
- The computed days, along with the hours, minutes and seconds
fields of this duration object is used to construct a new
Duration object.
Note that since the Calendar class uses int
to
hold the value of year and month, this method may produce
an unexpected result if this duration object holds
a very large value in the years or months fields.
Params: - startTimeInstant –
Calendar
reference point.
Throws: - NullPointerException – If the startTimeInstant parameter is null.
Returns: Duration
of years and months of this Duration
as days.
/**
* <p>Converts the years and months fields into the days field
* by using a specific time instant as the reference point.</p>
*
* <p>For example, duration of one month normalizes to 31 days
* given the start time instance "July 8th 2003, 17:40:32".</p>
*
* <p>Formally, the computation is done as follows:</p>
* <ol>
* <li>The given Calendar object is cloned.
* <li>The years, months and days fields will be added to
* the {@link Calendar} object
* by using the {@link Calendar#add(int,int)} method.
* <li>The difference between two Calendars are computed in terms of days.
* <li>The computed days, along with the hours, minutes and seconds
* fields of this duration object is used to construct a new
* Duration object.
* </ol>
*
* <p>Note that since the Calendar class uses <code>int</code> to
* hold the value of year and month, this method may produce
* an unexpected result if this duration object holds
* a very large value in the years or months fields.</p>
*
* @param startTimeInstant <code>Calendar</code> reference point.
*
* @return <code>Duration</code> of years and months of this <code>Duration</code> as days.
*
* @throws NullPointerException If the startTimeInstant parameter is null.
*/
public Duration normalizeWith(Calendar startTimeInstant) {
Calendar c = (Calendar) startTimeInstant.clone();
// using int may cause overflow, but
// Calendar internally treats value as int anyways.
c.add(Calendar.YEAR, getYears() * signum);
c.add(Calendar.MONTH, getMonths() * signum);
c.add(Calendar.DAY_OF_MONTH, getDays() * signum);
// obtain the difference in terms of days
long diff = getCalendarTimeInMillis(c) - getCalendarTimeInMillis(startTimeInstant);
int days = (int) (diff / (1000L * 60L * 60L * 24L));
return new DurationImpl(
days >= 0,
null,
null,
wrap(Math.abs(days)),
(BigInteger) getField(DatatypeConstants.HOURS),
(BigInteger) getField(DatatypeConstants.MINUTES),
(BigDecimal) getField(DatatypeConstants.SECONDS));
}
Computes a new duration whose value is factor
times
longer than the value of this duration.
This method is provided for the convenience.
It is functionally equivalent to the following code:
multiply(new BigDecimal(String.valueOf(factor)))
Params: - factor – Factor times longer of new
Duration
to create.
See Also: Returns: New Duration
that is factor
times longer than this Duration
.
/**
* <p>Computes a new duration whose value is <code>factor</code> times
* longer than the value of this duration.</p>
*
* <p>This method is provided for the convenience.
* It is functionally equivalent to the following code:</p>
* <pre>
* multiply(new BigDecimal(String.valueOf(factor)))
* </pre>
*
* @param factor Factor times longer of new <code>Duration</code> to create.
*
* @return New <code>Duration</code> that is <code>factor</code>times longer than this <code>Duration</code>.
*
* @see #multiply(BigDecimal)
*/
public Duration multiply(int factor) {
return multiply(BigDecimal.valueOf(factor));
}
Computes a new duration whose value is factor
times
longer than the value of this duration.
For example,
"P1M" (1 month) * "12" = "P12M" (12 months)
"PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
"P1M" (1 month) * "1.5" = IllegalStateException
Since the Duration
class is immutable, this method doesn't change the value of this object. It simply computes a new Duration object and returns it.
The operation will be performed field by field with the precision of BigDecimal
. Since all the fields except seconds are restricted to hold integers, any fraction produced by the computation will be carried down toward the next lower unit. For example, if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day, which will be carried down to "PT12H" (12 hours). When fractions of month cannot be meaningfully carried down to days, or year to months, this will cause an IllegalStateException
to be thrown. For example if you multiple one month by 0.5.
To avoid IllegalStateException
, use the normalizeWith(Calendar)
method to remove the years and months fields.
Params: - factor – to multiply by
Throws: - IllegalStateException – if operation produces fraction in
the months field.
- NullPointerException – if the
factor
parameter is
null
.
Returns: returns a non-null valid Duration
object
/**
* Computes a new duration whose value is <code>factor</code> times
* longer than the value of this duration.
*
* <p>
* For example,
* <pre>
* "P1M" (1 month) * "12" = "P12M" (12 months)
* "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
* "P1M" (1 month) * "1.5" = IllegalStateException
* </pre>
*
* <p>
* Since the {@link Duration} class is immutable, this method
* doesn't change the value of this object. It simply computes
* a new Duration object and returns it.
*
* <p>
* The operation will be performed field by field with the precision
* of {@link BigDecimal}. Since all the fields except seconds are
* restricted to hold integers,
* any fraction produced by the computation will be
* carried down toward the next lower unit. For example,
* if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,
* which will be carried down to "PT12H" (12 hours).
* When fractions of month cannot be meaningfully carried down
* to days, or year to months, this will cause an
* {@link IllegalStateException} to be thrown.
* For example if you multiple one month by 0.5.</p>
*
* <p>
* To avoid {@link IllegalStateException}, use
* the {@link #normalizeWith(Calendar)} method to remove the years
* and months fields.
*
* @param factor to multiply by
*
* @return
* returns a non-null valid {@link Duration} object
*
* @throws IllegalStateException if operation produces fraction in
* the months field.
*
* @throws NullPointerException if the <code>factor</code> parameter is
* <code>null</code>.
*
*/
public Duration multiply(BigDecimal factor) {
BigDecimal carry = ZERO;
int factorSign = factor.signum();
factor = factor.abs();
BigDecimal[] buf = new BigDecimal[6];
for (int i = 0; i < 5; i++) {
BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);
bd = bd.multiply(factor).add(carry);
buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);
bd = bd.subtract(buf[i]);
if (i == 1) {
if (bd.signum() != 0) {
throw new IllegalStateException(); // illegal carry-down
} else {
carry = ZERO;
}
}
else {
carry = bd.multiply(FACTORS[i]);
}
}
if (seconds != null) {
buf[5] = seconds.multiply(factor).add(carry);
}
else {
buf[5] = carry;
}
return new DurationImpl(
this.signum * factorSign >= 0,
toBigInteger(buf[0], null == years),
toBigInteger(buf[1], null == months),
toBigInteger(buf[2], null == days),
toBigInteger(buf[3], null == hours),
toBigInteger(buf[4], null == minutes),
(buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
}
Gets the value of the field as a BigDecimal
.
If the field is unset, return 0.
Params: - f – Field to get value for.
Returns: non-null valid BigDecimal
.
/**
* <p>Gets the value of the field as a {@link BigDecimal}.</p>
*
* <p>If the field is unset, return 0.</p>
*
* @param f Field to get value for.
*
* @return non-null valid {@link BigDecimal}.
*/
private BigDecimal getFieldAsBigDecimal(DatatypeConstants.Field f) {
if (f == DatatypeConstants.SECONDS) {
if (seconds != null) {
return seconds;
}
else {
return ZERO;
}
}
else {
BigInteger bi = (BigInteger) getField(f);
if (bi == null) {
return ZERO;
}
else {
return new BigDecimal(bi);
}
}
}
BigInteger value of BigDecimal value.
Params: - value – Value to convert.
- canBeNull – Can returned value be null?
Returns: BigInteger value of BigDecimal, possibly null.
/**
* <p>BigInteger value of BigDecimal value.</p>
*
* @param value Value to convert.
* @param canBeNull Can returned value be null?
*
* @return BigInteger value of BigDecimal, possibly null.
*/
private static BigInteger toBigInteger(
BigDecimal value,
boolean canBeNull) {
if (canBeNull && value.signum() == 0) {
return null;
}
else {
return value.unscaledValue();
}
}
1 unit of FIELDS[i] is equivalent to FACTORS[i]
unit of
FIELDS[i+1].
/**
* 1 unit of FIELDS[i] is equivalent to <code>FACTORS[i]</code> unit of
* FIELDS[i+1].
*/
private static final BigDecimal[] FACTORS = new BigDecimal[] {
BigDecimal.valueOf(12),
null/*undefined*/,
BigDecimal.valueOf(24),
BigDecimal.valueOf(60),
BigDecimal.valueOf(60)
};
Computes a new duration whose value is this+rhs
.
For example,
"1 day" + "-3 days" = "-2 days"
"1 year" + "1 day" = "1 year and 1 day"
"-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
"15 hours" + "-3 days" = "-(2 days,9 hours)"
"1 year" + "-1 day" = IllegalStateException
Since there's no way to meaningfully subtract 1 day from 1 month, there are cases where the operation fails in IllegalStateException
.
Formally, the computation is defined as follows.
Firstly, we can assume that two Duration
s to be added are both positive without losing generality (i.e., (-X)+Y=Y-X
, X+(-Y)=X-Y
,
(-X)+(-Y)=-(X+Y)
)
Addition of two positive Duration
s are simply defined as field by field addition where missing fields are treated as 0.
A field of the resulting Duration
will be unset if and only if respective fields of two input Duration
s are unset.
Note that lhs.add(rhs)
will be always successful if
lhs.signum()*rhs.signum()!=-1
or both of them are
normalized.
Params: - rhs –
Duration
to add to this Duration
Throws: - NullPointerException –
If the rhs parameter is null.
- IllegalStateException –
If two durations cannot be meaningfully added. For
example, adding negative one day to one month causes
this exception.
See Also: Returns:
non-null valid Duration object.
/**
* <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
*
* <p>For example,</p>
* <pre>
* "1 day" + "-3 days" = "-2 days"
* "1 year" + "1 day" = "1 year and 1 day"
* "-(1 hour,50 minutes)" + "-20 minutes" = "-(1 hours,70 minutes)"
* "15 hours" + "-3 days" = "-(2 days,9 hours)"
* "1 year" + "-1 day" = IllegalStateException
* </pre>
*
* <p>Since there's no way to meaningfully subtract 1 day from 1 month,
* there are cases where the operation fails in
* {@link IllegalStateException}.</p>
*
* <p>
* Formally, the computation is defined as follows.</p>
* <p>
* Firstly, we can assume that two {@link Duration}s to be added
* are both positive without losing generality (i.e.,
* <code>(-X)+Y=Y-X</code>, <code>X+(-Y)=X-Y</code>,
* <code>(-X)+(-Y)=-(X+Y)</code>)
*
* <p>
* Addition of two positive {@link Duration}s are simply defined as
* field by field addition where missing fields are treated as 0.
* <p>
* A field of the resulting {@link Duration} will be unset if and
* only if respective fields of two input {@link Duration}s are unset.
* <p>
* Note that <code>lhs.add(rhs)</code> will be always successful if
* <code>lhs.signum()*rhs.signum()!=-1</code> or both of them are
* normalized.</p>
*
* @param rhs <code>Duration</code> to add to this <code>Duration</code>
*
* @return
* non-null valid Duration object.
*
* @throws NullPointerException
* If the rhs parameter is null.
* @throws IllegalStateException
* If two durations cannot be meaningfully added. For
* example, adding negative one day to one month causes
* this exception.
*
*
* @see #subtract(Duration)
*/
public Duration add(final Duration rhs) {
Duration lhs = this;
BigDecimal[] buf = new BigDecimal[6];
buf[0] = sanitize((BigInteger) lhs.getField(DatatypeConstants.YEARS),
lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.YEARS), rhs.getSign()));
buf[1] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MONTHS),
lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MONTHS), rhs.getSign()));
buf[2] = sanitize((BigInteger) lhs.getField(DatatypeConstants.DAYS),
lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.DAYS), rhs.getSign()));
buf[3] = sanitize((BigInteger) lhs.getField(DatatypeConstants.HOURS),
lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.HOURS), rhs.getSign()));
buf[4] = sanitize((BigInteger) lhs.getField(DatatypeConstants.MINUTES),
lhs.getSign()).add(sanitize((BigInteger) rhs.getField(DatatypeConstants.MINUTES), rhs.getSign()));
buf[5] = sanitize((BigDecimal) lhs.getField(DatatypeConstants.SECONDS),
lhs.getSign()).add(sanitize((BigDecimal) rhs.getField(DatatypeConstants.SECONDS), rhs.getSign()));
// align sign
alignSigns(buf, 0, 2); // Y,M
alignSigns(buf, 2, 6); // D,h,m,s
// make sure that the sign bit is consistent across all 6 fields.
int s = 0;
for (int i = 0; i < 6; i++) {
if (s * buf[i].signum() < 0) {
throw new IllegalStateException();
}
if (s == 0) {
s = buf[i].signum();
}
}
return new DurationImpl(
s >= 0,
toBigInteger(sanitize(buf[0], s),
lhs.getField(DatatypeConstants.YEARS) == null && rhs.getField(DatatypeConstants.YEARS) == null),
toBigInteger(sanitize(buf[1], s),
lhs.getField(DatatypeConstants.MONTHS) == null && rhs.getField(DatatypeConstants.MONTHS) == null),
toBigInteger(sanitize(buf[2], s),
lhs.getField(DatatypeConstants.DAYS) == null && rhs.getField(DatatypeConstants.DAYS) == null),
toBigInteger(sanitize(buf[3], s),
lhs.getField(DatatypeConstants.HOURS) == null && rhs.getField(DatatypeConstants.HOURS) == null),
toBigInteger(sanitize(buf[4], s),
lhs.getField(DatatypeConstants.MINUTES) == null && rhs.getField(DatatypeConstants.MINUTES) == null),
(buf[5].signum() == 0
&& lhs.getField(DatatypeConstants.SECONDS) == null
&& rhs.getField(DatatypeConstants.SECONDS) == null) ? null : sanitize(buf[5], s));
}
private static void alignSigns(BigDecimal[] buf, int start, int end) {
// align sign
boolean touched;
do { // repeat until all the sign bits become consistent
touched = false;
int s = 0; // sign of the left fields
for (int i = start; i < end; i++) {
if (s * buf[i].signum() < 0) {
// this field has different sign than its left field.
touched = true;
// compute the number of unit that needs to be borrowed.
BigDecimal borrow =
buf[i].abs().divide(
FACTORS[i - 1],
BigDecimal.ROUND_UP);
if (buf[i].signum() > 0) {
borrow = borrow.negate();
}
// update values
buf[i - 1] = buf[i - 1].subtract(borrow);
buf[i] = buf[i].add(borrow.multiply(FACTORS[i - 1]));
}
if (buf[i].signum() != 0) {
s = buf[i].signum();
}
}
} while (touched);
}
Compute value*signum
where value==null is treated as
value==0.
Params: - value – Value to sanitize.
- signum – 0 to sanitize to 0, > 0 to sanitize to
value
, < 0 to sanitize to negative value
.
Returns: non-null BigDecimal
.
/**
* Compute <code>value*signum</code> where value==null is treated as
* value==0.
* @param value Value to sanitize.
* @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
*
* @return non-null {@link BigDecimal}.
*/
private static BigDecimal sanitize(BigInteger value, int signum) {
if (signum == 0 || value == null) {
return ZERO;
}
if (signum > 0) {
return new BigDecimal(value);
}
return new BigDecimal(value.negate());
}
Compute value*signum
where value==null
is treated as value==0
.
Params: - value – Value to sanitize.
- signum – 0 to sanitize to 0, > 0 to sanitize to
value
, < 0 to sanitize to negative value
.
Returns: non-null BigDecimal
.
/**
* <p>Compute <code>value*signum</code> where <code>value==null</code> is treated as <code>value==0</code></p>.
*
* @param value Value to sanitize.
* @param signum 0 to sanitize to 0, > 0 to sanitize to <code>value</code>, < 0 to sanitize to negative <code>value</code>.
*
* @return non-null {@link BigDecimal}.
*/
static BigDecimal sanitize(BigDecimal value, int signum) {
if (signum == 0 || value == null) {
return ZERO;
}
if (signum > 0) {
return value;
}
return value.negate();
}
Computes a new duration whose value is this-rhs
.
For example:
"1 day" - "-3 days" = "4 days"
"1 year" - "1 day" = IllegalStateException
"-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)"
"15 hours" - "-3 days" = "3 days and 15 hours"
"1 year" - "-1 day" = "1 year and 1 day"
Since there's no way to meaningfully subtract 1 day from 1 month, there are cases where the operation fails in IllegalStateException
.
Formally the computation is defined as follows. First, we can assume that two Duration
s are both positive without losing generality. (i.e., (-X)-Y=-(X+Y)
, X-(-Y)=X+Y
,
(-X)-(-Y)=-(X-Y)
)
Then two durations are subtracted field by field.
If the sign of any non-zero field F is different from
the sign of the most significant field,
1 (if F is negative) or -1 (otherwise)
will be borrowed from the next bigger unit of F.
This process is repeated until all the non-zero fields have
the same sign.
If a borrow occurs in the days field (in other words, if the computation needs to borrow 1 or -1 month to compensate days), then the computation fails by throwing an IllegalStateException
.
Params: - rhs –
Duration
to substract from this Duration
.
Throws: - IllegalStateException –
If two durations cannot be meaningfully subtracted. For
example, subtracting one day from one month causes
this exception.
- NullPointerException –
If the rhs parameter is null.
See Also: Returns: New Duration
created from subtracting rhs
from this Duration
.
/**
* <p>Computes a new duration whose value is <code>this-rhs</code>.</p>
*
* <p>For example:</p>
* <pre>
* "1 day" - "-3 days" = "4 days"
* "1 year" - "1 day" = IllegalStateException
* "-(1 hour,50 minutes)" - "-20 minutes" = "-(1hours,30 minutes)"
* "15 hours" - "-3 days" = "3 days and 15 hours"
* "1 year" - "-1 day" = "1 year and 1 day"
* </pre>
*
* <p>Since there's no way to meaningfully subtract 1 day from 1 month,
* there are cases where the operation fails in {@link IllegalStateException}.</p>
*
* <p>Formally the computation is defined as follows.
* First, we can assume that two {@link Duration}s are both positive
* without losing generality. (i.e.,
* <code>(-X)-Y=-(X+Y)</code>, <code>X-(-Y)=X+Y</code>,
* <code>(-X)-(-Y)=-(X-Y)</code>)</p>
*
* <p>Then two durations are subtracted field by field.
* If the sign of any non-zero field <tt>F</tt> is different from
* the sign of the most significant field,
* 1 (if <tt>F</tt> is negative) or -1 (otherwise)
* will be borrowed from the next bigger unit of <tt>F</tt>.</p>
*
* <p>This process is repeated until all the non-zero fields have
* the same sign.</p>
*
* <p>If a borrow occurs in the days field (in other words, if
* the computation needs to borrow 1 or -1 month to compensate
* days), then the computation fails by throwing an
* {@link IllegalStateException}.</p>
*
* @param rhs <code>Duration</code> to substract from this <code>Duration</code>.
*
* @return New <code>Duration</code> created from subtracting <code>rhs</code> from this <code>Duration</code>.
*
* @throws IllegalStateException
* If two durations cannot be meaningfully subtracted. For
* example, subtracting one day from one month causes
* this exception.
*
* @throws NullPointerException
* If the rhs parameter is null.
*
* @see #add(Duration)
*/
public Duration subtract(final Duration rhs) {
return add(rhs.negate());
}
Returns a new Duration
object whose value is -this
.
Since the Duration
class is immutable, this method doesn't change the value of this object. It simply computes a new Duration object and returns it.
Returns: always return a non-null valid Duration
object.
/**
* Returns a new {@link Duration} object whose
* value is <code>-this</code>.
*
* <p>
* Since the {@link Duration} class is immutable, this method
* doesn't change the value of this object. It simply computes
* a new Duration object and returns it.
*
* @return
* always return a non-null valid {@link Duration} object.
*/
public Duration negate() {
return new DurationImpl(
signum <= 0,
years,
months,
days,
hours,
minutes,
seconds);
}
Returns the sign of this duration in -1,0, or 1.
Returns:
-1 if this duration is negative, 0 if the duration is zero,
and 1 if the duration is postive.
/**
* Returns the sign of this duration in -1,0, or 1.
*
* @return
* -1 if this duration is negative, 0 if the duration is zero,
* and 1 if the duration is postive.
*/
public int signum() {
return signum;
}
Adds this duration to a Calendar
object. Calls Calendar.add(int, int)
in the order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS if those fields are present. Because the Calendar
class uses int to hold values, there are cases where this method won't work correctly (for example if values of fields exceed the range of int.)
Also, since this duration class is a Gregorian duration, this method will not work correctly if the given Calendar
object is based on some other calendar systems.
Any fractional parts of this Duration
object beyond milliseconds will be simply ignored. For example, if this duration is "P1.23456S", then 1 is added to SECONDS, 234 is added to MILLISECONDS, and the rest will be unused.
Note that because Calendar.add(int, int)
is using int, Duration
with values beyond the range of int in its fields will cause overflow/underflow to the given Calendar
. XMLGregorianCalendar.add(Duration)
provides the same basic operation as this method while avoiding the overflow/underflow issues.
Params: - calendar –
A calendar object whose value will be modified.
Throws: - NullPointerException –
if the calendar parameter is null.
/**
* Adds this duration to a {@link Calendar} object.
*
* <p>
* Calls {@link java.util.Calendar#add(int,int)} in the
* order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS
* if those fields are present. Because the {@link Calendar} class
* uses int to hold values, there are cases where this method
* won't work correctly (for example if values of fields
* exceed the range of int.)
* </p>
*
* <p>
* Also, since this duration class is a Gregorian duration, this
* method will not work correctly if the given {@link Calendar}
* object is based on some other calendar systems.
* </p>
*
* <p>
* Any fractional parts of this {@link Duration} object
* beyond milliseconds will be simply ignored. For example, if
* this duration is "P1.23456S", then 1 is added to SECONDS,
* 234 is added to MILLISECONDS, and the rest will be unused.
* </p>
*
* <p>
* Note that because {@link Calendar#add(int, int)} is using
* <tt>int</tt>, {@link Duration} with values beyond the
* range of <tt>int</tt> in its fields
* will cause overflow/underflow to the given {@link Calendar}.
* {@link XMLGregorianCalendar#add(Duration)} provides the same
* basic operation as this method while avoiding
* the overflow/underflow issues.
*
* @param calendar
* A calendar object whose value will be modified.
* @throws NullPointerException
* if the calendar parameter is null.
*/
public void addTo(Calendar calendar) {
calendar.add(Calendar.YEAR, getYears() * signum);
calendar.add(Calendar.MONTH, getMonths() * signum);
calendar.add(Calendar.DAY_OF_MONTH, getDays() * signum);
calendar.add(Calendar.HOUR, getHours() * signum);
calendar.add(Calendar.MINUTE, getMinutes() * signum);
calendar.add(Calendar.SECOND, getSeconds() * signum);
if (seconds != null) {
BigDecimal fraction =
seconds.subtract(seconds.setScale(0, BigDecimal.ROUND_DOWN));
int millisec = fraction.movePointRight(3).intValue();
calendar.add(Calendar.MILLISECOND, millisec * signum);
}
}
Adds this duration to a Date
object. The given date is first converted into a GregorianCalendar
, then the duration is added exactly like the addTo(Calendar)
method.
The updated time instant is then converted back into a Date
object and used to update the given Date
object.
This somewhat redundant computation is necessary to unambiguously
determine the duration of months and years.
Params: - date –
A date object whose value will be modified.
Throws: - NullPointerException –
if the date parameter is null.
/**
* Adds this duration to a {@link Date} object.
*
* <p>
* The given date is first converted into
* a {@link java.util.GregorianCalendar}, then the duration
* is added exactly like the {@link #addTo(Calendar)} method.
*
* <p>
* The updated time instant is then converted back into a
* {@link Date} object and used to update the given {@link Date} object.
*
* <p>
* This somewhat redundant computation is necessary to unambiguously
* determine the duration of months and years.
*
* @param date
* A date object whose value will be modified.
* @throws NullPointerException
* if the date parameter is null.
*/
public void addTo(Date date) {
Calendar cal = new GregorianCalendar();
cal.setTime(date); // this will throw NPE if date==null
this.addTo(cal);
date.setTime(getCalendarTimeInMillis(cal));
}
Returns time value in milliseconds
Params: - cal – A calendar object
Returns: time value
Diff from Xerces; Use JDK 1.5 feature.
/**
* Returns time value in milliseconds
* @param cal A calendar object
* @return time value
*
* Diff from Xerces; Use JDK 1.5 feature.
*/
private static long getCalendarTimeInMillis(Calendar cal) {
return cal.getTimeInMillis();
}
Stream Unique Identifier.
Serialization uses the lexical form returned by toString().
/**
* <p>Stream Unique Identifier.</p>
*
* <p>Serialization uses the lexical form returned by toString().</p>
*/
private static final long serialVersionUID = 1L;
Writes Duration
as a lexical representation for maximum future compatibility. Returns:
An object that encapsulates the string
returned by this.toString()
.
/**
* Writes {@link Duration} as a lexical representation
* for maximum future compatibility.
*
* @return
* An object that encapsulates the string
* returned by <code>this.toString()</code>.
*/
private Object writeReplace() throws IOException {
return new DurationStream(this.toString());
}
Representation of Duration
in the object stream. Author: Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
/**
* Representation of {@link Duration} in the object stream.
*
* @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
*/
private static class DurationStream implements Serializable {
private final String lexical;
private DurationStream(String _lexical) {
this.lexical = _lexical;
}
private Object readResolve() throws ObjectStreamException {
return new DurationImpl(lexical);
}
private static final long serialVersionUID = 1L;
}
}