com.ibm.icu.impl.duration.Period Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
* Copyright (C) 2007, International Business Machines Corporation and *
* others. All Rights Reserved. *
******************************************************************************
*/
package com.ibm.icu.impl.duration;
import com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit;
/**
* Represents an approximate duration in multiple TimeUnits. Each unit,
* if set, has a count (which can be fractional and must be non-negative).
* In addition Period can either represent the duration as being into the past
* or future, and as being more or less than the defined value.
*
* Use a PeriodFormatter to convert a Period to a String.
*
* Periods are immutable. Mutating operations return the new
* result leaving the original unchanged.
*
* Example:
* Period p1 = Period.at(3, WEEK).and(2, DAY).inFuture();
* Period p2 = p1.and(12, HOUR);
*/
public final class Period {
final byte timeLimit;
final boolean inFuture;
final int[] counts;
/**
* Constructs a Period representing a duration of
* count units extending into the past.
* @param count the number of units, must be non-negative
* @param unit the unit
* @return the new Period
*/
public static Period at(float count, TimeUnit unit) {
checkCount(count);
return new Period(ETimeLimit.NOLIMIT, false, count, unit);
}
/**
* Constructs a Period representing a duration more than
* count units extending into the past.
* @param count the number of units. must be non-negative
* @param unit the unit
* @return the new Period
*/
public static Period moreThan(float count, TimeUnit unit) {
checkCount(count);
return new Period(ETimeLimit.MT, false, count, unit);
}
/**
* Constructs a Period representing a duration
* less than count units extending into the past.
* @param count the number of units. must be non-negative
* @param unit the unit
* @return the new Period
*/
public static Period lessThan(float count, TimeUnit unit) {
checkCount(count);
return new Period(ETimeLimit.LT, false, count, unit);
}
/**
* Set the given unit to have the given count. Marks the
* unit as having been set. This can be used to set
* multiple units, or to reset a unit to have a new count.
* This does not add the count to an existing count
* for this unit.
*
* @param count the number of units. must be non-negative
* @param unit the unit
* @return the new Period
*/
public Period and(float count, TimeUnit unit) {
checkCount(count);
return setTimeUnitValue(unit, count);
}
/**
* Mark the given unit as not being set.
*
* @param unit the unit to unset
* @return the new Period
*/
public Period omit(TimeUnit unit) {
return setTimeUnitInternalValue(unit, 0);
}
/**
* Mark the duration as being at the defined duration.
*
* @return the new Period
*/
public Period at() {
return setTimeLimit(ETimeLimit.NOLIMIT);
}
/**
* Mark the duration as being more than the defined duration.
*
* @return the new Period
*/
public Period moreThan() {
return setTimeLimit(ETimeLimit.MT);
}
/**
* Mark the duration as being less than the defined duration.
*
* @return the new Period
*/
public Period lessThan() {
return setTimeLimit(ETimeLimit.LT);
}
/**
* Mark the time as being in the future.
*
* @return the new Period
*/
public Period inFuture() {
return setFuture(true);
}
/**
* Mark the duration as extending into the past.
*
* @return the new Period
*/
public Period inPast() {
return setFuture(false);
}
/**
* Mark the duration as extending into the future if
* future is true, and into the past otherwise.
*
* @param future true if the time is in the future
* @return the new Period
*/
public Period inFuture(boolean future) {
return setFuture(future);
}
/**
* Mark the duration as extending into the past if
* past is true, and into the future otherwise.
*
* @param past true if the time is in the past
* @return the new Period
*/
public Period inPast(boolean past) {
return setFuture(!past);
}
/**
* Returns true if any unit is set.
* @return true if any unit is set
*/
public boolean isSet() {
for (int i = 0; i < counts.length; ++i) {
if (counts[i] != 0) {
return true;
}
}
return false;
}
/**
* Returns true if the given unit is set.
* @param unit the unit to test
* @return true if the given unit is set.
*/
public boolean isSet(TimeUnit unit) {
return counts[unit.ordinal] > 0;
}
/**
* Returns the count for the specified unit. If the
* unit is not set, returns 0.
* @param unit the unit to test
* @return the count
*/
public float getCount(TimeUnit unit) {
int ord = unit.ordinal;
if (counts[ord] == 0) {
return 0;
}
return (counts[ord] - 1)/1000f;
}
/**
* Returns true if this represents a
* duration into the future.
* @return true if this represents a
* duration into the future.
*/
public boolean isInFuture() {
return inFuture;
}
/**
* Returns true if this represents a
* duration into the past
* @return true if this represents a
* duration into the past
*/
public boolean isInPast () {
return !inFuture;
}
/**
* Returns true if this represents a duration in
* excess of the defined duration.
* @return true if this represents a duration in
* excess of the defined duration.
*/
public boolean isMoreThan() {
return timeLimit == ETimeLimit.MT;
}
/**
* Returns true if this represents a duration
* less than the defined duration.
* @return true if this represents a duration
* less than the defined duration.
*/
public boolean isLessThan() {
return timeLimit == ETimeLimit.LT;
}
/**
* Returns true if rhs extends Period and
* the two Periods are equal.
* @param rhs the object to compare to
* @return true if rhs is a Period and is equal to this
*/
@Override
public boolean equals(Object rhs) {
try {
return equals((Period)rhs);
}
catch (ClassCastException e) {
return false;
}
}
/**
* Returns true if the same units are defined with
* the same counts, both extend into the future or both into the
* past, and if the limits (at, more than, less than) are the same.
* Note that this means that a period of 1000ms and a period of 1sec
* will not compare equal.
*
* @param rhs the period to compare to
* @return true if the two periods are equal
*/
public boolean equals(Period rhs) {
if (rhs != null &&
this.timeLimit == rhs.timeLimit &&
this.inFuture == rhs.inFuture) {
for (int i = 0; i < counts.length; ++i) {
if (counts[i] != rhs.counts[i]) {
return false;
}
}
return true;
}
return false;
}
/**
* Returns the hashCode.
* @return the hashCode
*/
@Override
public int hashCode() {
int hc = (timeLimit << 1) | (inFuture ? 1 : 0);
for (int i = 0; i < counts.length; ++i) {
hc = (hc << 2) ^ counts[i];
}
return hc;
}
/**
* Private constructor used by static factory methods.
*/
private Period(int limit, boolean future, float count, TimeUnit unit) {
this.timeLimit = (byte) limit;
this.inFuture = future;
this.counts = new int[TimeUnit.units.length];
this.counts[unit.ordinal] = (int)(count * 1000) + 1;
}
/**
* Package private constructor used by setters and factory.
*/
Period(int timeLimit, boolean inFuture, int[] counts) {
this.timeLimit = (byte) timeLimit;
this.inFuture = inFuture;
this.counts = counts;
}
/**
* Set the unit's internal value, converting from float to int.
*/
private Period setTimeUnitValue(TimeUnit unit, float value) {
if (value < 0) {
throw new IllegalArgumentException("value: " + value);
}
return setTimeUnitInternalValue(unit, (int)(value * 1000) + 1);
}
/**
* Sets the period to have the provided value, 1/1000 of the
* unit plus 1. Thus unset values are '0', 1' is the set value '0',
* 2 is the set value '1/1000', 3 is the set value '2/1000' etc.
* @param p the period to change
* @param value the int value as described above.
* @eturn the new Period object.
*/
private Period setTimeUnitInternalValue(TimeUnit unit, int value) {
int ord = unit.ordinal;
if (counts[ord] != value) {
int[] newCounts = new int[counts.length];
for (int i = 0; i < counts.length; ++i) {
newCounts[i] = counts[i];
}
newCounts[ord] = value;
return new Period(timeLimit, inFuture, newCounts);
}
return this;
}
/**
* Sets whether this defines a future time.
* @param future true if the time is in the future
* @return the new Period
*/
private Period setFuture(boolean future) {
if (this.inFuture != future) {
return new Period(timeLimit, future, counts);
}
return this;
}
/**
* Sets whether this is more than, less than, or
* 'about' the specified time.
* @param limit the kind of limit
* @return the new Period
*/
private Period setTimeLimit(byte limit) {
if (this.timeLimit != limit) {
return new Period(limit, inFuture, counts);
}
return this;
}
/**
* Validate count.
*/
private static void checkCount(float count) {
if (count < 0) {
throw new IllegalArgumentException("count (" + count +
") cannot be negative");
}
}
}