src/share/classes/java/time/Duration.java

Print this page

        

*** 59,72 **** * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package java.time; import static java.time.LocalTime.SECONDS_PER_DAY; ! import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoUnit.DAYS; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InvalidObjectException; --- 59,76 ---- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package java.time; + import static java.time.LocalTime.NANOS_PER_SECOND; import static java.time.LocalTime.SECONDS_PER_DAY; ! import static java.time.LocalTime.SECONDS_PER_HOUR; ! import static java.time.LocalTime.SECONDS_PER_MINUTE; import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoUnit.DAYS; + import static java.time.temporal.ChronoUnit.NANOS; + import static java.time.temporal.ChronoUnit.SECONDS; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InvalidObjectException;
*** 77,106 **** import java.math.RoundingMode; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; ! import java.time.temporal.TemporalAccessor; ! import java.time.temporal.TemporalAdder; ! import java.time.temporal.TemporalSubtractor; import java.time.temporal.TemporalUnit; import java.util.Objects; /** ! * A duration between two instants on the time-line. * <p> ! * This class models a duration of time and is not tied to any instant. ! * The model is of a directed duration, meaning that the duration may be negative. * <p> * A physical duration could be of infinite length. * For practicality, the duration is stored with constraints similar to {@link Instant}. * The duration uses nanosecond resolution with a maximum value of the seconds that can * be held in a {@code long}. This is greater than the current estimated age of the universe. * <p> * The range of a duration requires the storage of a number larger than a {@code long}. * To achieve this, the class stores a {@code long} representing seconds and an {@code int} * representing nanosecond-of-second, which will always be between 0 and 999,999,999. * <p> * The duration is measured in "seconds", but these are not necessarily identical to * the scientific "SI second" definition based on atomic clocks. * This difference only impacts durations measured near a leap-second and should not affect * most applications. --- 81,117 ---- import java.math.RoundingMode; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.Temporal; ! import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; + import java.util.Arrays; + import java.util.Collections; + import java.util.List; import java.util.Objects; + import java.util.regex.Matcher; + import java.util.regex.Pattern; /** ! * A time-based amount of time, such as '34.5 seconds'. * <p> ! * This class models a quantity or amount of time in terms of seconds and nanoseconds. ! * It can be accessed using other duration-based units, such as minutes and hours. ! * In addition, the {@link ChronoUnit#DAYS DAYS} unit can be used and is treated as ! * exactly equal to 24 hours, thus ignoring daylight savings effects. ! * See {@link Period} for the date-based equivalent to this class. * <p> * A physical duration could be of infinite length. * For practicality, the duration is stored with constraints similar to {@link Instant}. * The duration uses nanosecond resolution with a maximum value of the seconds that can * be held in a {@code long}. This is greater than the current estimated age of the universe. * <p> * The range of a duration requires the storage of a number larger than a {@code long}. * To achieve this, the class stores a {@code long} representing seconds and an {@code int} * representing nanosecond-of-second, which will always be between 0 and 999,999,999. + * The model is of a directed duration, meaning that the duration may be negative. * <p> * The duration is measured in "seconds", but these are not necessarily identical to * the scientific "SI second" definition based on atomic clocks. * This difference only impacts durations measured near a leap-second and should not affect * most applications.
*** 110,120 **** * This class is immutable and thread-safe. * * @since 1.8 */ public final class Duration ! implements TemporalAdder, TemporalSubtractor, Comparable<Duration>, Serializable { /** * Constant for a duration of zero. */ public static final Duration ZERO = new Duration(0, 0); --- 121,131 ---- * This class is immutable and thread-safe. * * @since 1.8 */ public final class Duration ! implements TemporalAmount, Comparable<Duration>, Serializable { /** * Constant for a duration of zero. */ public static final Duration ZERO = new Duration(0, 0);
*** 123,137 **** */ private static final long serialVersionUID = 3078945930695997490L; /** * Constant for nanos per second. */ ! private static final int NANOS_PER_SECOND = 1000_000_000; /** ! * Constant for nanos per second. */ ! private static final BigInteger BI_NANOS_PER_SECOND = BigInteger.valueOf(NANOS_PER_SECOND); /** * The number of seconds in the duration. */ private final long seconds; --- 134,151 ---- */ private static final long serialVersionUID = 3078945930695997490L; /** * Constant for nanos per second. */ ! private static final BigInteger BI_NANOS_PER_SECOND = BigInteger.valueOf(NANOS_PER_SECOND); /** ! * The pattern for parsing. */ ! private final static Pattern PATTERN = ! Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)D)?" + ! "(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?", ! Pattern.CASE_INSENSITIVE); /** * The number of seconds in the duration. */ private final long seconds;
*** 141,151 **** */ private final int nanos; //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} from a number of seconds. * <p> * The nanosecond in second field is set to zero. * * @param seconds the number of seconds, positive or negative * @return a {@code Duration}, not null --- 155,211 ---- */ private final int nanos; //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} representing a number of standard 24 hour days. ! * <p> ! * The seconds are calculated based on the standard definition of a day, ! * where each day is 86400 seconds which implies a 24 hour day. ! * The nanosecond in second field is set to zero. ! * ! * @param days the number of days, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input days exceeds the capacity of {@code Duration} ! */ ! public static Duration ofDays(long days) { ! return create(Math.multiplyExact(days, SECONDS_PER_DAY), 0); ! } ! ! /** ! * Obtains a {@code Duration} representing a number of standard hours. ! * <p> ! * The seconds are calculated based on the standard definition of an hour, ! * where each hour is 3600 seconds. ! * The nanosecond in second field is set to zero. ! * ! * @param hours the number of hours, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input hours exceeds the capacity of {@code Duration} ! */ ! public static Duration ofHours(long hours) { ! return create(Math.multiplyExact(hours, SECONDS_PER_HOUR), 0); ! } ! ! /** ! * Obtains a {@code Duration} representing a number of standard minutes. ! * <p> ! * The seconds are calculated based on the standard definition of a minute, ! * where each minute is 60 seconds. ! * The nanosecond in second field is set to zero. ! * ! * @param minutes the number of minutes, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input minutes exceeds the capacity of {@code Duration} ! */ ! public static Duration ofMinutes(long minutes) { ! return create(Math.multiplyExact(minutes, SECONDS_PER_MINUTE), 0); ! } ! ! //----------------------------------------------------------------------- ! /** ! * Obtains a {@code Duration} representing a number of seconds. * <p> * The nanosecond in second field is set to zero. * * @param seconds the number of seconds, positive or negative * @return a {@code Duration}, not null
*** 153,164 **** public static Duration ofSeconds(long seconds) { return create(seconds, 0); } /** ! * Obtains an instance of {@code Duration} from a number of seconds ! * and an adjustment in nanoseconds. * <p> * This method allows an arbitrary number of nanoseconds to be passed in. * The factory will alter the values of the second and nanosecond in order * to ensure that the stored nanosecond is in the range 0 to 999,999,999. * For example, the following will result in the exactly the same duration: --- 213,224 ---- public static Duration ofSeconds(long seconds) { return create(seconds, 0); } /** ! * Obtains a {@code Duration} representing a number of seconds and an ! * adjustment in nanoseconds. * <p> * This method allows an arbitrary number of nanoseconds to be passed in. * The factory will alter the values of the second and nanosecond in order * to ensure that the stored nanosecond is in the range 0 to 999,999,999. * For example, the following will result in the exactly the same duration:
*** 173,189 **** * @return a {@code Duration}, not null * @throws ArithmeticException if the adjustment causes the seconds to exceed the capacity of {@code Duration} */ public static Duration ofSeconds(long seconds, long nanoAdjustment) { long secs = Math.addExact(seconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND)); ! int nos = (int)Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); return create(secs, nos); } //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} from a number of milliseconds. * <p> * The seconds and nanoseconds are extracted from the specified milliseconds. * * @param millis the number of milliseconds, positive or negative * @return a {@code Duration}, not null --- 233,249 ---- * @return a {@code Duration}, not null * @throws ArithmeticException if the adjustment causes the seconds to exceed the capacity of {@code Duration} */ public static Duration ofSeconds(long seconds, long nanoAdjustment) { long secs = Math.addExact(seconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND)); ! int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); return create(secs, nos); } //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} representing a number of milliseconds. * <p> * The seconds and nanoseconds are extracted from the specified milliseconds. * * @param millis the number of milliseconds, positive or negative * @return a {@code Duration}, not null
*** 198,208 **** return create(secs, mos * 1000_000); } //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} from a number of nanoseconds. * <p> * The seconds and nanoseconds are extracted from the specified nanoseconds. * * @param nanos the number of nanoseconds, positive or negative * @return a {@code Duration}, not null --- 258,268 ---- return create(secs, mos * 1000_000); } //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} representing a number of nanoseconds. * <p> * The seconds and nanoseconds are extracted from the specified nanoseconds. * * @param nanos the number of nanoseconds, positive or negative * @return a {@code Duration}, not null
*** 217,273 **** return create(secs, nos); } //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} from a number of standard length minutes. ! * <p> ! * The seconds are calculated based on the standard definition of a minute, ! * where each minute is 60 seconds. ! * The nanosecond in second field is set to zero. ! * ! * @param minutes the number of minutes, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input minutes exceeds the capacity of {@code Duration} ! */ ! public static Duration ofMinutes(long minutes) { ! return create(Math.multiplyExact(minutes, 60), 0); ! } ! ! /** ! * Obtains an instance of {@code Duration} from a number of standard length hours. ! * <p> ! * The seconds are calculated based on the standard definition of an hour, ! * where each hour is 3600 seconds. ! * The nanosecond in second field is set to zero. ! * ! * @param hours the number of hours, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input hours exceeds the capacity of {@code Duration} ! */ ! public static Duration ofHours(long hours) { ! return create(Math.multiplyExact(hours, 3600), 0); ! } ! ! /** ! * Obtains an instance of {@code Duration} from a number of standard 24 hour days. ! * <p> ! * The seconds are calculated based on the standard definition of a day, ! * where each day is 86400 seconds which implies a 24 hour day. ! * The nanosecond in second field is set to zero. ! * ! * @param days the number of days, positive or negative ! * @return a {@code Duration}, not null ! * @throws ArithmeticException if the input days exceeds the capacity of {@code Duration} ! */ ! public static Duration ofDays(long days) { ! return create(Math.multiplyExact(days, 86400), 0); ! } ! ! //----------------------------------------------------------------------- ! /** ! * Obtains an instance of {@code Duration} from a duration in the specified unit. * <p> * The parameters represent the two parts of a phrase like '6 Hours'. For example: * <pre> * Duration.of(3, SECONDS); * Duration.of(465, HOURS); --- 277,287 ---- return create(secs, nos); } //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} representing an amount in the specified unit. * <p> * The parameters represent the two parts of a phrase like '6 Hours'. For example: * <pre> * Duration.of(3, SECONDS); * Duration.of(465, HOURS);
*** 286,399 **** return ZERO.plus(amount, unit); } //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} representing the duration between two instants. * <p> ! * A {@code Duration} represents a directed distance between two points on the time-line. ! * As such, this method will return a negative duration if the end is before the start. ! * To guarantee to obtain a positive duration call {@link #abs()} on the result of this factory. * * @param startInclusive the start instant, inclusive, not null * @param endExclusive the end instant, exclusive, not null * @return a {@code Duration}, not null * @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration} */ ! public static Duration between(TemporalAccessor startInclusive, TemporalAccessor endExclusive) { ! long secs = Math.subtractExact(endExclusive.getLong(INSTANT_SECONDS), startInclusive.getLong(INSTANT_SECONDS)); ! long nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND); ! secs = Math.addExact(secs, Math.floorDiv(nanos, NANOS_PER_SECOND)); ! nanos = Math.floorMod(nanos, NANOS_PER_SECOND); ! return create(secs, (int) nanos); // safe from overflow } //----------------------------------------------------------------------- /** ! * Obtains an instance of {@code Duration} by parsing a text string. ! * <p> ! * This will parse the string produced by {@link #toString()} which is ! * the ISO-8601 format {@code PTnS} where {@code n} is ! * the number of seconds with optional decimal part. ! * The number must consist of ASCII numerals. ! * There must only be a negative sign at the start of the number and it can ! * only be present if the value is less than zero. ! * There must be at least one digit before any decimal point. ! * There must be between 1 and 9 inclusive digits after any decimal point. ! * The letters (P, T and S) will be accepted in upper or lower case. * The decimal point may be either a dot or a comma. * * @param text the text to parse, not null ! * @return a {@code Duration}, not null ! * @throws DateTimeParseException if the text cannot be parsed to a {@code Duration} */ ! public static Duration parse(final CharSequence text) { Objects.requireNonNull(text, "text"); ! int len = text.length(); ! if (len < 4 || ! (text.charAt(0) != 'P' && text.charAt(0) != 'p') || ! (text.charAt(1) != 'T' && text.charAt(1) != 't') || ! (text.charAt(len - 1) != 'S' && text.charAt(len - 1) != 's') || ! (len == 5 && text.charAt(2) == '-' && text.charAt(3) == '0')) { ! throw new DateTimeParseException("Duration could not be parsed: " + text, text, 0); ! } ! String numberText = text.subSequence(2, len - 1).toString().replace(',', '.'); ! if (numberText.charAt(0) == '+') { ! throw new DateTimeParseException("Duration could not be parsed: " + text, text, 2); } - int dot = numberText.indexOf('.'); try { ! if (dot == -1) { ! // no decimal places ! if (numberText.startsWith("-0")) { ! throw new DateTimeParseException("Duration could not be parsed: " + text, text, 2); ! } ! return create(Long.parseLong(numberText), 0); ! } ! // decimal places ! boolean negative = false; ! if (numberText.charAt(0) == '-') { ! negative = true; ! } ! long secs = Long.parseLong(numberText.substring(0, dot)); ! numberText = numberText.substring(dot + 1); ! len = numberText.length(); ! if (len == 0 || len > 9 || numberText.charAt(0) == '-' || numberText.charAt(0) == '+') { ! throw new DateTimeParseException("Duration could not be parsed: " + text, text, 2); ! } ! int nanos = Integer.parseInt(numberText); ! switch (len) { ! case 1: ! nanos *= 100000000; ! break; ! case 2: ! nanos *= 10000000; ! break; ! case 3: ! nanos *= 1000000; ! break; ! case 4: ! nanos *= 100000; ! break; ! case 5: ! nanos *= 10000; ! break; ! case 6: ! nanos *= 1000; ! break; ! case 7: ! nanos *= 100; ! break; ! case 8: ! nanos *= 10; ! break; } - return negative ? ofSeconds(secs, -nanos) : create(secs, nanos); ! } catch (ArithmeticException | NumberFormatException ex) { ! throw new DateTimeParseException("Duration could not be parsed: " + text, text, 2, ex); } } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Duration} using seconds and nanoseconds. --- 300,442 ---- return ZERO.plus(amount, unit); } //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} representing the duration between two instants. * <p> ! * This calculates the duration between two temporal objects of the same type. ! * The difference in seconds is calculated using ! * {@link Temporal#periodUntil(Temporal, TemporalUnit)}. ! * The difference in nanoseconds is calculated using by querying the ! * {@link ChronoField#NANO_OF_SECOND NANO_OF_SECOND} field. ! * <p> ! * The result of this method can be a negative period if the end is before the start. ! * To guarantee to obtain a positive duration call {@link #abs()} on the result. * * @param startInclusive the start instant, inclusive, not null * @param endExclusive the end instant, exclusive, not null * @return a {@code Duration}, not null * @throws ArithmeticException if the calculation exceeds the capacity of {@code Duration} */ ! public static Duration between(Temporal startInclusive, Temporal endExclusive) { ! long secs = startInclusive.periodUntil(endExclusive, SECONDS); ! long nanos; ! try { ! nanos = endExclusive.getLong(NANO_OF_SECOND) - startInclusive.getLong(NANO_OF_SECOND); ! } catch (DateTimeException ex) { ! nanos = 0; ! } ! return ofSeconds(secs, nanos); } //----------------------------------------------------------------------- /** ! * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMn.nS}. ! * <p> ! * This will parse a textual representation of a duration, including the ! * string produced by {@code toString()}. The formats accepted are based ! * on the ISO-8601 duration format {@code PnDTnHnMn.nS} with days ! * considered to be exactly 24 hours. ! * <p> ! * 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 four sections, each consisting of a number and a suffix. ! * The sections have suffixes in ASCII of "D", "H", "M" and "S" for ! * days, hours, minutes and seconds, accepted in upper or lower case. ! * The suffixes must occur in order. The ASCII letter "T" must occur before ! * the first occurrence, if any, of an hour, minute or second section. ! * At least one of the four sections must be present, and if "T" is present ! * there must be at least one section after the "T". ! * The number part of each section must consist of one or more ASCII digits. ! * The number may be prefixed by the ASCII negative or positive symbol. ! * The number of days, hours and minutes must parse to an {@code long}. ! * The number of seconds must parse to an {@code long} with optional fraction. * The decimal point may be either a dot or a comma. + * The fractional part may have from zero to 9 digits. + * <p> + * The leading plus/minus sign, and negative values for other units are + * not part of the ISO-8601 standard. + * <p> + * Examples: + * <pre> + * "PT20.345S" -> parses as "20.345 seconds" + * "PT15M" -> parses as "15 minutes" (where a minute is 60 seconds) + * "PT10H" -> parses as "10 hours" (where an hour is 3600 seconds) + * "P2D" -> parses as "2 days" (where a day is 24 hours or 86400 seconds) + * "P2DT3H4M" -> parses as "2 days, 3 hours and 4 minutes" + * "P-6H3M" -> parses as "-6 hours and +3 minutes" + * "-P6H3M" -> parses as "-6 hours and -3 minutes" + * "-P-6H+3M" -> parses as "+6 hours and -3 minutes" + * </pre> * * @param text the text to parse, not null ! * @return the parsed duration, not null ! * @throws DateTimeParseException if the text cannot be parsed to a duration */ ! public static Duration parse(CharSequence text) { Objects.requireNonNull(text, "text"); ! Matcher matcher = PATTERN.matcher(text); ! if (matcher.matches()) { ! // check for letter T but no time sections ! if ("T".equals(matcher.group(3)) == false) { ! boolean negate = "-".equals(matcher.group(1)); ! String dayMatch = matcher.group(2); ! String hourMatch = matcher.group(4); ! String minuteMatch = matcher.group(5); ! String secondMatch = matcher.group(6); ! String fractionMatch = matcher.group(7); ! if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { ! long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); ! long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); ! long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); ! long seconds = parseNumber(text, secondMatch, 1, "seconds"); ! int nanos = parseFraction(text, fractionMatch, seconds < 0 ? -1 : 1); ! try { ! return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); ! } catch (ArithmeticException ex) { ! throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0).initCause(ex); ! } ! } ! } ! } ! throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0); ! } ! ! private static long parseNumber(CharSequence text, String parsed, int multiplier, String errorText) { ! // regex limits to [-+]?[0-9]+ ! if (parsed == null) { ! return 0; ! } ! try { ! long val = Long.parseLong(parsed); ! return Math.multiplyExact(val, multiplier); ! } catch (NumberFormatException | ArithmeticException ex) { ! throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex); ! } ! } ! ! private static int parseFraction(CharSequence text, String parsed, int negate) { ! // regex limits to [0-9]{0,9} ! if (parsed == null || parsed.length() == 0) { ! return 0; } try { ! parsed = (parsed + "000000000").substring(0, 9); ! return Integer.parseInt(parsed) * negate; ! } catch (NumberFormatException | ArithmeticException ex) { ! throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0).initCause(ex); ! } } ! private static Duration create(boolean negate, long daysAsSecs, long hoursAsSecs, long minsAsSecs, long secs, int nanos) { ! long seconds = Math.addExact(daysAsSecs, Math.addExact(hoursAsSecs, Math.addExact(minsAsSecs, secs))); ! if (negate) { ! return ofSeconds(seconds, nanos).negated(); } + return ofSeconds(seconds, nanos); } //----------------------------------------------------------------------- /** * Obtains an instance of {@code Duration} using seconds and nanoseconds.
*** 420,452 **** this.nanos = nanos; } //----------------------------------------------------------------------- /** ! * Checks if this duration is zero length. * <p> ! * A {@code Duration} represents a directed distance between two points on ! * the time-line and can therefore be positive, zero or negative. ! * This method checks whether the length is zero. * ! * @return true if this duration has a total length equal to zero */ ! public boolean isZero() { ! return (seconds | nanos) == 0; } /** ! * Checks if this duration is positive, excluding zero. * <p> * A {@code Duration} represents a directed distance between two points on * the time-line and can therefore be positive, zero or negative. ! * This method checks whether the length is greater than zero. * ! * @return true if this duration has a total length greater than zero */ ! public boolean isPositive() { ! return seconds >= 0 && ((seconds | nanos) != 0); } /** * Checks if this duration is negative, excluding zero. * <p> --- 463,532 ---- this.nanos = nanos; } //----------------------------------------------------------------------- /** ! * Gets the value of the requested unit. * <p> ! * This returns a value for each of the two supported units, ! * {@link ChronoUnit#SECONDS SECONDS} and {@link ChronoUnit#NANOS NANOS}. ! * All other units throw an exception. ! * ! * @param unit the {@code TemporalUnit} for which to return the value ! * @return the long value of the unit ! * @throws DateTimeException if the unit is not supported ! */ ! @Override ! public long get(TemporalUnit unit) { ! if (unit == SECONDS) { ! return seconds; ! } else if (unit == NANOS) { ! return nanos; ! } else { ! throw new DateTimeException("Unsupported unit: " + unit.getName()); ! } ! } ! ! /** ! * Gets the set of units supported by this duration. ! * <p> ! * The supported units are {@link ChronoUnit#SECONDS SECONDS}, ! * and {@link ChronoUnit#NANOS NANOS}. ! * They are returned in the order seconds, nanos. ! * <p> ! * This set can be used in conjunction with {@link #get(TemporalUnit)} ! * to access the entire state of the period. * ! * @return a list containing the seconds and nanos units, not null */ ! @Override ! public List<TemporalUnit> getUnits() { ! return DurationUnits.UNITS; ! } ! ! /** ! * Private class to delay initialization of this list until needed. ! * The circular dependency between Duration and ChronoUnit prevents ! * the simple initialization in Duration. ! */ ! private static class DurationUnits { ! final static List<TemporalUnit> UNITS = ! Collections.unmodifiableList(Arrays.<TemporalUnit>asList(SECONDS, NANOS)); } + //----------------------------------------------------------------------- /** ! * Checks if this duration is zero length. * <p> * A {@code Duration} represents a directed distance between two points on * the time-line and can therefore be positive, zero or negative. ! * This method checks whether the length is zero. * ! * @return true if this duration has a total length equal to zero */ ! public boolean isZero() { ! return (seconds | nanos) == 0; } /** * Checks if this duration is negative, excluding zero. * <p>
*** 497,506 **** --- 577,619 ---- return nanos; } //----------------------------------------------------------------------- /** + * Returns a copy of this duration with the specified amount of seconds. + * <p> + * This returns a duration with the specified seconds, retaining the + * nano-of-second part of this duration. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param seconds the seconds to represent, may be negative + * @return a {@code Duration} based on this period with the requested seconds, not null + */ + public Duration withSeconds(long seconds) { + return create(seconds, nanos); + } + + /** + * Returns a copy of this duration with the specified nano-of-second. + * <p> + * This returns a duration with the specified nano-of-second, retaining the + * seconds part of this duration. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param nanoOfSecond the nano-of-second to represent, from 0 to 999,999,999 + * @return a {@code Duration} based on this period with the requested nano-of-second, not null + * @throws DateTimeException if the nano-of-second is invalid + */ + public Duration withNanos(int nanoOfSecond) { + NANO_OF_SECOND.checkValidIntValue(nanoOfSecond); + return create(seconds, nanoOfSecond); + } + + //----------------------------------------------------------------------- + /** * Returns a copy of this duration with the specified duration added. * <p> * This instance is immutable and unaffected by this method call. * * @param duration the duration to add, positive or negative, not null
*** 550,559 **** --- 663,714 ---- return plusSeconds(duration.getSeconds()).plusNanos(duration.getNano()); } //----------------------------------------------------------------------- /** + * Returns a copy of this duration with the specified duration in standard 24 hour days added. + * <p> + * The number of days is multiplied by 86400 to obtain the number of seconds to add. + * This is based on the standard definition of a day as 24 hours. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param daysToAdd the days to add, positive or negative + * @return a {@code Duration} based on this duration with the specified days added, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration plusDays(long daysToAdd) { + return plus(Math.multiplyExact(daysToAdd, SECONDS_PER_DAY), 0); + } + + /** + * Returns a copy of this duration with the specified duration in hours added. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param hoursToAdd the hours to add, positive or negative + * @return a {@code Duration} based on this duration with the specified hours added, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration plusHours(long hoursToAdd) { + return plus(Math.multiplyExact(hoursToAdd, SECONDS_PER_HOUR), 0); + } + + /** + * Returns a copy of this duration with the specified duration in minutes added. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param minutesToAdd the minutes to add, positive or negative + * @return a {@code Duration} based on this duration with the specified minutes added, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration plusMinutes(long minutesToAdd) { + return plus(Math.multiplyExact(minutesToAdd, SECONDS_PER_MINUTE), 0); + } + + /** * Returns a copy of this duration with the specified duration in seconds added. * <p> * This instance is immutable and unaffected by this method call. * * @param secondsToAdd the seconds to add, positive or negative
*** 649,658 **** --- 804,859 ---- return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); } //----------------------------------------------------------------------- /** + * Returns a copy of this duration with the specified duration in standard 24 hour days subtracted. + * <p> + * The number of days is multiplied by 86400 to obtain the number of seconds to subtract. + * This is based on the standard definition of a day as 24 hours. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param daysToSubtract the days to subtract, positive or negative + * @return a {@code Duration} based on this duration with the specified days subtracted, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration minusDays(long daysToSubtract) { + return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract)); + } + + /** + * Returns a copy of this duration with the specified duration in hours subtracted. + * <p> + * The number of hours is multiplied by 3600 to obtain the number of seconds to subtract. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param hoursToSubtract the hours to subtract, positive or negative + * @return a {@code Duration} based on this duration with the specified hours subtracted, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration minusHours(long hoursToSubtract) { + return (hoursToSubtract == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hoursToSubtract)); + } + + /** + * Returns a copy of this duration with the specified duration in minutes subtracted. + * <p> + * The number of hours is multiplied by 60 to obtain the number of seconds to subtract. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @param minutesToSubtract the minutes to subtract, positive or negative + * @return a {@code Duration} based on this duration with the specified minutes subtracted, not null + * @throws ArithmeticException if numeric overflow occurs + */ + public Duration minusMinutes(long minutesToSubtract) { + return (minutesToSubtract == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutesToSubtract)); + } + + /** * Returns a copy of this duration with the specified duration in seconds subtracted. * <p> * This instance is immutable and unaffected by this method call. * * @param secondsToSubtract the seconds to subtract, positive or negative
*** 714,725 **** * <p> * This instance is immutable and unaffected by this method call. * * @param divisor the value to divide the duration by, positive or negative, not zero * @return a {@code Duration} based on this duration divided by the specified divisor, not null ! * @throws ArithmeticException if the divisor is zero ! * @throws ArithmeticException if numeric overflow occurs */ public Duration dividedBy(long divisor) { if (divisor == 0) { throw new ArithmeticException("Cannot divide by zero"); } --- 915,925 ---- * <p> * This instance is immutable and unaffected by this method call. * * @param divisor the value to divide the duration by, positive or negative, not zero * @return a {@code Duration} based on this duration divided by the specified divisor, not null ! * @throws ArithmeticException if the divisor is zero or if numeric overflow occurs */ public Duration dividedBy(long divisor) { if (divisor == 0) { throw new ArithmeticException("Cannot divide by zero"); }
*** 792,866 **** * <p> * This returns a temporal object of the same observable type as the input * with this duration added. * <p> * In most cases, it is clearer to reverse the calling pattern by using ! * {@link Temporal#plus(TemporalAdder)}. * <pre> * // these two lines are equivalent, but the second approach is recommended * dateTime = thisDuration.addTo(dateTime); * dateTime = dateTime.plus(thisDuration); * </pre> * <p> ! * A {@code Duration} can only be added to a {@code Temporal} that ! * represents an instant and can supply {@link ChronoField#INSTANT_SECONDS}. * <p> * This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { ! long instantSecs = temporal.getLong(INSTANT_SECONDS); ! long instantNanos = temporal.getLong(NANO_OF_SECOND); ! instantSecs = Math.addExact(instantSecs, seconds); ! instantNanos = Math.addExact(instantNanos, nanos); ! instantSecs = Math.addExact(instantSecs, Math.floorDiv(instantNanos, NANOS_PER_SECOND)); ! instantNanos = Math.floorMod(instantNanos, NANOS_PER_SECOND); ! return temporal.with(INSTANT_SECONDS, instantSecs).with(NANO_OF_SECOND, instantNanos); } /** * Subtracts this duration from the specified temporal object. * <p> * This returns a temporal object of the same observable type as the input * with this duration subtracted. * <p> * In most cases, it is clearer to reverse the calling pattern by using ! * {@link Temporal#minus(TemporalSubtractor)}. * <pre> * // these two lines are equivalent, but the second approach is recommended * dateTime = thisDuration.subtractFrom(dateTime); * dateTime = dateTime.minus(thisDuration); * </pre> * <p> ! * A {@code Duration} can only be subtracted from a {@code Temporal} that ! * represents an instant and can supply {@link ChronoField#INSTANT_SECONDS}. * <p> * This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { ! long instantSecs = temporal.getLong(INSTANT_SECONDS); ! long instantNanos = temporal.getLong(NANO_OF_SECOND); ! instantSecs = Math.subtractExact(instantSecs, seconds); ! instantNanos = Math.subtractExact(instantNanos, nanos); ! instantSecs = Math.addExact(instantSecs, Math.floorDiv(instantNanos, NANOS_PER_SECOND)); ! instantNanos = Math.floorMod(instantNanos, NANOS_PER_SECOND); ! return temporal.with(INSTANT_SECONDS, instantSecs).with(NANO_OF_SECOND, instantNanos); } //----------------------------------------------------------------------- /** * Converts this duration to the total length in milliseconds. * <p> * If this duration is too large to fit in a {@code long} milliseconds, then an * exception is thrown. * <p> --- 992,1109 ---- * <p> * This returns a temporal object of the same observable type as the input * with this duration added. * <p> * In most cases, it is clearer to reverse the calling pattern by using ! * {@link Temporal#plus(TemporalAmount)}. * <pre> * // these two lines are equivalent, but the second approach is recommended * dateTime = thisDuration.addTo(dateTime); * dateTime = dateTime.plus(thisDuration); * </pre> * <p> ! * The calculation will add the seconds, then nanos. ! * Only non-zero amounts will be added. * <p> * This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to add * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal addTo(Temporal temporal) { ! if (seconds != 0) { ! temporal = temporal.plus(seconds, SECONDS); ! } ! if (nanos != 0) { ! temporal = temporal.plus(nanos, NANOS); ! } ! return temporal; } /** * Subtracts this duration from the specified temporal object. * <p> * This returns a temporal object of the same observable type as the input * with this duration subtracted. * <p> * In most cases, it is clearer to reverse the calling pattern by using ! * {@link Temporal#minus(TemporalAmount)}. * <pre> * // these two lines are equivalent, but the second approach is recommended * dateTime = thisDuration.subtractFrom(dateTime); * dateTime = dateTime.minus(thisDuration); * </pre> * <p> ! * The calculation will subtract the seconds, then nanos. ! * Only non-zero amounts will be added. * <p> * This instance is immutable and unaffected by this method call. * * @param temporal the temporal object to adjust, not null * @return an object of the same type with the adjustment made, not null * @throws DateTimeException if unable to subtract * @throws ArithmeticException if numeric overflow occurs */ @Override public Temporal subtractFrom(Temporal temporal) { ! if (seconds != 0) { ! temporal = temporal.minus(seconds, SECONDS); ! } ! if (nanos != 0) { ! temporal = temporal.minus(nanos, NANOS); ! } ! return temporal; } //----------------------------------------------------------------------- /** + * Gets the number of minutes in this duration. + * <p> + * This returns the total number of minutes in the duration by dividing the + * number of seconds by 86400. + * This is based on the standard definition of a day as 24 hours. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @return the number of minutes in the duration, may be negative + */ + public long toDays() { + return seconds / SECONDS_PER_DAY; + } + + /** + * Gets the number of minutes in this duration. + * <p> + * This returns the total number of minutes in the duration by dividing the + * number of seconds by 3600. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @return the number of minutes in the duration, may be negative + */ + public long toHours() { + return seconds / SECONDS_PER_HOUR; + } + + /** + * Gets the number of minutes in this duration. + * <p> + * This returns the total number of minutes in the duration by dividing the + * number of seconds by 60. + * <p> + * This instance is immutable and unaffected by this method call. + * + * @return the number of minutes in the duration, may be negative + */ + public long toMinutes() { + return seconds / SECONDS_PER_MINUTE; + } + + /** * Converts this duration to the total length in milliseconds. * <p> * If this duration is too large to fit in a {@code long} milliseconds, then an * exception is thrown. * <p>
*** 885,895 **** * * @return the total length of the duration in nanoseconds * @throws ArithmeticException if numeric overflow occurs */ public long toNanos() { ! long millis = Math.multiplyExact(seconds, 1000_000_000); millis = Math.addExact(millis, nanos); return millis; } //----------------------------------------------------------------------- --- 1128,1138 ---- * * @return the total length of the duration in nanoseconds * @throws ArithmeticException if numeric overflow occurs */ public long toNanos() { ! long millis = Math.multiplyExact(seconds, NANOS_PER_SECOND); millis = Math.addExact(millis, nanos); return millis; } //-----------------------------------------------------------------------
*** 909,942 **** return cmp; } return nanos - otherDuration.nanos; } - /** - * Checks if this duration is greater than the specified {@code Duration}. - * <p> - * The comparison is based on the total length of the durations. - * - * @param otherDuration the other duration to compare to, not null - * @return true if this duration is greater than the specified duration - */ - public boolean isGreaterThan(Duration otherDuration) { - return compareTo(otherDuration) > 0; - } - - /** - * Checks if this duration is less than the specified {@code Duration}. - * <p> - * The comparison is based on the total length of the durations. - * - * @param otherDuration the other duration to compare to, not null - * @return true if this duration is less than the specified duration - */ - public boolean isLessThan(Duration otherDuration) { - return compareTo(otherDuration) < 0; - } - //----------------------------------------------------------------------- /** * Checks if this duration is equal to the specified {@code Duration}. * <p> * The comparison is based on the total length of the durations. --- 1152,1161 ----
*** 968,1000 **** } //----------------------------------------------------------------------- /** * A string representation of this duration using ISO-8601 seconds ! * based representation, such as {@code PT12.345S}. * <p> ! * The format of the returned string will be {@code PTnS} where n is ! * the seconds and fractional seconds of the duration. * * @return an ISO-8601 representation of this duration, not null */ @Override public String toString() { StringBuilder buf = new StringBuilder(24); buf.append("PT"); ! if (seconds < 0 && nanos > 0) { ! if (seconds == -1) { buf.append("-0"); } else { ! buf.append(seconds + 1); } } else { ! buf.append(seconds); } if (nanos > 0) { int pos = buf.length(); ! if (seconds < 0) { buf.append(2 * NANOS_PER_SECOND - nanos); } else { buf.append(nanos + NANOS_PER_SECOND); } while (buf.charAt(buf.length() - 1) == '0') { --- 1187,1247 ---- } //----------------------------------------------------------------------- /** * A string representation of this duration using ISO-8601 seconds ! * based representation, such as {@code PT8H6M12.345S}. * <p> ! * The format of the returned string will be {@code PTnHnMnS}, where n is ! * the relevant hours, minutes or seconds part of the duration. ! * Any fractional seconds are placed after a decimal point i the seconds section. ! * If a section has a zero value, it is omitted. ! * The hours, minutes and seconds will all have the same sign. ! * <p> ! * Examples: ! * <pre> ! * "20.345 seconds" -> "PT20.345S ! * "15 minutes" (15 * 60 seconds) -> "PT15M" ! * "10 hours" (10 * 3600 seconds) -> "PT10H" ! * "2 days" (2 * 86400 seconds) -> "PT48H" ! * </pre> ! * Note that multiples of 24 hours are not output as days to avoid confusion ! * with {@code Period}. * * @return an ISO-8601 representation of this duration, not null */ @Override public String toString() { + if (this == ZERO) { + return "PT0S"; + } + long hours = seconds / SECONDS_PER_HOUR; + int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE); + int secs = (int) (seconds % SECONDS_PER_MINUTE); StringBuilder buf = new StringBuilder(24); buf.append("PT"); ! if (hours != 0) { ! buf.append(hours).append('H'); ! } ! if (minutes != 0) { ! buf.append(minutes).append('M'); ! } ! if (secs == 0 && nanos == 0 && buf.length() > 2) { ! return buf.toString(); ! } ! if (secs < 0 && nanos > 0) { ! if (secs == -1) { buf.append("-0"); } else { ! buf.append(secs + 1); } } else { ! buf.append(secs); } if (nanos > 0) { int pos = buf.length(); ! if (secs < 0) { buf.append(2 * NANOS_PER_SECOND - nanos); } else { buf.append(nanos + NANOS_PER_SECOND); } while (buf.charAt(buf.length() - 1) == '0') {