*
- *
The {@link java.nio.file.attribute.UserPrincipal} and
+ * The {@link java.nio.file.attribute.UserPrincipal} and
* {@link java.nio.file.attribute.GroupPrincipal} interfaces represent an
* identity or group identity.
*
- *
The {@link java.nio.file.attribute.UserPrincipalLookupService}
+ * The {@link java.nio.file.attribute.UserPrincipalLookupService}
* interface defines methods to lookup user or group principals.
*
- *
The {@link java.nio.file.attribute.FileAttribute} interface
+ * The {@link java.nio.file.attribute.FileAttribute} interface
* represents the value of an attribute for cases where the attribute value is
* required to be set atomically when creating an object in the file system.
*
diff --git a/src/share/classes/java/nio/file/package-info.java b/src/share/classes/java/nio/file/package-info.java
index c0cf571b555df1121013b8d77ea422c2942fc698..ca3e946d567ab8bd8104e30e9100a31f293da096 100644
--- a/src/share/classes/java/nio/file/package-info.java
+++ b/src/share/classes/java/nio/file/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2013, 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
@@ -33,8 +33,8 @@
* package is used by service provider implementors wishing to extend the
* platform default provider, or to construct other provider implementations.
*
- * Symbolic Links
- * Many operating systems and file systems support for symbolic links .
+ *
+ * Many operating systems and file systems support for symbolic links .
* A symbolic link is a special file that serves as a reference to another file.
* For the most part, symbolic links are transparent to applications and
* operations on symbolic links are automatically redirected to the target
@@ -45,8 +45,8 @@
* that are semantically close but support for these other types of links is
* not included in this package.
*
- * Interoperability
- * The {@link java.io.File} class defines the {@link java.io.File#toPath
+ *
+ * The {@link java.io.File} class defines the {@link java.io.File#toPath
* toPath} method to construct a {@link java.nio.file.Path} by converting
* the abstract path represented by the {@code java.io.File} object. The resulting
* {@code Path} can be used to operate on the same file as the {@code File}
@@ -55,7 +55,7 @@
* and {@code java.io.File} objects.
*
* Visibility
- * The view of the files and file system provided by classes in this package are
+ * The view of the files and file system provided by classes in this package are
* guaranteed to be consistent with other views provided by other instances in the
* same Java virtual machine. The view may or may not, however, be consistent with
* the view of the file system as seen by other concurrently running programs due
@@ -65,8 +65,8 @@
* or on some other machine. The exact nature of any such inconsistencies are
* system-dependent and are therefore unspecified.
*
- * Synchronized I/O File Integrity
- * The {@link java.nio.file.StandardOpenOption#SYNC SYNC} and {@link
+ *
+ * The {@link java.nio.file.StandardOpenOption#SYNC SYNC} and {@link
* java.nio.file.StandardOpenOption#DSYNC DSYNC} options are used when opening a file
* to require that updates to the file are written synchronously to the underlying
* storage device. In the case of the default provider, and the file resides on
@@ -83,7 +83,7 @@
* specific.
*
* General Exceptions
- * Unless otherwise noted, passing a {@code null} argument to a constructor
+ * Unless otherwise noted, passing a {@code null} argument to a constructor
* or method of any class or interface in this package will cause a {@link
* java.lang.NullPointerException NullPointerException} to be thrown. Additionally,
* invoking a method with a collection containing a {@code null} element will
diff --git a/src/share/classes/java/security/cert/PKIXRevocationChecker.java b/src/share/classes/java/security/cert/PKIXRevocationChecker.java
index 2446c821df660061a55445ae412fc1cbf0f1631c..d0e2ee0298611f45e5e793ec6cef0c35cfd711ed 100644
--- a/src/share/classes/java/security/cert/PKIXRevocationChecker.java
+++ b/src/share/classes/java/security/cert/PKIXRevocationChecker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2013, 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
@@ -50,21 +50,26 @@ import java.util.Set;
* status of certificates with OCSP and CRLs. By default, OCSP is the
* preferred mechanism for checking revocation status, with CRLs as the
* fallback mechanism. However, this preference can be switched to CRLs with
- * the {@link Option#PREFER_CRLS PREFER_CRLS} option.
+ * the {@link Option#PREFER_CRLS PREFER_CRLS} option. In addition, the fallback
+ * mechanism can be disabled with the {@link Option#NO_FALLBACK NO_FALLBACK}
+ * option.
*
*
A {@code PKIXRevocationChecker} is obtained by calling the
* {@link CertPathValidator#getRevocationChecker getRevocationChecker} method
* of a PKIX {@code CertPathValidator}. Additional parameters and options
- * specific to revocation can be set (by calling {@link #setOCSPResponder}
- * method for instance). The {@code PKIXRevocationChecker} is added to
- * a {@code PKIXParameters} object using the
- * {@link PKIXParameters#addCertPathChecker addCertPathChecker}
+ * specific to revocation can be set (by calling the
+ * {@link #setOcspResponder setOcspResponder} method for instance). The
+ * {@code PKIXRevocationChecker} is added to a {@code PKIXParameters} object
+ * using the {@link PKIXParameters#addCertPathChecker addCertPathChecker}
* or {@link PKIXParameters#setCertPathCheckers setCertPathCheckers} method,
* and then the {@code PKIXParameters} is passed along with the {@code CertPath}
* to be validated to the {@link CertPathValidator#validate validate} method
* of a PKIX {@code CertPathValidator}. When supplying a revocation checker in
* this manner, it will be used to check revocation irrespective of the setting
* of the {@link PKIXParameters#isRevocationEnabled RevocationEnabled} flag.
+ * Similarly, a {@code PKIXRevocationChecker} may be added to a
+ * {@code PKIXBuilderParameters} object for use with a PKIX
+ * {@code CertPathBuilder}.
*
*
Note that when a {@code PKIXRevocationChecker} is added to
* {@code PKIXParameters}, it clones the {@code PKIXRevocationChecker};
@@ -83,6 +88,13 @@ import java.util.Set;
* need not synchronize.
*
* @since 1.8
+ *
+ * @see RFC 2560: X.509
+ * Internet Public Key Infrastructure Online Certificate Status Protocol -
+ * OCSP , RFC 5280: Internet X.509
+ * Public Key Infrastructure Certificate and Certificate Revocation List (CRL)
+ * Profile
*/
public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
private URI ocspResponder;
@@ -101,7 +113,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @param uri the responder URI
*/
- public void setOCSPResponder(URI uri) {
+ public void setOcspResponder(URI uri) {
this.ocspResponder = uri;
}
@@ -114,7 +126,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @return the responder URI, or {@code null} if not set
*/
- public URI getOCSPResponder() {
+ public URI getOcspResponder() {
return ocspResponder;
}
@@ -126,7 +138,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @param cert the responder's certificate
*/
- public void setOCSPResponderCert(X509Certificate cert) {
+ public void setOcspResponderCert(X509Certificate cert) {
this.ocspResponderCert = cert;
}
@@ -140,7 +152,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*
* @return the responder's certificate, or {@code null} if not set
*/
- public X509Certificate getOCSPResponderCert() {
+ public X509Certificate getOcspResponderCert() {
return ocspResponderCert;
}
@@ -151,7 +163,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* @param extensions a list of extensions. The list is copied to protect
* against subsequent modification.
*/
- public void setOCSPExtensions(List extensions)
+ public void setOcspExtensions(List extensions)
{
this.ocspExtensions = (extensions == null)
? Collections.emptyList()
@@ -161,10 +173,10 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
/**
* Gets the optional OCSP request extensions.
*
- * @return an unmodifiable list of extensions. Returns an empty list if no
+ * @return an unmodifiable list of extensions. The list is empty if no
* extensions have been specified.
*/
- public List getOCSPExtensions() {
+ public List getOcspExtensions() {
return Collections.unmodifiableList(ocspExtensions);
}
@@ -177,7 +189,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* DER-encoded OCSP response for that certificate. A deep copy of
* the map is performed to protect against subsequent modification.
*/
- public void setOCSPResponses(Map responses)
+ public void setOcspResponses(Map responses)
{
if (responses == null) {
this.ocspResponses = Collections.emptyMap();
@@ -200,7 +212,7 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
* the map is returned to protect against subsequent modification.
* Returns an empty map if no responses have been specified.
*/
- public Map getOCSPResponses() {
+ public Map getOcspResponses() {
Map copy = new HashMap<>(ocspResponses.size());
for (Map.Entry e : ocspResponses.entrySet()) {
copy.put(e.getKey(), e.getValue().clone());
@@ -223,15 +235,31 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
/**
* Gets the revocation options.
*
- * @return an unmodifiable set of revocation options, or an empty set if
- * none are specified
+ * @return an unmodifiable set of revocation options. The set is empty if
+ * no options have been specified.
*/
public Set getOptions() {
return Collections.unmodifiableSet(options);
}
+ /**
+ * Returns a list containing the exceptions that are ignored by the
+ * revocation checker when the {@link Option#SOFT_FAIL SOFT_FAIL} option
+ * is set. The list is cleared each time {@link #init init} is called.
+ * The list is ordered in ascending order according to the certificate
+ * index returned by {@link CertPathValidatorException#getIndex getIndex}
+ * method of each entry.
+ *
+ * An implementation of {@code PKIXRevocationChecker} is responsible for
+ * adding the ignored exceptions to the list.
+ *
+ * @return an unmodifiable list containing the ignored exceptions. The list
+ * is empty if no exceptions have been ignored.
+ */
+ public abstract List getSoftFailExceptions();
+
@Override
- public Object clone() {
+ public PKIXRevocationChecker clone() {
PKIXRevocationChecker copy = (PKIXRevocationChecker)super.clone();
copy.ocspExtensions = new ArrayList<>(ocspExtensions);
copy.ocspResponses = new HashMap<>(ocspResponses);
@@ -262,9 +290,26 @@ public abstract class PKIXRevocationChecker extends PKIXCertPathChecker {
*/
PREFER_CRLS,
/**
- * Ignore network failures. The default behavior is to consider it a
- * failure if the revocation status of a certificate cannot be obtained
- * due to a network error. This option applies to both OCSP and CRLs.
+ * Disable the fallback mechanism.
+ */
+ NO_FALLBACK,
+ /**
+ * Allow revocation check to succeed if the revocation status cannot be
+ * determined for one of the following reasons:
+ *
+ * The CRL or OCSP response cannot be obtained because of a
+ * network error.
+ * The OCSP responder returns one of the following errors
+ * specified in section 2.3 of RFC 2560: internalError, tryLater,
+ * or unauthorized.
+ *
+ * Note that these conditions apply to both OCSP and CRLs, and unless
+ * the {@code NO_FALLBACK} option is set, the revocation check is
+ * allowed to succeed only if both mechanisms fail under one of the
+ * conditions as stated above.
+ * Exceptions that cause the network errors are ignored but can be
+ * later retrieved by calling the
+ * {@link #getSoftFailExceptions getSoftFailExceptions} method.
*/
SOFT_FAIL
}
diff --git a/src/share/classes/java/time/DayOfWeek.java b/src/share/classes/java/time/DayOfWeek.java
index d2a6f5aa5416721c3cca967202113a2e4359a958..b48482241df2e357b46f5e5c3cdc453ad8fa4672 100644
--- a/src/share/classes/java/time/DayOfWeek.java
+++ b/src/share/classes/java/time/DayOfWeek.java
@@ -61,7 +61,6 @@
*/
package java.time;
-import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.temporal.ChronoField.DAY_OF_WEEK;
import static java.time.temporal.ChronoUnit.DAYS;
@@ -73,6 +72,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields;
import java.util.Locale;
@@ -339,7 +339,7 @@ public enum DayOfWeek implements TemporalAccessor, TemporalAdjuster {
if (field == DAY_OF_WEEK) {
return getValue();
} else if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
diff --git a/src/share/classes/java/time/Duration.java b/src/share/classes/java/time/Duration.java
index 52b225e6e697c591ba6deef9de77d119a5ae9be7..ab7da66f83a63f9c0d536183e999f93fe0836817 100644
--- a/src/share/classes/java/time/Duration.java
+++ b/src/share/classes/java/time/Duration.java
@@ -459,9 +459,9 @@ public final class Duration
*/
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
try {
- return ofNanos(startInclusive.periodUntil(endExclusive, NANOS));
+ return ofNanos(startInclusive.until(endExclusive, NANOS));
} catch (DateTimeException | ArithmeticException ex) {
- long secs = startInclusive.periodUntil(endExclusive, SECONDS);
+ long secs = startInclusive.until(endExclusive, SECONDS);
long nanos;
try {
nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND);
@@ -523,7 +523,7 @@ public final class Duration
} else if (unit == NANOS) {
return nanos;
} else {
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}
diff --git a/src/share/classes/java/time/Instant.java b/src/share/classes/java/time/Instant.java
index 6dc246469451213cb14ac4dc2623819861ecc68c..aeecdbed5247becb2137b6e6e3f3603425d3bd29 100644
--- a/src/share/classes/java/time/Instant.java
+++ b/src/share/classes/java/time/Instant.java
@@ -69,6 +69,7 @@ import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.MICRO_OF_SECOND;
import static java.time.temporal.ChronoField.MILLI_OF_SECOND;
import static java.time.temporal.ChronoField.NANO_OF_SECOND;
+import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.NANOS;
import java.io.DataInput;
@@ -418,8 +419,9 @@ public final class Instant
* Checks if the specified field is supported.
*
* This checks if this instant can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -447,6 +449,44 @@ public final class Instant
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ * {@code DAYS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased() || unit == DAYS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -511,7 +551,7 @@ public final class Instant
case MILLI_OF_SECOND: return nanos / 1000_000;
case INSTANT_SECONDS: INSTANT_SECONDS.checkValidIntValue(seconds);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return range(field).checkValidIntValue(field.getFrom(this), field);
}
@@ -548,7 +588,7 @@ public final class Instant
case MILLI_OF_SECOND: return nanos / 1000_000;
case INSTANT_SECONDS: return seconds;
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
@@ -665,7 +705,7 @@ public final class Instant
case NANO_OF_SECOND: return (newValue != nanos ? create(seconds, (int) newValue) : this);
case INSTANT_SECONDS: return (newValue != seconds ? create(newValue, nanos) : this);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@@ -807,7 +847,7 @@ public final class Instant
case HALF_DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY / 2));
case DAYS: return plusSeconds(Math.multiplyExact(amountToAdd, SECONDS_PER_DAY));
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
@@ -1053,14 +1093,14 @@ public final class Instant
* complete units between the two instants.
* The {@code Temporal} passed to this method must be an {@code Instant}.
* For example, the amount in days between two dates can be calculated
- * using {@code startInstant.periodUntil(endInstant, SECONDS)}.
+ * using {@code startInstant.until(endInstant, SECONDS)}.
*
* There are two equivalent ways of using this method.
* The first is to invoke this method.
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, SECONDS);
+ * amount = start.until(end, SECONDS);
* amount = SECONDS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1085,7 +1125,7 @@ public final class Instant
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endInstant, TemporalUnit unit) {
+ public long until(Temporal endInstant, TemporalUnit unit) {
if (endInstant instanceof Instant == false) {
Objects.requireNonNull(endInstant, "endInstant");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -1103,7 +1143,7 @@ public final class Instant
case HALF_DAYS: return secondsUntil(end) / (12 * SECONDS_PER_HOUR);
case DAYS: return secondsUntil(end) / (SECONDS_PER_DAY);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endInstant);
}
diff --git a/src/share/classes/java/time/LocalDate.java b/src/share/classes/java/time/LocalDate.java
index ede6d8326b96e554aa7b94b0176427266c6fddc2..d96f5b1fd1e9fc1606663dd960b4ddb6cd7f84a0 100644
--- a/src/share/classes/java/time/LocalDate.java
+++ b/src/share/classes/java/time/LocalDate.java
@@ -127,7 +127,7 @@ import java.util.Objects;
* @since 1.8
*/
public final class LocalDate
- implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
+ implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {
/**
* The minimum supported {@code LocalDate}, '-999999999-01-01'.
@@ -466,8 +466,9 @@ public final class LocalDate
* Checks if the specified field is supported.
*
* This checks if this date can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -501,6 +502,41 @@ public final class LocalDate
return ChronoLocalDate.super.isSupported(field);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code DAYS}
+ * {@code WEEKS}
+ * {@code MONTHS}
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoLocalDate.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -538,7 +574,7 @@ public final class LocalDate
}
return field.range();
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@@ -631,7 +667,7 @@ public final class LocalDate
case YEAR: return year;
case ERA: return (year >= 1 ? 1 : 0);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
private long getProlepticMonth() {
@@ -988,7 +1024,7 @@ public final class LocalDate
case YEAR: return withYear((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@@ -1187,7 +1223,7 @@ public final class LocalDate
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
@@ -1497,7 +1533,7 @@ public final class LocalDate
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalDate}.
* For example, the amount in days between two dates can be calculated
- * using {@code startDate.periodUntil(endDate, DAYS)}.
+ * using {@code startDate.until(endDate, DAYS)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two dates.
@@ -1509,7 +1545,7 @@ public final class LocalDate
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1534,7 +1570,7 @@ public final class LocalDate
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endDate, TemporalUnit unit) {
+ public long until(Temporal endDate, TemporalUnit unit) {
Objects.requireNonNull(unit, "unit");
if (endDate instanceof LocalDate == false) {
Objects.requireNonNull(endDate, "endDate");
@@ -1552,7 +1588,7 @@ public final class LocalDate
case MILLENNIA: return monthsUntil(end) / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endDate);
}
@@ -1591,7 +1627,7 @@ public final class LocalDate
* The second is to use {@link Period#between(LocalDate, LocalDate)}:
*
* // these two lines are equivalent
- * period = start.periodUntil(end);
+ * period = start.until(end);
* period = Period.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1600,7 +1636,7 @@ public final class LocalDate
* @return the period between this date and the end date, not null
*/
@Override
- public Period periodUntil(ChronoLocalDate> endDate) {
+ public Period until(ChronoLocalDate endDate) {
LocalDate end = LocalDate.from(endDate);
long totalMonths = end.getProlepticMonth() - this.getProlepticMonth(); // safe
int days = end.day - this.day;
@@ -1803,7 +1839,7 @@ public final class LocalDate
* @return the comparator value, negative if less, positive if greater
*/
@Override // override for Javadoc and performance
- public int compareTo(ChronoLocalDate> other) {
+ public int compareTo(ChronoLocalDate other) {
if (other instanceof LocalDate) {
return compareTo0((LocalDate) other);
}
@@ -1843,7 +1879,7 @@ public final class LocalDate
* @return true if this date is after the specified date
*/
@Override // override for Javadoc and performance
- public boolean isAfter(ChronoLocalDate> other) {
+ public boolean isAfter(ChronoLocalDate other) {
if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) > 0;
}
@@ -1872,7 +1908,7 @@ public final class LocalDate
* @return true if this date is before the specified date
*/
@Override // override for Javadoc and performance
- public boolean isBefore(ChronoLocalDate> other) {
+ public boolean isBefore(ChronoLocalDate other) {
if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) < 0;
}
@@ -1901,7 +1937,7 @@ public final class LocalDate
* @return true if this date is equal to the specified date
*/
@Override // override for Javadoc and performance
- public boolean isEqual(ChronoLocalDate> other) {
+ public boolean isEqual(ChronoLocalDate other) {
if (other instanceof LocalDate) {
return compareTo0((LocalDate) other) == 0;
}
diff --git a/src/share/classes/java/time/LocalDateTime.java b/src/share/classes/java/time/LocalDateTime.java
index 6e6c87bc2ff9a077460f60d0a8558df4d41ea368..d68d6f52537d9b517a0e1f419c3bd4edb8ee95fd 100644
--- a/src/share/classes/java/time/LocalDateTime.java
+++ b/src/share/classes/java/time/LocalDateTime.java
@@ -515,8 +515,9 @@ public final class LocalDateTime
* Checks if the specified field is supported.
*
* This checks if this date-time can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -569,6 +570,48 @@ public final class LocalDateTime
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ * {@code DAYS}
+ * {@code WEEKS}
+ * {@code MONTHS}
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoLocalDateTime.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -1570,7 +1613,7 @@ public final class LocalDateTime
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalDateTime}.
* For example, the amount in days between two date-times can be calculated
- * using {@code startDateTime.periodUntil(endDateTime, DAYS)}.
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two date-times.
@@ -1582,7 +1625,7 @@ public final class LocalDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1609,18 +1652,17 @@ public final class LocalDateTime
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof LocalDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
}
LocalDateTime end = (LocalDateTime) endDateTime;
if (unit instanceof ChronoUnit) {
- ChronoUnit f = (ChronoUnit) unit;
- if (f.isTimeUnit()) {
+ if (unit.isTimeBased()) {
long amount = date.daysUntil(end.date);
if (amount == 0) {
- return time.periodUntil(end.time, unit);
+ return time.until(end.time, unit);
}
long timePart = end.time.toNanoOfDay() - time.toNanoOfDay();
if (amount > 0) {
@@ -1630,7 +1672,7 @@ public final class LocalDateTime
amount++; // safe
timePart -= NANOS_PER_DAY; // safe
}
- switch (f) {
+ switch ((ChronoUnit) unit) {
case NANOS:
amount = Math.multiplyExact(amount, NANOS_PER_DAY);
break;
@@ -1667,7 +1709,7 @@ public final class LocalDateTime
} else if (endDate.isBefore(date) && end.time.isAfter(time)) {
endDate = endDate.plusDays(1);
}
- return date.periodUntil(endDate, unit);
+ return date.until(endDate, unit);
}
return unit.between(this, endDateTime);
}
diff --git a/src/share/classes/java/time/LocalTime.java b/src/share/classes/java/time/LocalTime.java
index 41b1a92756009e31c55bab5f25fdc42c19d60a9f..2bace6e7bc9047f36b3da4d61ac818c83ffa53da 100644
--- a/src/share/classes/java/time/LocalTime.java
+++ b/src/share/classes/java/time/LocalTime.java
@@ -470,8 +470,9 @@ public final class LocalTime
* Checks if the specified field is supported.
*
* This checks if this time can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -510,6 +511,43 @@ public final class LocalTime
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -628,7 +666,7 @@ public final class LocalTime
case CLOCK_HOUR_OF_DAY: return (hour == 0 ? 24 : hour);
case AMPM_OF_DAY: return hour / 12;
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
//-----------------------------------------------------------------------
@@ -803,7 +841,7 @@ public final class LocalTime
case CLOCK_HOUR_OF_DAY: return withHour((int) (newValue == 24 ? 0 : newValue));
case AMPM_OF_DAY: return plusHours((newValue - (hour / 12)) * 12);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@@ -995,8 +1033,7 @@ public final class LocalTime
@Override
public LocalTime plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
- ChronoUnit f = (ChronoUnit) unit;
- switch (f) {
+ switch ((ChronoUnit) unit) {
case NANOS: return plusNanos(amountToAdd);
case MICROS: return plusNanos((amountToAdd % MICROS_PER_DAY) * 1000);
case MILLIS: return plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000_000);
@@ -1005,7 +1042,7 @@ public final class LocalTime
case HOURS: return plusHours(amountToAdd);
case HALF_DAYS: return plusHours((amountToAdd % 2) * 12);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
@@ -1295,7 +1332,7 @@ public final class LocalTime
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code LocalTime}.
* For example, the amount in hours between two times can be calculated
- * using {@code startTime.periodUntil(endTime, HOURS)}.
+ * using {@code startTime.until(endTime, HOURS)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two times.
@@ -1307,7 +1344,7 @@ public final class LocalTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MINUTES);
+ * amount = start.until(end, MINUTES);
* amount = MINUTES.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1332,7 +1369,7 @@ public final class LocalTime
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endTime, TemporalUnit unit) {
+ public long until(Temporal endTime, TemporalUnit unit) {
if (endTime instanceof LocalTime == false) {
Objects.requireNonNull(endTime, "endTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -1349,7 +1386,7 @@ public final class LocalTime
case HOURS: return nanosUntil / NANOS_PER_HOUR;
case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endTime);
}
diff --git a/src/share/classes/java/time/Month.java b/src/share/classes/java/time/Month.java
index 272e8f96b360a62373580569900265390b05fcfc..85d12d8f1362e655110e234c8f769d22a6e0fcbc 100644
--- a/src/share/classes/java/time/Month.java
+++ b/src/share/classes/java/time/Month.java
@@ -61,7 +61,6 @@
*/
package java.time;
-import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoUnit.MONTHS;
@@ -75,6 +74,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Locale;
@@ -370,7 +370,7 @@ public enum Month implements TemporalAccessor, TemporalAdjuster {
if (field == MONTH_OF_YEAR) {
return getValue();
} else if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
diff --git a/src/share/classes/java/time/MonthDay.java b/src/share/classes/java/time/MonthDay.java
index 4204ef74a7ddd3247d10c2ec378477ef6129a655..06aa0437af8a94acf16640129a34f4cb997fae6a 100644
--- a/src/share/classes/java/time/MonthDay.java
+++ b/src/share/classes/java/time/MonthDay.java
@@ -438,7 +438,7 @@ public final class MonthDay
case DAY_OF_MONTH: return day;
case MONTH_OF_YEAR: return month;
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
diff --git a/src/share/classes/java/time/OffsetDateTime.java b/src/share/classes/java/time/OffsetDateTime.java
index b053085a9f5d6a9670b82ba8b214dbc3c12903a8..5641154cf3ec44560fac4f311fc5d34fc49dae9d 100644
--- a/src/share/classes/java/time/OffsetDateTime.java
+++ b/src/share/classes/java/time/OffsetDateTime.java
@@ -65,6 +65,7 @@ import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.NANO_OF_DAY;
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import java.io.IOException;
@@ -137,25 +138,40 @@ public final class OffsetDateTime
public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN);
/**
- * Comparator for two {@code OffsetDateTime} instances based solely on the instant.
+ * Gets a comparator that compares two {@code OffsetDateTime} instances
+ * based solely on the instant.
*
* This method differs from the comparison in {@link #compareTo} in that it
* only compares the underlying instant.
*
+ * @return a comparator that compares in time-line order
+ *
* @see #isAfter
* @see #isBefore
* @see #isEqual
*/
- public static final Comparator INSTANT_COMPARATOR = new Comparator() {
- @Override
- public int compare(OffsetDateTime datetime1, OffsetDateTime datetime2) {
- int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
- if (cmp == 0) {
- cmp = Long.compare(datetime1.toLocalTime().toNanoOfDay(), datetime2.toLocalTime().toNanoOfDay());
- }
- return cmp;
+ public static Comparator timeLineOrder() {
+ return OffsetDateTime::compareInstant;
+ }
+
+ /**
+ * Compares this {@code OffsetDateTime} to another date-time.
+ * The comparison is based on the instant.
+ *
+ * @param datetime1 the first date-time to compare, not null
+ * @param datetime2 the other date-time to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ private static int compareInstant(OffsetDateTime datetime1, OffsetDateTime datetime2) {
+ if (datetime1.getOffset().equals(datetime2.getOffset())) {
+ return datetime1.toLocalDateTime().compareTo(datetime2.toLocalDateTime());
+ }
+ int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond());
+ if (cmp == 0) {
+ cmp = datetime1.toLocalTime().getNano() - datetime2.toLocalTime().getNano();
}
- };
+ return cmp;
+ }
/**
* Serialization version.
@@ -406,8 +422,9 @@ public final class OffsetDateTime
* Checks if the specified field is supported.
*
* This checks if this date-time can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -458,6 +475,51 @@ public final class OffsetDateTime
return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ * {@code DAYS}
+ * {@code WEEKS}
+ * {@code MONTHS}
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -1528,7 +1590,7 @@ public final class OffsetDateTime
* The start and end points are {@code this} and the specified date-time.
* The result will be negative if the end is before the start.
* For example, the period in days between two date-times can be calculated
- * using {@code startDateTime.periodUntil(endDateTime, DAYS)}.
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
*
* The {@code Temporal} passed to this method must be an {@code OffsetDateTime}.
* If the offset differs between the two date-times, the specified
@@ -1544,7 +1606,7 @@ public final class OffsetDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1571,7 +1633,7 @@ public final class OffsetDateTime
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof OffsetDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -1579,7 +1641,7 @@ public final class OffsetDateTime
if (unit instanceof ChronoUnit) {
OffsetDateTime end = (OffsetDateTime) endDateTime;
end = end.withOffsetSameInstant(offset);
- return dateTime.periodUntil(end.dateTime, unit);
+ return dateTime.until(end.dateTime, unit);
}
return unit.between(this, endDateTime);
}
@@ -1724,15 +1786,9 @@ public final class OffsetDateTime
*/
@Override
public int compareTo(OffsetDateTime other) {
- if (getOffset().equals(other.getOffset())) {
- return toLocalDateTime().compareTo(other.toLocalDateTime());
- }
- int cmp = Long.compare(toEpochSecond(), other.toEpochSecond());
+ int cmp = compareInstant(this, other);
if (cmp == 0) {
- cmp = toLocalTime().getNano() - other.toLocalTime().getNano();
- if (cmp == 0) {
- cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
- }
+ cmp = toLocalDateTime().compareTo(other.toLocalDateTime());
}
return cmp;
}
diff --git a/src/share/classes/java/time/OffsetTime.java b/src/share/classes/java/time/OffsetTime.java
index ff990696942dffef8eca4b9080d11c4f37740c68..2872cff4b265aae3d7d4d6adbece3169d4cc57de 100644
--- a/src/share/classes/java/time/OffsetTime.java
+++ b/src/share/classes/java/time/OffsetTime.java
@@ -348,8 +348,9 @@ public final class OffsetTime
* Checks if the specified field is supported.
*
* This checks if this time can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -389,6 +390,43 @@ public final class OffsetTime
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isTimeBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -1084,7 +1122,7 @@ public final class OffsetTime
* The start and end points are {@code this} and the specified time.
* The result will be negative if the end is before the start.
* For example, the period in hours between two times can be calculated
- * using {@code startTime.periodUntil(endTime, HOURS)}.
+ * using {@code startTime.until(endTime, HOURS)}.
*
* The {@code Temporal} passed to this method must be an {@code OffsetTime}.
* If the offset differs between the two times, then the specified
@@ -1100,7 +1138,7 @@ public final class OffsetTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MINUTES);
+ * amount = start.until(end, MINUTES);
* amount = MINUTES.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -1125,7 +1163,7 @@ public final class OffsetTime
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endTime, TemporalUnit unit) {
+ public long until(Temporal endTime, TemporalUnit unit) {
if (endTime instanceof OffsetTime == false) {
Objects.requireNonNull(endTime, "endTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -1142,7 +1180,7 @@ public final class OffsetTime
case HOURS: return nanosUntil / NANOS_PER_HOUR;
case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endTime);
}
diff --git a/src/share/classes/java/time/Period.java b/src/share/classes/java/time/Period.java
index b2748f089ce8826a991ca14e928cc7710651d955..45980d08740a4ee260d67adce2cf8a1a2dd1689f 100644
--- a/src/share/classes/java/time/Period.java
+++ b/src/share/classes/java/time/Period.java
@@ -139,7 +139,7 @@ public final class Period
* The pattern for parsing.
*/
private final static Pattern PATTERN =
- Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE);
/**
* The set of supported units.
*/
@@ -186,6 +186,20 @@ public final class Period
return create(0, months, 0);
}
+ /**
+ * Obtains a {@code Period} representing a number of weeks.
+ *
+ * The resulting period will be day-based, with the amount of days
+ * equal to the number of weeks multiplied by 7.
+ * The years and months units will be zero.
+ *
+ * @param weeks the number of weeks, positive or negative
+ * @return the period, with the input weeks converted to days, not null
+ */
+ public static Period ofWeeks(int weeks) {
+ return create(0, 0, Math.multiplyExact(weeks, 7));
+ }
+
/**
* Obtains a {@code Period} representing a number of days.
*
@@ -257,22 +271,36 @@ public final class Period
* Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
*
* This will parse the string produced by {@code toString()} which is
- * based on the ISO-8601 period format {@code PnYnMnD}.
+ * based on the ISO-8601 period formats {@code PnYnMnD} and {@code PnW}.
*
* The string starts with an optional sign, denoted by the ASCII negative
* or positive symbol. If negative, the whole period is negated.
* The ASCII letter "P" is next in upper or lower case.
- * There are then three sections, each consisting of a number and a suffix.
- * At least one of the three sections must be present.
- * The sections have suffixes in ASCII of "Y", "M" and "D" for
- * years, months and days, accepted in upper or lower case.
+ * There are then four sections, each consisting of a number and a suffix.
+ * At least one of the four sections must be present.
+ * The sections have suffixes in ASCII of "Y", "M", "W" and "D" for
+ * years, months, weeks and days, accepted in upper or lower case.
* The suffixes must occur in order.
* The number part of each section must consist of ASCII digits.
* The number may be prefixed by the ASCII negative or positive symbol.
* The number must parse to an {@code int}.
*
* The leading plus/minus sign, and negative values for other units are
- * not part of the ISO-8601 standard.
+ * not part of the ISO-8601 standard. In addition, ISO-8601 does not
+ * permit mixing between the {@code PnYnMnD} and {@code PnW} formats.
+ * Any week-based input is multiplied by 7 and treated as a number of days.
+ *
+ * For example, the following are valid inputs:
+ *
+ * "P2Y" -- Period.ofYears(2)
+ * "P3M" -- Period.ofMonths(3)
+ * "P4W" -- Period.ofWeeks(4)
+ * "P5D" -- Period.ofDays(5)
+ * "P1Y2M3D" -- Period.of(1, 2, 3)
+ * "P1Y2M3W4D" -- Period.of(1, 2, 25)
+ * "P-1Y2M" -- Period.of(-1, 2, 0)
+ * "-P1Y2M" -- Period.of(-1, -2, 0)
+ *
*
* @param text the text to parse, not null
* @return the parsed period, not null
@@ -285,14 +313,18 @@ public final class Period
int negate = ("-".equals(matcher.group(1)) ? -1 : 1);
String yearMatch = matcher.group(2);
String monthMatch = matcher.group(3);
- String dayMatch = matcher.group(4);
- if (yearMatch != null || monthMatch != null || dayMatch != null) {
+ String weekMatch = matcher.group(4);
+ String dayMatch = matcher.group(5);
+ if (yearMatch != null || monthMatch != null || dayMatch != null || weekMatch != null) {
try {
- return create(parseNumber(text, yearMatch, negate),
- parseNumber(text, monthMatch, negate),
- parseNumber(text, dayMatch, negate));
+ int years = parseNumber(text, yearMatch, negate);
+ int months = parseNumber(text, monthMatch, negate);
+ int weeks = parseNumber(text, weekMatch, negate);
+ int days = parseNumber(text, dayMatch, negate);
+ days = Math.addExact(days, Math.multiplyExact(weeks, 7));
+ return create(years, months, days);
} catch (NumberFormatException ex) {
- throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
+ throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
}
}
}
@@ -307,7 +339,7 @@ public final class Period
try {
return Math.multiplyExact(val, negate);
} catch (ArithmeticException ex) {
- throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
+ throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
}
}
@@ -329,10 +361,10 @@ public final class Period
* @param startDate the start date, inclusive, not null
* @param endDate the end date, exclusive, not null
* @return the period between this date and the end date, not null
- * @see ChronoLocalDate#periodUntil(ChronoLocalDate)
+ * @see ChronoLocalDate#until(ChronoLocalDate)
*/
public static Period between(LocalDate startDate, LocalDate endDate) {
- return startDate.periodUntil(endDate);
+ return startDate.until(endDate);
}
//-----------------------------------------------------------------------
@@ -386,7 +418,7 @@ public final class Period
} else if (unit == ChronoUnit.DAYS) {
return getDays();
} else {
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
}
diff --git a/src/share/classes/java/time/Year.java b/src/share/classes/java/time/Year.java
index c2d9974d79ae6c21420df33a4240027ec84e1294..fb180d859cbee2265e6c2eb7feee3e34917a5f91 100644
--- a/src/share/classes/java/time/Year.java
+++ b/src/share/classes/java/time/Year.java
@@ -64,6 +64,10 @@ package java.time;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.CENTURIES;
+import static java.time.temporal.ChronoUnit.DECADES;
+import static java.time.temporal.ChronoUnit.ERAS;
+import static java.time.temporal.ChronoUnit.MILLENNIA;
import static java.time.temporal.ChronoUnit.YEARS;
import java.io.DataInput;
@@ -329,8 +333,9 @@ public final class Year
* Checks if the specified field is supported.
*
* This checks if this year can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -357,6 +362,41 @@ public final class Year
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -450,7 +490,7 @@ public final class Year
case YEAR: return year;
case ERA: return (year < 1 ? 0 : 1);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
@@ -575,7 +615,7 @@ public final class Year
case YEAR: return Year.of((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : Year.of(1 - year));
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@@ -664,7 +704,7 @@ public final class Year
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
@@ -821,7 +861,7 @@ public final class Year
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code Year}.
* For example, the period in decades between two year can be calculated
- * using {@code startYear.periodUntil(endYear, DECADES)}.
+ * using {@code startYear.until(endYear, DECADES)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two years.
@@ -833,7 +873,7 @@ public final class Year
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, YEARS);
+ * amount = start.until(end, YEARS);
* amount = YEARS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -858,7 +898,7 @@ public final class Year
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endYear, TemporalUnit unit) {
+ public long until(Temporal endYear, TemporalUnit unit) {
if (endYear instanceof Year == false) {
Objects.requireNonNull(endYear, "endYear");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -873,7 +913,7 @@ public final class Year
case MILLENNIA: return yearsUntil / 1000;
case ERAS: return end.getLong(ERA) - getLong(ERA);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endYear);
}
diff --git a/src/share/classes/java/time/YearMonth.java b/src/share/classes/java/time/YearMonth.java
index 855774eed1a77b41891ee5b244e9d570fe609dbd..1d974095336124d6c9e1daf949d573a102cbe652 100644
--- a/src/share/classes/java/time/YearMonth.java
+++ b/src/share/classes/java/time/YearMonth.java
@@ -66,7 +66,12 @@ import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.CENTURIES;
+import static java.time.temporal.ChronoUnit.DECADES;
+import static java.time.temporal.ChronoUnit.ERAS;
+import static java.time.temporal.ChronoUnit.MILLENNIA;
import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.YEARS;
import java.io.DataInput;
import java.io.DataOutput;
@@ -313,8 +318,9 @@ public final class YearMonth
* Checks if the specified field is supported.
*
* This checks if this year-month can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -344,6 +350,42 @@ public final class YearMonth
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code MONTHS}
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ public boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit == MONTHS || unit == YEARS || unit == DECADES || unit == CENTURIES || unit == MILLENNIA || unit == ERAS;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -440,7 +482,7 @@ public final class YearMonth
case YEAR: return year;
case ERA: return (year < 1 ? 0 : 1);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
@@ -639,7 +681,7 @@ public final class YearMonth
case YEAR: return withYear((int) newValue);
case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year));
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.adjustInto(this, newValue);
}
@@ -761,7 +803,7 @@ public final class YearMonth
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.addTo(this, amountToAdd);
}
@@ -952,7 +994,7 @@ public final class YearMonth
* The result will be negative if the end is before the start.
* The {@code Temporal} passed to this method must be a {@code YearMonth}.
* For example, the period in years between two year-months can be calculated
- * using {@code startYearMonth.periodUntil(endYearMonth, YEARS)}.
+ * using {@code startYearMonth.until(endYearMonth, YEARS)}.
*
* The calculation returns a whole number, representing the number of
* complete units between the two year-months.
@@ -964,7 +1006,7 @@ public final class YearMonth
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -989,7 +1031,7 @@ public final class YearMonth
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endYearMonth, TemporalUnit unit) {
+ public long until(Temporal endYearMonth, TemporalUnit unit) {
if (endYearMonth instanceof YearMonth == false) {
Objects.requireNonNull(endYearMonth, "endYearMonth");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -1005,7 +1047,7 @@ public final class YearMonth
case MILLENNIA: return monthsUntil / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endYearMonth);
}
diff --git a/src/share/classes/java/time/ZoneId.java b/src/share/classes/java/time/ZoneId.java
index 026f73ef5125a00ac357996970598bc39c95a113..dcde85ae884b9f9fb14d16a3d4845f30882663eb 100644
--- a/src/share/classes/java/time/ZoneId.java
+++ b/src/share/classes/java/time/ZoneId.java
@@ -400,6 +400,36 @@ public abstract class ZoneId implements Serializable {
return of(zoneId, true);
}
+ /**
+ * Obtains an instance of {@code ZoneId} wrapping an offset.
+ *
+ * If the prefix is "GMT", "UTC", or "UT" a {@code ZoneId}
+ * with the prefix and the non-zero offset is returned.
+ * If the prefix is empty {@code ""} the {@code ZoneOffset} is returned.
+ *
+ * @param prefix the time-zone ID, not null
+ * @param offset the offset, not null
+ * @return the zone ID, not null
+ * @throws IllegalArgumentException if the prefix is not one of
+ * "GMT", "UTC", or "UT", or ""
+ */
+ public static ZoneId ofOffset(String prefix, ZoneOffset offset) {
+ Objects.requireNonNull(prefix, "prefix");
+ Objects.requireNonNull(offset, "offset");
+ if (prefix.length() == 0) {
+ return offset;
+ }
+
+ if (!prefix.equals("GMT") && !prefix.equals("UTC") && !prefix.equals("UT")) {
+ throw new IllegalArgumentException("prefix should be GMT, UTC or UT, is: " + prefix);
+ }
+
+ if (offset.getTotalSeconds() != 0) {
+ prefix = prefix.concat(offset.getId());
+ }
+ return new ZoneRegion(prefix, offset.getRules());
+ }
+
/**
* Parses the ID, taking a flag to indicate whether {@code ZoneRulesException}
* should be thrown or not, used in deserialization.
@@ -433,7 +463,7 @@ public abstract class ZoneId implements Serializable {
private static ZoneId ofWithPrefix(String zoneId, int prefixLength, boolean checkAvailable) {
String prefix = zoneId.substring(0, prefixLength);
if (zoneId.length() == prefixLength) {
- return ZoneRegion.ofPrefixedOffset(prefix, ZoneOffset.UTC);
+ return ofOffset(prefix, ZoneOffset.UTC);
}
if (zoneId.charAt(prefixLength) != '+' && zoneId.charAt(prefixLength) != '-') {
return ZoneRegion.ofId(zoneId, checkAvailable); // drop through to ZoneRulesProvider
@@ -441,9 +471,9 @@ public abstract class ZoneId implements Serializable {
try {
ZoneOffset offset = ZoneOffset.of(zoneId.substring(prefixLength));
if (offset == ZoneOffset.UTC) {
- return ZoneRegion.ofPrefixedOffset(prefix, offset);
+ return ofOffset(prefix, offset);
}
- return ZoneRegion.ofPrefixedOffset(prefix + offset.toString(), offset);
+ return ofOffset(prefix, offset);
} catch (DateTimeException ex) {
throw new DateTimeException("Invalid ID for offset-based ZoneId: " + zoneId, ex);
}
diff --git a/src/share/classes/java/time/ZoneOffset.java b/src/share/classes/java/time/ZoneOffset.java
index 4bbc4e4fa84fa5f74991d8e3e8718335c56ddce6..c5e4d056ee82528891af0051ee390fc64448ac90 100644
--- a/src/share/classes/java/time/ZoneOffset.java
+++ b/src/share/classes/java/time/ZoneOffset.java
@@ -61,7 +61,6 @@
*/
package java.time;
-import java.time.temporal.UnsupportedTemporalTypeException;
import static java.time.LocalTime.MINUTES_PER_HOUR;
import static java.time.LocalTime.SECONDS_PER_HOUR;
import static java.time.LocalTime.SECONDS_PER_MINUTE;
@@ -79,6 +78,7 @@ import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
+import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.time.zone.ZoneRules;
import java.util.Objects;
@@ -581,7 +581,7 @@ public final class ZoneOffset
if (field == OFFSET_SECONDS) {
return totalSeconds;
} else if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return range(field).checkValidIntValue(getLong(field), field);
}
@@ -613,7 +613,7 @@ public final class ZoneOffset
if (field == OFFSET_SECONDS) {
return totalSeconds;
} else if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
diff --git a/src/share/classes/java/time/ZoneRegion.java b/src/share/classes/java/time/ZoneRegion.java
index af6a5405a69e57245c70468f6de9a5bbe557dbdf..66d30709d98a75ffa7084246326cb3124f271782 100644
--- a/src/share/classes/java/time/ZoneRegion.java
+++ b/src/share/classes/java/time/ZoneRegion.java
@@ -66,7 +66,6 @@ import java.time.zone.ZoneRules;
import java.time.zone.ZoneRulesException;
import java.time.zone.ZoneRulesProvider;
import java.util.Objects;
-import java.util.regex.Pattern;
/**
* A geographical region where the same time-zone rules apply.
@@ -153,19 +152,6 @@ final class ZoneRegion extends ZoneId implements Serializable {
}
}
- /**
- * Obtains an instance of {@code ZoneId} wrapping an offset.
- *
- * For example, zone IDs like 'UTC', 'GMT', 'UT' and 'UTC+01:30' will be setup here.
- *
- * @param zoneId the time-zone ID, not null
- * @param offset the offset, not null
- * @return the zone ID, not null
- */
- static ZoneRegion ofPrefixedOffset(String zoneId, ZoneOffset offset) {
- return new ZoneRegion(zoneId, offset.getRules());
- }
-
//-------------------------------------------------------------------------
/**
* Constructor.
diff --git a/src/share/classes/java/time/ZonedDateTime.java b/src/share/classes/java/time/ZonedDateTime.java
index e7ed5551d33adcee26319fbe7325b3871b96ba95..151470ecf939903b5c480ea9a01692988867e3ee 100644
--- a/src/share/classes/java/time/ZonedDateTime.java
+++ b/src/share/classes/java/time/ZonedDateTime.java
@@ -642,8 +642,9 @@ public final class ZonedDateTime
* Checks if the specified field is supported.
*
* This checks if this date-time can be queried for the specified field.
- * If false, then calling the {@link #range(TemporalField) range} and
- * {@link #get(TemporalField) get} methods will throw an exception.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
*
* If the field is a {@link ChronoField} then the query is implemented here.
* The supported fields are:
@@ -694,6 +695,48 @@ public final class ZonedDateTime
return field instanceof ChronoField || (field != null && field.isSupportedBy(this));
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to, or subtracted from, this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * If the unit is a {@link ChronoUnit} then the query is implemented here.
+ * The supported units are:
+ *
+ * {@code NANOS}
+ * {@code MICROS}
+ * {@code MILLIS}
+ * {@code SECONDS}
+ * {@code MINUTES}
+ * {@code HOURS}
+ * {@code HALF_DAYS}
+ * {@code DAYS}
+ * {@code WEEKS}
+ * {@code MONTHS}
+ * {@code YEARS}
+ * {@code DECADES}
+ * {@code CENTURIES}
+ * {@code MILLENNIA}
+ * {@code ERAS}
+ *
+ * All other {@code ChronoUnit} instances will return false.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override // override for Javadoc
+ public boolean isSupported(TemporalUnit unit) {
+ return ChronoZonedDateTime.super.isSupported(unit);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Gets the range of valid values for the specified field.
*
@@ -1540,8 +1583,7 @@ public final class ZonedDateTime
@Override
public ZonedDateTime plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
- ChronoUnit u = (ChronoUnit) unit;
- if (u.isDateUnit()) {
+ if (unit.isDateBased()) {
return resolveLocal(dateTime.plus(amountToAdd, unit));
} else {
return resolveInstant(dateTime.plus(amountToAdd, unit));
@@ -1990,7 +2032,7 @@ public final class ZonedDateTime
* The start and end points are {@code this} and the specified date-time.
* The result will be negative if the end is before the start.
* For example, the period in days between two date-times can be calculated
- * using {@code startDateTime.periodUntil(endDateTime, DAYS)}.
+ * using {@code startDateTime.until(endDateTime, DAYS)}.
*
* The {@code Temporal} passed to this method must be a {@code ZonedDateTime}.
* If the time-zone differs between the two zoned date-times, the specified
@@ -2006,7 +2048,7 @@ public final class ZonedDateTime
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -2047,7 +2089,7 @@ public final class ZonedDateTime
* @throws ArithmeticException if numeric overflow occurs
*/
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ZonedDateTime == false) {
Objects.requireNonNull(endDateTime, "endDateTime");
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
@@ -2055,11 +2097,10 @@ public final class ZonedDateTime
if (unit instanceof ChronoUnit) {
ZonedDateTime end = (ZonedDateTime) endDateTime;
end = end.withZoneSameInstant(zone);
- ChronoUnit u = (ChronoUnit) unit;
- if (u.isDateUnit()) {
- return dateTime.periodUntil(end.dateTime, unit);
+ if (unit.isDateBased()) {
+ return dateTime.until(end.dateTime, unit);
} else {
- return toOffsetDateTime().periodUntil(end.toOffsetDateTime(), unit);
+ return toOffsetDateTime().until(end.toOffsetDateTime(), unit);
}
}
return unit.between(this, endDateTime);
diff --git a/src/share/classes/java/time/chrono/ChronoDateImpl.java b/src/share/classes/java/time/chrono/ChronoDateImpl.java
index 14e2bf58f2a7fe53d710c9c13171dea1e04735d4..99ba58f05b9a8dae2c84a789d3eef569bb1ad8bb 100644
--- a/src/share/classes/java/time/chrono/ChronoDateImpl.java
+++ b/src/share/classes/java/time/chrono/ChronoDateImpl.java
@@ -67,6 +67,8 @@ import java.time.DateTimeException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
@@ -96,12 +98,12 @@ import java.util.Objects;
* // Enumerate the list of available calendars and print today for each
* Set<Chronology> chronos = Chronology.getAvailableChronologies();
* for (Chronology chrono : chronos) {
- * ChronoLocalDate<?> date = chrono.dateNow();
+ * ChronoLocalDate date = chrono.dateNow();
* System.out.printf(" %20s: %s%n", chrono.getID(), date.toString());
* }
*
* // Print the Hijrah date and calendar
- * ChronoLocalDate<?> date = Chronology.of("Hijrah").dateNow();
+ * ChronoLocalDate date = Chronology.of("Hijrah").dateNow();
* int day = date.get(ChronoField.DAY_OF_MONTH);
* int dow = date.get(ChronoField.DAY_OF_WEEK);
* int month = date.get(ChronoField.MONTH_OF_YEAR);
@@ -110,10 +112,10 @@ import java.util.Objects;
* dow, day, month, year);
* // Print today's date and the last day of the year
- * ChronoLocalDate<?> now1 = Chronology.of("Hijrah").dateNow();
- * ChronoLocalDate<?> first = now1.with(ChronoField.DAY_OF_MONTH, 1)
+ * ChronoLocalDate now1 = Chronology.of("Hijrah").dateNow();
+ * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1)
* .with(ChronoField.MONTH_OF_YEAR, 1);
- * ChronoLocalDate<?> last = first.plus(1, ChronoUnit.YEARS)
+ * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS)
* .minus(1, ChronoUnit.DAYS);
* System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChronology().getID(),
* first, last);
@@ -138,22 +140,61 @@ import java.util.Objects;
* @param the ChronoLocalDate of this date-time
* @since 1.8
*/
-abstract class ChronoDateImpl>
- implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
+abstract class ChronoDateImpl
+ implements ChronoLocalDate, Temporal, TemporalAdjuster, Serializable {
/**
* Serialization version.
*/
private static final long serialVersionUID = 6282433883239719096L;
+ /**
+ * Casts the {@code Temporal} to {@code ChronoLocalDate} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoLocalDate}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
+ * or the chronology is not equal this Chronology
+ */
+ static D ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ D other = (D) temporal;
+ if (chrono.equals(other.getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, expected: " + chrono.getId() + ", actual: " + other.getChronology().getId());
+ }
+ return other;
+ }
+
+ //-----------------------------------------------------------------------
/**
* Creates an instance.
*/
ChronoDateImpl() {
}
+ @Override
+ @SuppressWarnings("unchecked")
+ public D with(TemporalAdjuster adjuster) {
+ return (D) ChronoLocalDate.super.with(adjuster);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D with(TemporalField field, long value) {
+ return (D) ChronoLocalDate.super.with(field, value);
+ }
+
+ //-----------------------------------------------------------------------
+ @Override
+ @SuppressWarnings("unchecked")
+ public D plus(TemporalAmount amount) {
+ return (D) ChronoLocalDate.super.plus(amount);
+ }
+
//-----------------------------------------------------------------------
@Override
+ @SuppressWarnings("unchecked")
public D plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
ChronoUnit f = (ChronoUnit) unit;
@@ -167,9 +208,21 @@ abstract class ChronoDateImpl>
case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000));
case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd));
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
- return ChronoLocalDate.super.plus(amountToAdd, unit);
+ return (D) ChronoLocalDate.super.plus(amountToAdd, unit);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D minus(TemporalAmount amount) {
+ return (D) ChronoLocalDate.super.minus(amount);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public D minus(long amountToSubtract, TemporalUnit unit) {
+ return (D) ChronoLocalDate.super.minus(amountToSubtract, unit);
}
//-----------------------------------------------------------------------
@@ -254,6 +307,7 @@ abstract class ChronoDateImpl>
* @return a date based on this one with the years subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
+ @SuppressWarnings("unchecked")
D minusYears(long yearsToSubtract) {
return (yearsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl)plusYears(Long.MAX_VALUE)).plusYears(1) : plusYears(-yearsToSubtract));
}
@@ -274,6 +328,7 @@ abstract class ChronoDateImpl>
* @return a date based on this one with the months subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
+ @SuppressWarnings("unchecked")
D minusMonths(long monthsToSubtract) {
return (monthsToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl)plusMonths(Long.MAX_VALUE)).plusMonths(1) : plusMonths(-monthsToSubtract));
}
@@ -293,6 +348,7 @@ abstract class ChronoDateImpl>
* @return a date based on this one with the weeks subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
+ @SuppressWarnings("unchecked")
D minusWeeks(long weeksToSubtract) {
return (weeksToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl)plusWeeks(Long.MAX_VALUE)).plusWeeks(1) : plusWeeks(-weeksToSubtract));
}
@@ -310,6 +366,7 @@ abstract class ChronoDateImpl>
* @return a date based on this one with the days subtracted, not null
* @throws DateTimeException if the result exceeds the supported date range
*/
+ @SuppressWarnings("unchecked")
D minusDays(long daysToSubtract) {
return (daysToSubtract == Long.MIN_VALUE ? ((ChronoDateImpl)plusDays(Long.MAX_VALUE)).plusDays(1) : plusDays(-daysToSubtract));
}
@@ -321,13 +378,13 @@ abstract class ChronoDateImpl>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
Objects.requireNonNull(endDateTime, "endDateTime");
Objects.requireNonNull(unit, "unit");
if (endDateTime instanceof ChronoLocalDate == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
}
- ChronoLocalDate> end = (ChronoLocalDate>) endDateTime;
+ ChronoLocalDate end = (ChronoLocalDate) endDateTime;
if (getChronology().equals(end.getChronology()) == false) {
throw new DateTimeException("Unable to calculate amount as objects have different chronologies");
}
@@ -342,16 +399,16 @@ abstract class ChronoDateImpl>
case MILLENNIA: return monthsUntil(end) / 12000;
case ERAS: return end.getLong(ERA) - getLong(ERA);
}
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
return unit.between(this, endDateTime);
}
- private long daysUntil(ChronoLocalDate> end) {
+ private long daysUntil(ChronoLocalDate end) {
return end.toEpochDay() - toEpochDay(); // no overflow
}
- private long monthsUntil(ChronoLocalDate> end) {
+ private long monthsUntil(ChronoLocalDate end) {
ValueRange range = getChronology().range(MONTH_OF_YEAR);
if (range.getMaximum() != 12) {
throw new IllegalStateException("ChronoDateImpl only supports Chronologies with 12 months per year");
@@ -367,7 +424,7 @@ abstract class ChronoDateImpl>
return true;
}
if (obj instanceof ChronoLocalDate) {
- return compareTo((ChronoLocalDate>) obj) == 0;
+ return compareTo((ChronoLocalDate) obj) == 0;
}
return false;
}
diff --git a/src/share/classes/java/time/chrono/ChronoLocalDate.java b/src/share/classes/java/time/chrono/ChronoLocalDate.java
index 31c08260d6e5f3a750095b8db63d6d69572b277a..923e8960b0f219fab34cec2a635673a31e693403 100644
--- a/src/share/classes/java/time/chrono/ChronoLocalDate.java
+++ b/src/share/classes/java/time/chrono/ChronoLocalDate.java
@@ -242,11 +242,10 @@ import java.util.Objects;
* Additional calendar systems may be added to the system.
* See {@link Chronology} for more details.
*
- * @param the concrete type for the date
* @since 1.8
*/
-public interface ChronoLocalDate>
- extends Temporal, TemporalAdjuster, Comparable> {
+public interface ChronoLocalDate
+ extends Temporal, TemporalAdjuster, Comparable {
/**
* Gets a comparator that compares {@code ChronoLocalDate} in
@@ -263,7 +262,7 @@ public interface ChronoLocalDate>
* @see #isBefore
* @see #isEqual
*/
- static Comparator> timeLineOrder() {
+ static Comparator timeLineOrder() {
return Chronology.DATE_ORDER;
}
@@ -289,9 +288,9 @@ public interface ChronoLocalDate>
* @throws DateTimeException if unable to convert to a {@code ChronoLocalDate}
* @see Chronology#date(TemporalAccessor)
*/
- static ChronoLocalDate> from(TemporalAccessor temporal) {
+ static ChronoLocalDate from(TemporalAccessor temporal) {
if (temporal instanceof ChronoLocalDate) {
- return (ChronoLocalDate>) temporal;
+ return (ChronoLocalDate) temporal;
}
Chronology chrono = temporal.query(TemporalQuery.chronology());
if (chrono == null) {
@@ -367,6 +366,25 @@ public interface ChronoLocalDate>
return (isLeapYear() ? 366 : 365);
}
+ /**
+ * Checks if the specified field is supported.
+ *
+ * This checks if the specified field can be queried on this date.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ *
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} date fields.
+ *
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
@Override
default boolean isSupported(TemporalField field) {
if (field instanceof ChronoField) {
@@ -375,6 +393,32 @@ public interface ChronoLocalDate>
return field != null && field.isSupportedBy(this);
}
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to or subtracted from this date.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} date units except {@code FOREVER}.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit.isDateBased();
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
//-----------------------------------------------------------------------
// override for covariant return type
/**
@@ -383,8 +427,8 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D with(TemporalAdjuster adjuster) {
- return (D) getChronology().ensureChronoLocalDate(Temporal.super.with(adjuster));
+ default ChronoLocalDate with(TemporalAdjuster adjuster) {
+ return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.with(adjuster));
}
/**
@@ -394,11 +438,11 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D with(TemporalField field, long newValue) {
+ default ChronoLocalDate with(TemporalField field, long newValue) {
if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
- return (D) getChronology().ensureChronoLocalDate(field.adjustInto(this, newValue));
+ return ChronoDateImpl.ensureValid(getChronology(), field.adjustInto(this, newValue));
}
/**
@@ -407,8 +451,8 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D plus(TemporalAmount amount) {
- return (D) getChronology().ensureChronoLocalDate(Temporal.super.plus(amount));
+ default ChronoLocalDate plus(TemporalAmount amount) {
+ return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.plus(amount));
}
/**
@@ -417,11 +461,11 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D plus(long amountToAdd, TemporalUnit unit) {
+ default ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) {
if (unit instanceof ChronoUnit) {
- throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
}
- return (D) getChronology().ensureChronoLocalDate(unit.addTo(this, amountToAdd));
+ return ChronoDateImpl.ensureValid(getChronology(), unit.addTo(this, amountToAdd));
}
/**
@@ -430,8 +474,8 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D minus(TemporalAmount amount) {
- return (D) getChronology().ensureChronoLocalDate(Temporal.super.minus(amount));
+ default ChronoLocalDate minus(TemporalAmount amount) {
+ return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amount));
}
/**
@@ -441,8 +485,8 @@ public interface ChronoLocalDate>
* @throws ArithmeticException {@inheritDoc}
*/
@Override
- default D minus(long amountToSubtract, TemporalUnit unit) {
- return (D) getChronology().ensureChronoLocalDate(Temporal.super.minus(amountToSubtract, unit));
+ default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) {
+ return ChronoDateImpl.ensureValid(getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
@@ -522,14 +566,14 @@ public interface ChronoLocalDate>
* The calculation returns a whole number, representing the number of
* complete units between the two dates.
* For example, the amount in days between two dates can be calculated
- * using {@code startDate.periodUntil(endDate, DAYS)}.
+ * using {@code startDate.until(endDate, DAYS)}.
*
* There are two equivalent ways of using this method.
* The first is to invoke this method.
* The second is to use {@link TemporalUnit#between(Temporal, Temporal)}:
*
* // these two lines are equivalent
- * amount = start.periodUntil(end, MONTHS);
+ * amount = start.until(end, MONTHS);
* amount = MONTHS.between(start, end);
*
* The choice should be made based on which makes the code more readable.
@@ -555,7 +599,7 @@ public interface ChronoLocalDate>
* @throws ArithmeticException if numeric overflow occurs
*/
@Override // override for Javadoc
- long periodUntil(Temporal endDate, TemporalUnit unit);
+ long until(Temporal endDate, TemporalUnit unit);
/**
* Calculates the period between this date and another date as a {@code Period}.
@@ -575,7 +619,7 @@ public interface ChronoLocalDate>
* @throws DateTimeException if the period cannot be calculated
* @throws ArithmeticException if numeric overflow occurs
*/
- Period periodUntil(ChronoLocalDate> endDate);
+ Period until(ChronoLocalDate endDate);
/**
* Formats this date using the specified formatter.
@@ -606,8 +650,9 @@ public interface ChronoLocalDate>
* @param localTime the local time to use, not null
* @return the local date-time formed from this date and the specified time, not null
*/
- default ChronoLocalDateTime atTime(LocalTime localTime) {
- return (ChronoLocalDateTime)ChronoLocalDateTimeImpl.of(this, localTime);
+ @SuppressWarnings("unchecked")
+ default ChronoLocalDateTime> atTime(LocalTime localTime) {
+ return ChronoLocalDateTimeImpl.of(this, localTime);
}
//-----------------------------------------------------------------------
@@ -656,7 +701,7 @@ public interface ChronoLocalDate>
* @return the comparator value, negative if less, positive if greater
*/
@Override
- default int compareTo(ChronoLocalDate> other) {
+ default int compareTo(ChronoLocalDate other) {
int cmp = Long.compare(toEpochDay(), other.toEpochDay());
if (cmp == 0) {
cmp = getChronology().compareTo(other.getChronology());
@@ -678,7 +723,7 @@ public interface ChronoLocalDate>
* @param other the other date to compare to, not null
* @return true if this is after the specified date
*/
- default boolean isAfter(ChronoLocalDate> other) {
+ default boolean isAfter(ChronoLocalDate other) {
return this.toEpochDay() > other.toEpochDay();
}
@@ -696,7 +741,7 @@ public interface ChronoLocalDate>
* @param other the other date to compare to, not null
* @return true if this is before the specified date
*/
- default boolean isBefore(ChronoLocalDate> other) {
+ default boolean isBefore(ChronoLocalDate other) {
return this.toEpochDay() < other.toEpochDay();
}
@@ -714,7 +759,7 @@ public interface ChronoLocalDate>
* @param other the other date to compare to, not null
* @return true if the underlying date is equal to the specified date
*/
- default boolean isEqual(ChronoLocalDate> other) {
+ default boolean isEqual(ChronoLocalDate other) {
return this.toEpochDay() == other.toEpochDay();
}
diff --git a/src/share/classes/java/time/chrono/ChronoLocalDateTime.java b/src/share/classes/java/time/chrono/ChronoLocalDateTime.java
index f0a893a4c9523de658d3578f3aa8038ca9fde9eb..be93f0e036a0ab494b4c56a2208bd7fde68125c8 100644
--- a/src/share/classes/java/time/chrono/ChronoLocalDateTime.java
+++ b/src/share/classes/java/time/chrono/ChronoLocalDateTime.java
@@ -63,6 +63,7 @@ package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.NANO_OF_DAY;
+import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import java.time.DateTimeException;
@@ -73,6 +74,7 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
@@ -114,7 +116,7 @@ import java.util.Objects;
* @param the concrete type for the date of this date-time
* @since 1.8
*/
-public interface ChronoLocalDateTime>
+public interface ChronoLocalDateTime
extends Temporal, TemporalAdjuster, Comparable> {
/**
@@ -191,9 +193,54 @@ public interface ChronoLocalDateTime>
*/
LocalTime toLocalTime();
- @Override // Override to provide javadoc
+ /**
+ * Checks if the specified field is supported.
+ *
+ * This checks if the specified field can be queried on this date-time.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ *
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} date and time fields.
+ *
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
+ @Override
boolean isSupported(TemporalField field);
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to or subtracted from this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} units except {@code FOREVER}.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
//-----------------------------------------------------------------------
// override for covariant return type
/**
@@ -203,7 +250,7 @@ public interface ChronoLocalDateTime>
*/
@Override
default ChronoLocalDateTime with(TemporalAdjuster adjuster) {
- return (ChronoLocalDateTime)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.with(adjuster)));
+ return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster));
}
/**
@@ -221,7 +268,7 @@ public interface ChronoLocalDateTime>
*/
@Override
default ChronoLocalDateTime plus(TemporalAmount amount) {
- return (ChronoLocalDateTime)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.plus(amount)));
+ return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount));
}
/**
@@ -239,7 +286,7 @@ public interface ChronoLocalDateTime>
*/
@Override
default ChronoLocalDateTime minus(TemporalAmount amount) {
- return (ChronoLocalDateTime)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.minus(amount)));
+ return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount));
}
/**
@@ -249,7 +296,7 @@ public interface ChronoLocalDateTime>
*/
@Override
default ChronoLocalDateTime minus(long amountToSubtract, TemporalUnit unit) {
- return (ChronoLocalDateTime)(toLocalDate().getChronology().ensureChronoLocalDateTime(Temporal.super.minus(amountToSubtract, unit)));
+ return ChronoLocalDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
diff --git a/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java b/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
index 1f7195bc6f7f9cdf3546ef3fce17402360dc3d00..14f7bd927c232b5b62f55fd4910b23a0b799336e 100644
--- a/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
+++ b/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java
@@ -98,7 +98,7 @@ import java.util.Objects;
* @param the concrete type for the date of this date-time
* @since 1.8
*/
-final class ChronoLocalDateTimeImpl>
+final class ChronoLocalDateTimeImpl
implements ChronoLocalDateTime, Temporal, TemporalAdjuster, Serializable {
/**
@@ -171,9 +171,27 @@ final class ChronoLocalDateTimeImpl>
* @param time the local time, not null
* @return the local date-time, not null
*/
- @SuppressWarnings("rawtypes")
- static ChronoLocalDateTimeImpl> of(ChronoLocalDate> date, LocalTime time) {
- return new ChronoLocalDateTimeImpl(date, time);
+ static ChronoLocalDateTimeImpl of(R date, LocalTime time) {
+ return new ChronoLocalDateTimeImpl<>(date, time);
+ }
+
+ /**
+ * Casts the {@code Temporal} to {@code ChronoLocalDateTime} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
+ * or the chronology is not equal this Chronology
+ */
+ static ChronoLocalDateTimeImpl ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ ChronoLocalDateTimeImpl other = (ChronoLocalDateTimeImpl) temporal;
+ if (chrono.equals(other.toLocalDate().getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ + ", actual: " + other.toLocalDate().getChronology().getId());
+ }
+ return other;
}
/**
@@ -202,7 +220,7 @@ final class ChronoLocalDateTimeImpl>
return this;
}
// Validate that the new Temporal is a ChronoLocalDate (and not something else)
- D cd = (D) date.getChronology().ensureChronoLocalDate(newDate);
+ D cd = ChronoDateImpl.ensureValid(date.getChronology(), newDate);
return new ChronoLocalDateTimeImpl<>(cd, newTime);
}
@@ -260,13 +278,13 @@ final class ChronoLocalDateTimeImpl>
public ChronoLocalDateTimeImpl with(TemporalAdjuster adjuster) {
if (adjuster instanceof ChronoLocalDate) {
// The Chronology is checked in with(date,time)
- return with((ChronoLocalDate) adjuster, time);
+ return with((ChronoLocalDate) adjuster, time);
} else if (adjuster instanceof LocalTime) {
return with(date, (LocalTime) adjuster);
} else if (adjuster instanceof ChronoLocalDateTimeImpl) {
- return (ChronoLocalDateTimeImpl)(date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl>) adjuster));
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl>) adjuster);
}
- return (ChronoLocalDateTimeImpl)(date.getChronology().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl>) adjuster.adjustInto(this)));
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), (ChronoLocalDateTimeImpl>) adjuster.adjustInto(this));
}
@Override
@@ -279,7 +297,7 @@ final class ChronoLocalDateTimeImpl>
return with(date.with(field, newValue), time);
}
}
- return (ChronoLocalDateTimeImpl)(date.getChronology().ensureChronoLocalDateTime(field.adjustInto(this, newValue)));
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), field.adjustInto(this, newValue));
}
//-----------------------------------------------------------------------
@@ -298,7 +316,7 @@ final class ChronoLocalDateTimeImpl>
}
return with(date.plus(amountToAdd, unit), time);
}
- return (ChronoLocalDateTimeImpl)(date.getChronology().ensureChronoLocalDateTime(unit.addTo(this, amountToAdd)));
+ return ChronoLocalDateTimeImpl.ensureValid(date.getChronology(), unit.addTo(this, amountToAdd));
}
private ChronoLocalDateTimeImpl plusDays(long days) {
@@ -322,7 +340,7 @@ final class ChronoLocalDateTimeImpl>
}
//-----------------------------------------------------------------------
- private ChronoLocalDateTimeImpl plusWithOverflow(ChronoLocalDate> newDate, long hours, long minutes, long seconds, long nanos) {
+ private ChronoLocalDateTimeImpl plusWithOverflow(D newDate, long hours, long minutes, long seconds, long nanos) {
// 9223372036854775808 long, 2147483648 int
if ((hours | minutes | seconds | nanos) == 0) {
return with(newDate, time);
@@ -351,7 +369,7 @@ final class ChronoLocalDateTimeImpl>
//-----------------------------------------------------------------------
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ChronoLocalDateTime == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
}
@@ -361,10 +379,9 @@ final class ChronoLocalDateTimeImpl>
throw new DateTimeException("Unable to calculate amount as objects have different chronologies");
}
if (unit instanceof ChronoUnit) {
- ChronoUnit f = (ChronoUnit) unit;
- if (f.isTimeUnit()) {
+ if (unit.isTimeBased()) {
long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY);
- switch (f) {
+ switch ((ChronoUnit) unit) {
case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break;
case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break;
case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break;
@@ -373,13 +390,13 @@ final class ChronoLocalDateTimeImpl>
case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break;
case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break;
}
- return Math.addExact(amount, time.periodUntil(end.toLocalTime(), unit));
+ return Math.addExact(amount, time.until(end.toLocalTime(), unit));
}
- D endDate = end.toLocalDate();
+ ChronoLocalDate endDate = end.toLocalDate();
if (end.toLocalTime().isBefore(time)) {
endDate = endDate.minus(1, ChronoUnit.DAYS);
}
- return date.periodUntil(endDate, unit);
+ return date.until(endDate, unit);
}
return unit.between(this, endDateTime);
}
@@ -404,7 +421,7 @@ final class ChronoLocalDateTimeImpl>
}
static ChronoLocalDateTime> readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
- ChronoLocalDate> date = (ChronoLocalDate>) in.readObject();
+ ChronoLocalDate date = (ChronoLocalDate) in.readObject();
LocalTime time = (LocalTime) in.readObject();
return date.atTime(time);
}
diff --git a/src/share/classes/java/time/chrono/ChronoZonedDateTime.java b/src/share/classes/java/time/chrono/ChronoZonedDateTime.java
index d838a36ec292cb32756ba616be45053193dac42a..dd911fcd789647af34697c15f0018f7fba18d645 100644
--- a/src/share/classes/java/time/chrono/ChronoZonedDateTime.java
+++ b/src/share/classes/java/time/chrono/ChronoZonedDateTime.java
@@ -63,6 +63,7 @@ package java.time.chrono;
import static java.time.temporal.ChronoField.INSTANT_SECONDS;
import static java.time.temporal.ChronoField.OFFSET_SECONDS;
+import static java.time.temporal.ChronoUnit.FOREVER;
import static java.time.temporal.ChronoUnit.NANOS;
import java.time.DateTimeException;
@@ -73,6 +74,7 @@ import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
@@ -115,7 +117,7 @@ import java.util.Objects;
* @param the concrete type for the date of this date-time
* @since 1.8
*/
-public interface ChronoZonedDateTime>
+public interface ChronoZonedDateTime
extends Temporal, Comparable> {
/**
@@ -338,9 +340,54 @@ public interface ChronoZonedDateTime>
*/
ChronoZonedDateTime withZoneSameInstant(ZoneId zone);
- @Override // Override to provide javadoc
+ /**
+ * Checks if the specified field is supported.
+ *
+ * This checks if the specified field can be queried on this date-time.
+ * If false, then calling the {@link #range(TemporalField) range},
+ * {@link #get(TemporalField) get} and {@link #with(TemporalField, long)}
+ * methods will throw an exception.
+ *
+ * The set of supported fields is defined by the chronology and normally includes
+ * all {@code ChronoField} fields.
+ *
+ * If the field is not a {@code ChronoField}, then the result of this method
+ * is obtained by invoking {@code TemporalField.isSupportedBy(TemporalAccessor)}
+ * passing {@code this} as the argument.
+ * Whether the field is supported is determined by the field.
+ *
+ * @param field the field to check, null returns false
+ * @return true if the field can be queried, false if not
+ */
+ @Override
boolean isSupported(TemporalField field);
+ /**
+ * Checks if the specified unit is supported.
+ *
+ * This checks if the specified unit can be added to or subtracted from this date-time.
+ * If false, then calling the {@link #plus(long, TemporalUnit)} and
+ * {@link #minus(long, TemporalUnit) minus} methods will throw an exception.
+ *
+ * The set of supported units is defined by the chronology and normally includes
+ * all {@code ChronoUnit} units except {@code FOREVER}.
+ *
+ * If the unit is not a {@code ChronoUnit}, then the result of this method
+ * is obtained by invoking {@code TemporalUnit.isSupportedBy(Temporal)}
+ * passing {@code this} as the argument.
+ * Whether the unit is supported is determined by the unit.
+ *
+ * @param unit the unit to check, null returns false
+ * @return true if the unit can be added/subtracted, false if not
+ */
+ @Override
+ default boolean isSupported(TemporalUnit unit) {
+ if (unit instanceof ChronoUnit) {
+ return unit != FOREVER;
+ }
+ return unit != null && unit.isSupportedBy(this);
+ }
+
//-----------------------------------------------------------------------
// override for covariant return type
/**
@@ -350,7 +397,7 @@ public interface ChronoZonedDateTime>
*/
@Override
default ChronoZonedDateTime with(TemporalAdjuster adjuster) {
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.with(adjuster)));
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.with(adjuster));
}
/**
@@ -368,7 +415,7 @@ public interface ChronoZonedDateTime>
*/
@Override
default ChronoZonedDateTime plus(TemporalAmount amount) {
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.plus(amount)));
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.plus(amount));
}
/**
@@ -386,7 +433,7 @@ public interface ChronoZonedDateTime>
*/
@Override
default ChronoZonedDateTime minus(TemporalAmount amount) {
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amount)));
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amount));
}
/**
@@ -396,7 +443,7 @@ public interface ChronoZonedDateTime>
*/
@Override
default ChronoZonedDateTime minus(long amountToSubtract, TemporalUnit unit) {
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit)));
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), Temporal.super.minus(amountToSubtract, unit));
}
//-----------------------------------------------------------------------
diff --git a/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java b/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java
index 835ec9321847ae56064e157a49f901a2327187a6..a39ed298d37f8748b55b7c986a0c75ba12feda5e 100644
--- a/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java
+++ b/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java
@@ -101,7 +101,7 @@ import java.util.Objects;
* @param the concrete type for the date of this date-time
* @since 1.8
*/
-final class ChronoZonedDateTimeImpl>
+final class ChronoZonedDateTimeImpl
implements ChronoZonedDateTime, Serializable {
/**
@@ -131,7 +131,7 @@ final class ChronoZonedDateTimeImpl>
* @param preferredOffset the zone offset, null if no preference
* @return the zoned date-time, not null
*/
- static > ChronoZonedDateTime ofBest(
+ static ChronoZonedDateTime ofBest(
ChronoLocalDateTimeImpl localDateTime, ZoneId zone, ZoneOffset preferredOffset) {
Objects.requireNonNull(localDateTime, "localDateTime");
Objects.requireNonNull(zone, "zone");
@@ -167,14 +167,13 @@ final class ChronoZonedDateTimeImpl>
* @param zone the zone identifier, not null
* @return the zoned date-time, not null
*/
- @SuppressWarnings("rawtypes")
static ChronoZonedDateTimeImpl> ofInstant(Chronology chrono, Instant instant, ZoneId zone) {
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules
LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
- ChronoLocalDateTimeImpl> cldt = (ChronoLocalDateTimeImpl>) chrono.localDateTime(ldt);
- return new ChronoZonedDateTimeImpl(cldt, offset, zone);
+ ChronoLocalDateTimeImpl> cldt = (ChronoLocalDateTimeImpl>)chrono.localDateTime(ldt);
+ return new ChronoZonedDateTimeImpl<>(cldt, offset, zone);
}
/**
@@ -184,10 +183,30 @@ final class ChronoZonedDateTimeImpl>
* @param zone the time-zone to use, validated not null
* @return the zoned date-time, validated not null
*/
+ @SuppressWarnings("unchecked")
private ChronoZonedDateTimeImpl create(Instant instant, ZoneId zone) {
return (ChronoZonedDateTimeImpl)ofInstant(toLocalDate().getChronology(), instant, zone);
}
+ /**
+ * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} ensuring it bas the specified chronology.
+ *
+ * @param chrono the chronology to check for, not null
+ * @param temporal a date-time to cast, not null
+ * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
+ * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
+ * or the chronology is not equal this Chronology
+ */
+ static ChronoZonedDateTimeImpl ensureValid(Chronology chrono, Temporal temporal) {
+ @SuppressWarnings("unchecked")
+ ChronoZonedDateTimeImpl other = (ChronoZonedDateTimeImpl) temporal;
+ if (chrono.equals(other.toLocalDate().getChronology()) == false) {
+ throw new ClassCastException("Chronology mismatch, required: " + chrono.getId()
+ + ", actual: " + other.toLocalDate().getChronology().getId());
+ }
+ return other;
+ }
+
//-----------------------------------------------------------------------
/**
* Constructor.
@@ -271,7 +290,7 @@ final class ChronoZonedDateTimeImpl>
}
return ofBest(dateTime.with(field, newValue), zone, offset);
}
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(field.adjustInto(this, newValue)));
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), field.adjustInto(this, newValue));
}
//-----------------------------------------------------------------------
@@ -280,12 +299,12 @@ final class ChronoZonedDateTimeImpl>
if (unit instanceof ChronoUnit) {
return with(dateTime.plus(amountToAdd, unit));
}
- return (ChronoZonedDateTime)(toLocalDate().getChronology().ensureChronoZonedDateTime(unit.addTo(this, amountToAdd))); /// TODO: Generics replacement Risk!
+ return ChronoZonedDateTimeImpl.ensureValid(toLocalDate().getChronology(), unit.addTo(this, amountToAdd)); /// TODO: Generics replacement Risk!
}
//-----------------------------------------------------------------------
@Override
- public long periodUntil(Temporal endDateTime, TemporalUnit unit) {
+ public long until(Temporal endDateTime, TemporalUnit unit) {
if (endDateTime instanceof ChronoZonedDateTime == false) {
throw new DateTimeException("Unable to calculate amount as objects are of two different types");
}
@@ -296,7 +315,7 @@ final class ChronoZonedDateTimeImpl>
}
if (unit instanceof ChronoUnit) {
end = end.withZoneSameInstant(offset);
- return dateTime.periodUntil(end.toLocalDateTime(), unit);
+ return dateTime.until(end.toLocalDateTime(), unit);
}
return unit.between(this, endDateTime);
}
diff --git a/src/share/classes/java/time/chrono/Chronology.java b/src/share/classes/java/time/chrono/Chronology.java
index e5daae764645a4faff914a5b653ba8a0ddf91816..561e2f794faa0dabfbeebeacfa618f19a0c45006 100644
--- a/src/share/classes/java/time/chrono/Chronology.java
+++ b/src/share/classes/java/time/chrono/Chronology.java
@@ -74,6 +74,9 @@ import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.MONTHS;
+import static java.time.temporal.ChronoUnit.WEEKS;
import static java.time.temporal.TemporalAdjuster.nextOrSame;
import java.io.DataInput;
@@ -88,14 +91,16 @@ import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
+import java.time.Month;
+import java.time.Year;
import java.time.ZoneId;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
-import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.UnsupportedTemporalTypeException;
@@ -188,8 +193,8 @@ public abstract class Chronology implements Comparable {
/**
* ChronoLocalDate order constant.
*/
- static final Comparator> DATE_ORDER =
- (Comparator> & Serializable) (date1, date2) -> {
+ static final Comparator DATE_ORDER =
+ (Comparator & Serializable) (date1, date2) -> {
return Long.compare(date1.toEpochDay(), date2.toEpochDay());
};
/**
@@ -480,60 +485,6 @@ public abstract class Chronology implements Comparable {
protected Chronology() {
}
- //-----------------------------------------------------------------------
- /**
- * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology.
- *
- * @param temporal a date-time to cast, not null
- * @return the date-time checked and cast to {@code ChronoLocalDate}, not null
- * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
- * or the chronology is not equal this Chronology
- */
- ChronoLocalDate> ensureChronoLocalDate(Temporal temporal) {
- @SuppressWarnings("unchecked")
- ChronoLocalDate> other = (ChronoLocalDate>) temporal;
- if (this.equals(other.getChronology()) == false) {
- throw new ClassCastException("Chronology mismatch, expected: " + getId() + ", actual: " + other.getChronology().getId());
- }
- return other;
- }
-
- /**
- * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology.
- *
- * @param temporal a date-time to cast, not null
- * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
- * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
- * or the chronology is not equal this Chronology
- */
- ChronoLocalDateTimeImpl> ensureChronoLocalDateTime(Temporal temporal) {
- @SuppressWarnings("unchecked")
- ChronoLocalDateTimeImpl> other = (ChronoLocalDateTimeImpl>) temporal;
- if (this.equals(other.toLocalDate().getChronology()) == false) {
- throw new ClassCastException("Chronology mismatch, required: " + getId()
- + ", supplied: " + other.toLocalDate().getChronology().getId());
- }
- return other;
- }
-
- /**
- * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology.
- *
- * @param temporal a date-time to cast, not null
- * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
- * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
- * or the chronology is not equal this Chronology
- */
- ChronoZonedDateTimeImpl> ensureChronoZonedDateTime(Temporal temporal) {
- @SuppressWarnings("unchecked")
- ChronoZonedDateTimeImpl> other = (ChronoZonedDateTimeImpl>) temporal;
- if (this.equals(other.toLocalDate().getChronology()) == false) {
- throw new ClassCastException("Chronology mismatch, required: " + getId()
- + ", supplied: " + other.toLocalDate().getChronology().getId());
- }
- return other;
- }
-
//-----------------------------------------------------------------------
/**
* Gets the ID of the chronology.
@@ -574,7 +525,7 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
- public ChronoLocalDate> date(Era era, int yearOfEra, int month, int dayOfMonth) {
+ public ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
@@ -588,7 +539,7 @@ public abstract class Chronology implements Comparable {
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
- public abstract ChronoLocalDate> date(int prolepticYear, int month, int dayOfMonth);
+ public abstract ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth);
/**
* Obtains a local date in this chronology from the era, year-of-era and
@@ -601,7 +552,7 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if unable to create the date
* @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
- public ChronoLocalDate> dateYearDay(Era era, int yearOfEra, int dayOfYear) {
+ public ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
@@ -614,7 +565,7 @@ public abstract class Chronology implements Comparable {
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
- public abstract ChronoLocalDate> dateYearDay(int prolepticYear, int dayOfYear);
+ public abstract ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear);
/**
* Obtains a local date in this chronology from the epoch-day.
@@ -626,7 +577,7 @@ public abstract class Chronology implements Comparable {
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
- public abstract ChronoLocalDate> dateEpochDay(long epochDay);
+ public abstract ChronoLocalDate dateEpochDay(long epochDay);
//-----------------------------------------------------------------------
/**
@@ -643,7 +594,7 @@ public abstract class Chronology implements Comparable {
* @return the current local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
- public ChronoLocalDate> dateNow() {
+ public ChronoLocalDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
@@ -660,7 +611,7 @@ public abstract class Chronology implements Comparable {
* @return the current local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
- public ChronoLocalDate> dateNow(ZoneId zone) {
+ public ChronoLocalDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
@@ -675,7 +626,7 @@ public abstract class Chronology implements Comparable {
* @return the current local date, not null
* @throws DateTimeException if unable to create the date
*/
- public ChronoLocalDate> dateNow(Clock clock) {
+ public ChronoLocalDate dateNow(Clock clock) {
Objects.requireNonNull(clock, "clock");
return date(LocalDate.now(clock));
}
@@ -699,7 +650,7 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if unable to create the date
* @see ChronoLocalDate#from(TemporalAccessor)
*/
- public abstract ChronoLocalDate> date(TemporalAccessor temporal);
+ public abstract ChronoLocalDate date(TemporalAccessor temporal);
/**
* Obtains a local date-time in this chronology from another temporal object.
@@ -722,7 +673,7 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if unable to create the date-time
* @see ChronoLocalDateTime#from(TemporalAccessor)
*/
- public ChronoLocalDateTime> localDateTime(TemporalAccessor temporal) {
+ public ChronoLocalDateTime extends ChronoLocalDate> localDateTime(TemporalAccessor temporal) {
try {
return date(temporal).atTime(LocalTime.from(temporal));
} catch (DateTimeException ex) {
@@ -754,7 +705,7 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if unable to create the date-time
* @see ChronoZonedDateTime#from(TemporalAccessor)
*/
- public ChronoZonedDateTime> zonedDateTime(TemporalAccessor temporal) {
+ public ChronoZonedDateTime extends ChronoLocalDate> zonedDateTime(TemporalAccessor temporal) {
try {
ZoneId zone = ZoneId.from(temporal);
try {
@@ -762,8 +713,7 @@ public abstract class Chronology implements Comparable {
return zonedDateTime(instant, zone);
} catch (DateTimeException ex1) {
- @SuppressWarnings("rawtypes")
- ChronoLocalDateTimeImpl cldt = ensureChronoLocalDateTime(localDateTime(temporal));
+ ChronoLocalDateTimeImpl> cldt = ChronoLocalDateTimeImpl.ensureValid(this, localDateTime(temporal));
return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
}
} catch (DateTimeException ex) {
@@ -781,7 +731,7 @@ public abstract class Chronology implements Comparable {
* @return the zoned date-time, not null
* @throws DateTimeException if the result exceeds the supported range
*/
- public ChronoZonedDateTime> zonedDateTime(Instant instant, ZoneId zone) {
+ public ChronoZonedDateTime extends ChronoLocalDate> zonedDateTime(Instant instant, ZoneId zone) {
return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
}
@@ -929,11 +879,82 @@ public abstract class Chronology implements Comparable {
* As such, {@code ChronoField} date fields are resolved here in the
* context of a specific chronology.
*
+ * {@code ChronoField} instances are resolved by this method, which may
+ * be overridden in subclasses.
+ *
+ * {@code EPOCH_DAY} - If present, this is converted to a date and
+ * all other date fields are then cross-checked against the date.
+ * {@code PROLEPTIC_MONTH} - If present, then it is split into the
+ * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
+ * then the field is validated.
+ * {@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
+ * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
+ * range is not validated, in smart and strict mode it is. The {@code ERA} is
+ * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
+ * present, and the mode is smart or lenient, then the last available era
+ * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
+ * left untouched. If only the {@code ERA} is present, then it is left untouched.
+ * {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
+ * If all three are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is smart or strict, then the month and day are validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the first month in the requested year,
+ * then adding the difference in months, then the difference in days.
+ * If the mode is smart, and the day-of-month is greater than the maximum for
+ * the year-month, then the day-of-month is adjusted to the last day-of-month.
+ * If the mode is strict, then the three fields must form a valid date.
+ * {@code YEAR} and {@code DAY_OF_YEAR} -
+ * If both are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the difference in days.
+ * If the mode is smart or strict, then the two fields must form a valid date.
+ * {@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
+ * If all four are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the first month in the requested year, then adding
+ * the difference in months, then the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all four fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year and month, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the month.
+ * {@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
+ * {@code DAY_OF_WEEK} - If all four are present, then they are combined to
+ * form a date. The approach is the same as described above for
+ * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
+ * The day-of-week is adjusted as the next or same matching day-of-week once
+ * the years, months and weeks have been handled.
+ * {@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
+ * If all three are present, then they are combined to form a date.
+ * In all three modes, the {@code YEAR} is validated.
+ * If the mode is lenient, then the date is combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the difference in weeks, then in days.
+ * If the mode is smart or strict, then the all three fields are validated to
+ * their outer ranges. The date is then combined in a manner equivalent to
+ * creating a date on the first day of the requested year, then adding
+ * the amount in weeks and days to reach their values. If the mode is strict,
+ * the date is additionally validated to check that the day and week adjustment
+ * did not change the year.
+ * {@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
+ * If all three are present, then they are combined to form a date.
+ * The approach is the same as described above for years and weeks in
+ * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
+ * next or same matching day-of-week once the years and weeks have been handled.
+ *
+ *
* The default implementation is suitable for most calendar systems.
* If {@link ChronoField#YEAR_OF_ERA} is found without an {@link ChronoField#ERA}
* then the last era in {@link #eras()} is used.
* The implementation assumes a 7 day week, that the first day-of-month
- * has the value 1, and that first day-of-year has the value 1.
+ * has the value 1, that first day-of-year has the value 1, and that the
+ * first of the month and year always exists.
*
* @param fieldValues the map of fields to values, which can be updated, not null
* @param resolverStyle the requested type of resolve, not null
@@ -941,99 +962,214 @@ public abstract class Chronology implements Comparable {
* @throws DateTimeException if the date cannot be resolved, typically
* because of a conflict in the input data
*/
- public ChronoLocalDate> resolveDate(Map fieldValues, ResolverStyle resolverStyle) {
+ public ChronoLocalDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) {
// check epoch-day before inventing era
if (fieldValues.containsKey(EPOCH_DAY)) {
return dateEpochDay(fieldValues.remove(EPOCH_DAY));
}
// fix proleptic month before inventing era
- Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
- if (pMonth != null) {
- // first day-of-month is likely to be safest for setting proleptic-month
- // cannot add to year zero, as not all chronologies have a year zero
- ChronoLocalDate> chronoDate = dateNow()
- .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
- addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
- addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
- }
+ resolveProlepticMonth(fieldValues, resolverStyle);
// invent era if necessary to resolve year-of-era
- Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
- if (yoeLong != null) {
- Long eraLong = fieldValues.remove(ERA);
- int yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
- if (eraLong != null) {
- Era eraObj = eraOf(Math.toIntExact(eraLong));
- addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
- } else if (fieldValues.containsKey(YEAR)) {
- int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
- ChronoLocalDate> chronoDate = dateYearDay(year, 1);
- addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
- } else {
- List eras = eras();
- if (eras.isEmpty()) {
- addFieldValue(fieldValues, YEAR, yoe);
- } else {
- Era eraObj = eras.get(eras.size() - 1);
- addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
- }
- }
+ ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle);
+ if (resolved != null) {
+ return resolved;
}
// build date
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
- int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
- return date(y, moy, dom);
+ return resolveYMD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
- int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
- int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
- ChronoLocalDate> chronoDate = date(y, moy, 1);
- return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
+ return resolveYMAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
- int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
- int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
- ChronoLocalDate> chronoDate = date(y, moy, 1);
- return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ return resolveYMAD(fieldValues, resolverStyle);
}
}
}
if (fieldValues.containsKey(DAY_OF_YEAR)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
- return dateYearDay(y, doy);
+ return resolveYD(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
- int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
- ChronoLocalDate> chronoDate = dateYearDay(y, 1);
- return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
+ return resolveYAA(fieldValues, resolverStyle);
}
if (fieldValues.containsKey(DAY_OF_WEEK)) {
- int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
- int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
- int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
- ChronoLocalDate> chronoDate = dateYearDay(y, 1);
- return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ return resolveYAD(fieldValues, resolverStyle);
+ }
+ }
+ }
+ return null;
+ }
+
+ void resolveProlepticMonth(Map fieldValues, ResolverStyle resolverStyle) {
+ Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
+ if (pMonth != null) {
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ PROLEPTIC_MONTH.checkValidValue(pMonth);
+ }
+ // first day-of-month is likely to be safest for setting proleptic-month
+ // cannot add to year zero, as not all chronologies have a year zero
+ ChronoLocalDate chronoDate = dateNow()
+ .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
+ addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
+ addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
+ }
+ }
+
+ ChronoLocalDate resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) {
+ Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
+ if (yoeLong != null) {
+ Long eraLong = fieldValues.remove(ERA);
+ int yoe;
+ if (resolverStyle != ResolverStyle.LENIENT) {
+ yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
+ } else {
+ yoe = Math.toIntExact(yoeLong);
+ }
+ if (eraLong != null) {
+ Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ } else {
+ if (fieldValues.containsKey(YEAR)) {
+ int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
+ ChronoLocalDate chronoDate = dateYearDay(year, 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
+ } else if (resolverStyle == ResolverStyle.STRICT) {
+ // do not invent era if strict
+ // reinstate the field removed earlier, no cross-check issues
+ fieldValues.put(YEAR_OF_ERA, yoeLong);
+ } else {
+ List eras = eras();
+ if (eras.isEmpty()) {
+ addFieldValue(fieldValues, YEAR, yoe);
+ } else {
+ Era eraObj = eras.get(eras.size() - 1);
+ addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
+ }
}
}
+ } else if (fieldValues.containsKey(ERA)) {
+ range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated
}
return null;
}
+ ChronoLocalDate resolveYMD(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
+ return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ ValueRange domRange = range(DAY_OF_MONTH);
+ int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
+ if (resolverStyle == ResolverStyle.SMART) { // previous valid
+ try {
+ return date(y, moy, dom);
+ } catch (DateTimeException ex) {
+ return date(y, moy, 1).with(TemporalAdjuster.lastDayOfMonth());
+ }
+ }
+ return date(y, moy, dom);
+ }
+
+ ChronoLocalDate resolveYD(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
+ return dateYearDay(y, 1).plus(days, DAYS);
+ }
+ int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
+ return dateYearDay(y, doy); // smart is same as strict
+ }
+
+ ChronoLocalDate resolveYMAA(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
+ long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
+ return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
+ int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
+ ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYMAD(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
+ long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
+ return resolveAligned(date(y, 1, 1), months, weeks, dow);
+ }
+ int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
+ int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
+ int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
+ ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYAA(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
+ long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
+ return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS);
+ }
+ int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
+ int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
+ ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
+ if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveYAD(Map fieldValues, ResolverStyle resolverStyle) {
+ int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
+ if (resolverStyle == ResolverStyle.LENIENT) {
+ long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
+ long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
+ return resolveAligned(dateYearDay(y, 1), 0, weeks, dow);
+ }
+ int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
+ int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
+ ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
+ if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
+ throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
+ }
+ return date;
+ }
+
+ ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) {
+ ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS);
+ if (dow > 7) {
+ date = date.plus((dow - 1) / 7, WEEKS);
+ dow = ((dow - 1) % 7) + 1;
+ } else if (dow < 1) {
+ date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS);
+ dow = ((dow + 6) % 7) + 1;
+ }
+ return date.with(nextOrSame(DayOfWeek.of((int) dow)));
+ }
+
/**
* Adds a field-value pair to the map, checking for conflicts.
*
diff --git a/src/share/classes/java/time/chrono/Era.java b/src/share/classes/java/time/chrono/Era.java
index 330346dcc360ee91057629b8ec95cdb6535f0870..0fd31c9887dff908d10d5daf0887d24c2d178cb0 100644
--- a/src/share/classes/java/time/chrono/Era.java
+++ b/src/share/classes/java/time/chrono/Era.java
@@ -238,7 +238,7 @@ public interface Era extends TemporalAccessor, TemporalAdjuster {
if (field == ERA) {
return getValue();
} else if (field instanceof ChronoField) {
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
diff --git a/src/share/classes/java/time/chrono/HijrahChronology.java b/src/share/classes/java/time/chrono/HijrahChronology.java
index 63dfc1c213ba62bccfdf0a9733e88e34007193fb..c5061b05a323ae895311d8abb8c2e472f70bc281 100644
--- a/src/share/classes/java/time/chrono/HijrahChronology.java
+++ b/src/share/classes/java/time/chrono/HijrahChronology.java
@@ -71,8 +71,10 @@ import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
+import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.HashMap;
@@ -115,7 +117,7 @@ import sun.util.logging.PlatformLogger;
*
* Hijrah-umalqura
* islamic-umalqura
- * ca-islamic-cv-umalqura
+ * ca-islamic-umalqura
* Islamic - Umm Al-Qura calendar of Saudi Arabia
*
*
@@ -126,10 +128,10 @@ import sun.util.logging.PlatformLogger;
*
* Selecting the chronology from the locale uses {@link Chronology#ofLocale}
* to find the Chronology based on Locale supported BCP 47 extension mechanism
- * to request a specific calendar ("ca") and variant ("cv"). For example,
+ * to request a specific calendar ("ca"). For example,
*
*
- * Locale locale = Locale.forLanguageTag("en-US-u-ca-islamic-cv-umalqura");
+ * Locale locale = Locale.forLanguageTag("en-US-u-ca-islamic-umalqura");
* Chronology chrono = Chronology.ofLocale(locale);
*
*
@@ -472,11 +474,16 @@ public final class HijrahChronology extends Chronology implements Serializable {
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
* @return the Hijrah local date, not null
- * @throws DateTimeException if unable to create the date
+ * @throws DateTimeException if the value of the year is out of range,
+ * or if the day-of-year is invalid for the year
*/
@Override
public HijrahDate dateYearDay(int prolepticYear, int dayOfYear) {
- return HijrahDate.of(this, prolepticYear, 1, 1).plusDays(dayOfYear - 1); // TODO better
+ HijrahDate date = HijrahDate.of(this, prolepticYear, 1, 1);
+ if (dayOfYear > date.lengthOfYear()) {
+ throw new DateTimeException("Invalid dayOfYear: " + dayOfYear);
+ }
+ return date.plusDays(dayOfYear - 1);
}
/**
@@ -515,16 +522,19 @@ public final class HijrahChronology extends Chronology implements Serializable {
}
@Override
+ @SuppressWarnings("unchecked")
public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) {
return (ChronoLocalDateTime) super.localDateTime(temporal);
}
@Override
+ @SuppressWarnings("unchecked")
public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) {
return (ChronoZonedDateTime) super.zonedDateTime(temporal);
}
@Override
+ @SuppressWarnings("unchecked")
public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) {
return (ChronoZonedDateTime) super.zonedDateTime(instant, zone);
}
@@ -550,7 +560,7 @@ public final class HijrahChronology extends Chronology implements Serializable {
}
@Override
- public Era eraOf(int eraValue) {
+ public HijrahEra eraOf(int eraValue) {
switch (eraValue) {
case 1:
return HijrahEra.AH;
@@ -580,6 +590,8 @@ public final class HijrahChronology extends Chronology implements Serializable {
case YEAR:
case YEAR_OF_ERA:
return ValueRange.of(getMinimumYear(), getMaximumYear());
+ case ERA:
+ return ValueRange.of(1, 1);
default:
return field.range();
}
@@ -587,6 +599,13 @@ public final class HijrahChronology extends Chronology implements Serializable {
return field.range();
}
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public HijrahDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) {
+ return (HijrahDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ //-----------------------------------------------------------------------
/**
* Check the validity of a year.
*
diff --git a/src/share/classes/java/time/chrono/HijrahDate.java b/src/share/classes/java/time/chrono/HijrahDate.java
index 3c01e94b9805782dd6a06c8a95c16054b947073b..ef40b6dc94839ddd762a14db7802bab1e891ae4b 100644
--- a/src/share/classes/java/time/chrono/HijrahDate.java
+++ b/src/share/classes/java/time/chrono/HijrahDate.java
@@ -109,7 +109,7 @@ import java.time.temporal.ValueRange;
*/
public final class HijrahDate
extends ChronoDateImpl
- implements ChronoLocalDate, Serializable {
+ implements ChronoLocalDate, Serializable {
/**
* Serialization version.
@@ -204,7 +204,7 @@ public final class HijrahDate
* @throws DateTimeException if the current date cannot be obtained
*/
public static HijrahDate now(Clock clock) {
- return HijrahChronology.INSTANCE.date(LocalDate.now(clock));
+ return HijrahDate.ofEpochDay(HijrahChronology.INSTANCE, LocalDate.now(clock).toEpochDay());
}
/**
@@ -349,7 +349,7 @@ public final class HijrahDate
}
return getChronology().range(f);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.rangeRefinedBy(this);
}
@@ -372,7 +372,7 @@ public final class HijrahDate
case YEAR: return prolepticYear;
case ERA: return getEraValue();
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
return field.getFrom(this);
}
@@ -393,7 +393,7 @@ public final class HijrahDate
case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH));
case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR));
case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, monthOfYear, nvalue);
- case DAY_OF_YEAR: return resolvePreviousValid(prolepticYear, ((nvalue - 1) / 30) + 1, ((nvalue - 1) % 30) + 1);
+ case DAY_OF_YEAR: return plusDays(Math.min(nvalue, lengthOfYear()) - getDayOfYear());
case EPOCH_DAY: return new HijrahDate(chrono, newValue);
case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7);
case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7);
@@ -403,9 +403,9 @@ public final class HijrahDate
case YEAR: return resolvePreviousValid(nvalue, monthOfYear, dayOfMonth);
case ERA: return resolvePreviousValid(1 - prolepticYear, monthOfYear, dayOfMonth);
}
- throw new UnsupportedTemporalTypeException("Unsupported field: " + field.getName());
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
- return ChronoLocalDate.super.with(field, newValue);
+ return super.with(field, newValue);
}
private HijrahDate resolvePreviousValid(int prolepticYear, int month, int day) {
@@ -479,7 +479,7 @@ public final class HijrahDate
* @return the day-of-year
*/
private int getDayOfYear() {
- return chrono.getDayOfYear(prolepticYear, monthOfYear);
+ return chrono.getDayOfYear(prolepticYear, monthOfYear) + dayOfMonth;
}
/**
@@ -575,12 +575,13 @@ public final class HijrahDate
}
@Override // for javadoc and covariant return type
+ @SuppressWarnings("unchecked")
public final ChronoLocalDateTime atTime(LocalTime localTime) {
- return super.atTime(localTime);
+ return (ChronoLocalDateTime)super.atTime(localTime);
}
@Override
- public Period periodUntil(ChronoLocalDate> endDate) {
+ public Period until(ChronoLocalDate endDate) {
// TODO: untested
HijrahDate end = getChronology().date(endDate);
long totalMonths = (end.prolepticYear - this.prolepticYear) * 12 + (end.monthOfYear - this.monthOfYear); // safe
@@ -622,7 +623,7 @@ public final class HijrahDate
return this;
}
- static ChronoLocalDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ static HijrahDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
HijrahChronology chrono = (HijrahChronology) in.readObject();
int year = in.readInt();
int month = in.readByte();
diff --git a/src/share/classes/java/time/chrono/IsoChronology.java b/src/share/classes/java/time/chrono/IsoChronology.java
index 66b0dc10d23e4f36dfe981636f605fed4b778936..a2f6badeaa524b544cc6ab749debd1965a077ffa 100644
--- a/src/share/classes/java/time/chrono/IsoChronology.java
+++ b/src/share/classes/java/time/chrono/IsoChronology.java
@@ -61,25 +61,16 @@
*/
package java.time.chrono;
-import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
-import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
-import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
-import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
import static java.time.temporal.ChronoField.DAY_OF_MONTH;
-import static java.time.temporal.ChronoField.DAY_OF_WEEK;
-import static java.time.temporal.ChronoField.DAY_OF_YEAR;
-import static java.time.temporal.ChronoField.EPOCH_DAY;
import static java.time.temporal.ChronoField.ERA;
import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
import static java.time.temporal.ChronoField.YEAR;
import static java.time.temporal.ChronoField.YEAR_OF_ERA;
-import static java.time.temporal.TemporalAdjuster.nextOrSame;
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
-import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -398,7 +389,7 @@ public final class IsoChronology extends Chronology implements Serializable {
}
@Override
- public Era eraOf(int eraValue) {
+ public IsoEra eraOf(int eraValue) {
return IsoEra.of(eraValue);
}
@@ -421,7 +412,7 @@ public final class IsoChronology extends Chronology implements Serializable {
* as follows.
*
* {@code EPOCH_DAY} - If present, this is converted to a {@code LocalDate}
- * all other date fields are then cross-checked against the date
+ * and all other date fields are then cross-checked against the date.
* {@code PROLEPTIC_MONTH} - If present, then it is split into the
* {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
* then the field is validated.
@@ -430,7 +421,7 @@ public final class IsoChronology extends Chronology implements Serializable {
* range is not validated, in smart and strict mode it is. The {@code ERA} is
* validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
* present, and the mode is smart or lenient, then the current era (CE/AD)
- * is assumed. In strict mode, no ers is assumed and the {@code YEAR_OF_ERA} is
+ * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
* left untouched. If only the {@code ERA} is present, then it is left untouched.
* {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
* If all three are present, then they are combined to form a {@code LocalDate}.
@@ -495,48 +486,11 @@ public final class IsoChronology extends Chronology implements Serializable {
*/
@Override // override for performance
public LocalDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) {
- // check epoch-day before inventing era
- if (fieldValues.containsKey(EPOCH_DAY)) {
- return LocalDate.ofEpochDay(fieldValues.remove(EPOCH_DAY));
- }
-
- // fix proleptic month before inventing era
- resolveProlepticMonth(fieldValues, resolverStyle);
-
- // invent era if necessary to resolve year-of-era
- resolveYearOfEra(fieldValues, resolverStyle);
-
- // build date
- if (fieldValues.containsKey(YEAR)) {
- if (fieldValues.containsKey(MONTH_OF_YEAR)) {
- if (fieldValues.containsKey(DAY_OF_MONTH)) {
- return resolveYMD(fieldValues, resolverStyle);
- }
- if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
- if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
- return resolveYMAA(fieldValues, resolverStyle);
- }
- if (fieldValues.containsKey(DAY_OF_WEEK)) {
- return resolveYMAD(fieldValues, resolverStyle);
- }
- }
- }
- if (fieldValues.containsKey(DAY_OF_YEAR)) {
- return resolveYD(fieldValues, resolverStyle);
- }
- if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
- if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
- return resolveYAA(fieldValues, resolverStyle);
- }
- if (fieldValues.containsKey(DAY_OF_WEEK)) {
- return resolveYAD(fieldValues, resolverStyle);
- }
- }
- }
- return null;
+ return (LocalDate) super.resolveDate(fieldValues, resolverStyle);
}
- private void resolveProlepticMonth(Map fieldValues, ResolverStyle resolverStyle) {
+ @Override // override for better proleptic algorithm
+ void resolveProlepticMonth(Map fieldValues, ResolverStyle resolverStyle) {
Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
if (pMonth != null) {
if (resolverStyle != ResolverStyle.LENIENT) {
@@ -547,7 +501,8 @@ public final class IsoChronology extends Chronology implements Serializable {
}
}
- private void resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) {
+ @Override // override for enhanced behaviour
+ LocalDate resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) {
Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
if (yoeLong != null) {
if (resolverStyle != ResolverStyle.LENIENT) {
@@ -575,10 +530,14 @@ public final class IsoChronology extends Chronology implements Serializable {
} else {
throw new DateTimeException("Invalid value for era: " + era);
}
+ } else if (fieldValues.containsKey(ERA)) {
+ ERA.checkValidValue(fieldValues.get(ERA)); // always validated
}
+ return null;
}
- private LocalDate resolveYMD(Map fieldValues, ResolverStyle resolverStyle) {
+ @Override // override for performance
+ LocalDate resolveYMD(Map fieldValues, ResolverStyle resolverStyle) {
int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
if (resolverStyle == ResolverStyle.LENIENT) {
long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
@@ -598,96 +557,6 @@ public final class IsoChronology extends Chronology implements Serializable {
return LocalDate.of(y, moy, dom);
}
- private LocalDate resolveYD(Map fieldValues, ResolverStyle resolverStyle) {
- int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
- if (resolverStyle == ResolverStyle.LENIENT) {
- long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
- return LocalDate.of(y, 1, 1).plusDays(days);
- }
- int doy = DAY_OF_YEAR.checkValidIntValue(fieldValues.remove(DAY_OF_YEAR));
- return LocalDate.ofYearDay(y, doy); // smart is same as strict
- }
-
- private LocalDate resolveYMAA(Map fieldValues, ResolverStyle resolverStyle) {
- int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
- if (resolverStyle == ResolverStyle.LENIENT) {
- long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
- long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
- long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
- return LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks).plusDays(days);
- }
- int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
- int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH));
- int ad = ALIGNED_DAY_OF_WEEK_IN_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH));
- LocalDate date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7 + (ad - 1));
- if (resolverStyle == ResolverStyle.STRICT && date.getMonthValue() != moy) {
- throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
- }
- return date;
- }
-
- private LocalDate resolveYMAD(Map fieldValues, ResolverStyle resolverStyle) {
- int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
- if (resolverStyle == ResolverStyle.LENIENT) {
- long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
- long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
- long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
- return resolveAligned(y, months, weeks, dow);
- }
- int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR));
- int aw = ALIGNED_WEEK_OF_MONTH.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH));
- int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK));
- LocalDate date = LocalDate.of(y, moy, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow)));
- if (resolverStyle == ResolverStyle.STRICT && date.getMonthValue() != moy) {
- throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
- }
- return date;
- }
-
- private LocalDate resolveYAA(Map fieldValues, ResolverStyle resolverStyle) {
- int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
- if (resolverStyle == ResolverStyle.LENIENT) {
- long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
- long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
- return LocalDate.of(y, 1, 1).plusWeeks(weeks).plusDays(days);
- }
- int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR));
- int ad = ALIGNED_DAY_OF_WEEK_IN_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR));
- LocalDate date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7 + (ad - 1));
- if (resolverStyle == ResolverStyle.STRICT && date.getYear() != y) {
- throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
- }
- return date;
- }
-
- private LocalDate resolveYAD(Map fieldValues, ResolverStyle resolverStyle) {
- int y = YEAR.checkValidIntValue(fieldValues.remove(YEAR));
- if (resolverStyle == ResolverStyle.LENIENT) {
- long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
- long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
- return resolveAligned(y, 0, weeks, dow);
- }
- int aw = ALIGNED_WEEK_OF_YEAR.checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR));
- int dow = DAY_OF_WEEK.checkValidIntValue(fieldValues.remove(DAY_OF_WEEK));
- LocalDate date = LocalDate.of(y, 1, 1).plusDays((aw - 1) * 7).with(nextOrSame(DayOfWeek.of(dow)));
- if (resolverStyle == ResolverStyle.STRICT && date.getYear() != y) {
- throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
- }
- return date;
- }
-
- private LocalDate resolveAligned(int y, long months, long weeks, long dow) {
- LocalDate date = LocalDate.of(y, 1, 1).plusMonths(months).plusWeeks(weeks);
- if (dow > 7) {
- date = date.plusWeeks((dow - 1) / 7);
- dow = ((dow - 1) % 7) + 1;
- } else if (dow < 1) {
- date = date.plusWeeks(Math.subtractExact(dow, 7) / 7);
- dow = ((dow + 6) % 7) + 1;
- }
- return date.with(nextOrSame(DayOfWeek.of((int) dow)));
- }
-
//-----------------------------------------------------------------------
@Override
public ValueRange range(ChronoField field) {
diff --git a/src/share/classes/java/time/chrono/JapaneseChronology.java b/src/share/classes/java/time/chrono/JapaneseChronology.java
index 1aead71b8ad3006ecd65b959227aa1298ef9c7ae..68a12755ea30aff50caf2ebb73018417f53e3de3 100644
--- a/src/share/classes/java/time/chrono/JapaneseChronology.java
+++ b/src/share/classes/java/time/chrono/JapaneseChronology.java
@@ -56,6 +56,15 @@
*/
package java.time.chrono;
+import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+import static java.time.temporal.ChronoField.ERA;
+import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+import static java.time.temporal.ChronoField.YEAR;
+import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+import static java.time.temporal.ChronoUnit.DAYS;
+import static java.time.temporal.ChronoUnit.MONTHS;
+
import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
@@ -63,13 +72,18 @@ import java.time.Instant;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
+import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjuster;
+import java.time.temporal.TemporalField;
+import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import sun.util.calendar.CalendarSystem;
import sun.util.calendar.LocalGregorianCalendar;
@@ -82,8 +96,22 @@ import sun.util.calendar.LocalGregorianCalendar;
* The Japanese Imperial calendar system is the same as the ISO calendar system
* apart from the era-based year numbering.
*
- * Only Meiji (1865-04-07 - 1868-09-07) and later eras are supported.
- * Older eras are handled as an unknown era where the year-of-era is the ISO year.
+ * Japan introduced the Gregorian calendar starting with Meiji 6.
+ * Only Meiji and later eras are supported;
+ * dates before Meiji 6, January 1 are not supported.
+ *
+ * The supported {@code ChronoField} instances are:
+ *
+ * {@code DAY_OF_WEEK}
+ * {@code DAY_OF_MONTH}
+ * {@code DAY_OF_YEAR}
+ * {@code EPOCH_DAY}
+ * {@code MONTH_OF_YEAR}
+ * {@code PROLEPTIC_MONTH}
+ * {@code YEAR_OF_ERA}
+ * {@code YEAR}
+ * {@code ERA}
+ *
*
* @implSpec
* This class is immutable and thread-safe.
@@ -91,7 +119,6 @@ import sun.util.calendar.LocalGregorianCalendar;
* @since 1.8
*/
public final class JapaneseChronology extends Chronology implements Serializable {
- // TODO: definition for unknown era may break requirement that year-of-era >= 1
static final LocalGregorianCalendar JCAL =
(LocalGregorianCalendar) CalendarSystem.forName("japanese");
@@ -152,6 +179,16 @@ public final class JapaneseChronology extends Chronology implements Serializable
/**
* Obtains a local date in Japanese calendar system from the
* era, year-of-era, month-of-year and day-of-month fields.
+ *
+ * The Japanese month and day-of-month are the same as those in the
+ * ISO calendar system. They are not reset when the era changes.
+ * For example:
+ *
+ * 6th Jan Showa 64 = ISO 1989-01-06
+ * 7th Jan Showa 64 = ISO 1989-01-07
+ * 8th Jan Heisei 1 = ISO 1989-01-08
+ * 9th Jan Heisei 1 = ISO 1989-01-09
+ *
*
* @param era the Japanese era, not null
* @param yearOfEra the year-of-era
@@ -172,6 +209,9 @@ public final class JapaneseChronology extends Chronology implements Serializable
/**
* Obtains a local date in Japanese calendar system from the
* proleptic-year, month-of-year and day-of-month fields.
+ *
+ * The Japanese proleptic year, month and day-of-month are the same as those
+ * in the ISO calendar system. They are not reset when the era changes.
*
* @param prolepticYear the proleptic-year
* @param month the month-of-year
@@ -187,6 +227,17 @@ public final class JapaneseChronology extends Chronology implements Serializable
/**
* Obtains a local date in Japanese calendar system from the
* era, year-of-era and day-of-year fields.
+ *
+ * The day-of-year in this factory is expressed relative to the start of the year-of-era.
+ * This definition changes the normal meaning of day-of-year only in those years
+ * where the year-of-era is reset to one due to a change in the era.
+ * For example:
+ *
+ * 6th Jan Showa 64 = day-of-year 6
+ * 7th Jan Showa 64 = day-of-year 7
+ * 8th Jan Heisei 1 = day-of-year 1
+ * 9th Jan Heisei 1 = day-of-year 2
+ *
*
* @param era the Japanese era, not null
* @param yearOfEra the year-of-era
@@ -203,6 +254,10 @@ public final class JapaneseChronology extends Chronology implements Serializable
/**
* Obtains a local date in Japanese calendar system from the
* proleptic-year and day-of-year fields.
+ *
+ * The day-of-year in this factory is expressed relative to the start of the proleptic year.
+ * The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
+ * They are not reset when the era changes.
*
* @param prolepticYear the proleptic-year
* @param dayOfYear the day-of-year
@@ -211,8 +266,7 @@ public final class JapaneseChronology extends Chronology implements Serializable
*/
@Override
public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
- LocalDate date = LocalDate.ofYearDay(prolepticYear, dayOfYear);
- return date(prolepticYear, date.getMonthValue(), date.getDayOfMonth());
+ return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
}
/**
@@ -290,15 +344,6 @@ public final class JapaneseChronology extends Chronology implements Serializable
throw new ClassCastException("Era must be JapaneseEra");
}
- if (era == JapaneseEra.SEIREKI) {
- JapaneseEra nextEra = JapaneseEra.values()[1];
- int nextEraYear = nextEra.getPrivateEra().getSinceDate().getYear();
- if (yearOfEra >= nextEraYear || yearOfEra < Year.MIN_VALUE) {
- throw new DateTimeException("Invalid yearOfEra value");
- }
- return yearOfEra;
- }
-
JapaneseEra jera = (JapaneseEra) era;
int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
if (yearOfEra == 1) {
@@ -320,14 +365,13 @@ public final class JapaneseChronology extends Chronology implements Serializable
* See the description of each Era for the numeric values of:
* {@link JapaneseEra#HEISEI}, {@link JapaneseEra#SHOWA},{@link JapaneseEra#TAISHO},
* {@link JapaneseEra#MEIJI}), only Meiji and later eras are supported.
- * Prior to Meiji {@link JapaneseEra#SEIREKI} is used.
*
* @param eraValue the era value
* @return the Japanese {@code Era} for the given numeric era value
* @throws DateTimeException if {@code eraValue} is invalid
*/
@Override
- public Era eraOf(int eraValue) {
+ public JapaneseEra eraOf(int eraValue) {
return JapaneseEra.of(eraValue);
}
@@ -346,49 +390,117 @@ public final class JapaneseChronology extends Chronology implements Serializable
@Override
public ValueRange range(ChronoField field) {
switch (field) {
- case YEAR:
- case DAY_OF_MONTH:
- case DAY_OF_WEEK:
- case MICRO_OF_DAY:
- case MICRO_OF_SECOND:
- case HOUR_OF_DAY:
- case HOUR_OF_AMPM:
- case MINUTE_OF_DAY:
- case MINUTE_OF_HOUR:
- case SECOND_OF_DAY:
- case SECOND_OF_MINUTE:
- case MILLI_OF_DAY:
- case MILLI_OF_SECOND:
- case NANO_OF_DAY:
- case NANO_OF_SECOND:
- case CLOCK_HOUR_OF_DAY:
- case CLOCK_HOUR_OF_AMPM:
- case EPOCH_DAY:
- case PROLEPTIC_MONTH:
- case MONTH_OF_YEAR:
- return field.range();
- case ERA:
- return ValueRange.of(JapaneseEra.SEIREKI.getValue(),
- getCurrentEra().getValue());
- }
- Calendar jcal = Calendar.getInstance(LOCALE);
- int fieldIndex;
- switch (field) {
+ case ALIGNED_DAY_OF_WEEK_IN_MONTH:
+ case ALIGNED_DAY_OF_WEEK_IN_YEAR:
+ case ALIGNED_WEEK_OF_MONTH:
+ case ALIGNED_WEEK_OF_YEAR:
+ throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
case YEAR_OF_ERA: {
+ Calendar jcal = Calendar.getInstance(LOCALE);
int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
- return ValueRange.of(Year.MIN_VALUE, jcal.getGreatestMinimum(Calendar.YEAR),
+ return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
Year.MAX_VALUE - startYear);
}
- case DAY_OF_YEAR:
- fieldIndex = Calendar.DAY_OF_YEAR;
- break;
+ case DAY_OF_YEAR: {
+ Calendar jcal = Calendar.getInstance(LOCALE);
+ int fieldIndex = Calendar.DAY_OF_YEAR;
+ return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
+ jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
+ }
+ case YEAR:
+ return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
+ case ERA:
+ return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
default:
- // TODO: review the remaining fields
- throw new UnsupportedOperationException("Unimplementable field: " + field);
+ return field.range();
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ @Override // override for return type
+ public JapaneseDate resolveDate(Map fieldValues, ResolverStyle resolverStyle) {
+ return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
+ }
+
+ @Override // override for special Japanese behavior
+ ChronoLocalDate resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) {
+ // validate era and year-of-era
+ Long eraLong = fieldValues.get(ERA);
+ JapaneseEra era = null;
+ if (eraLong != null) {
+ era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); // always validated
+ }
+ Long yoeLong = fieldValues.get(YEAR_OF_ERA);
+ int yoe = 0;
+ if (yoeLong != null) {
+ yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); // always validated
+ }
+ // if only year-of-era and no year then invent era unless strict
+ if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
+ era = JapaneseEra.values()[JapaneseEra.values().length - 1];
+ }
+ // if both present, then try to create date
+ if (yoeLong != null && era != null) {
+ if (fieldValues.containsKey(MONTH_OF_YEAR)) {
+ if (fieldValues.containsKey(DAY_OF_MONTH)) {
+ return resolveYMD(era, yoe, fieldValues, resolverStyle);
+ }
+ }
+ if (fieldValues.containsKey(DAY_OF_YEAR)) {
+ return resolveYD(era, yoe, fieldValues, resolverStyle);
+ }
+ }
+ return null;
+ }
+
+ private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
+ return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
+ }
+
+ private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map