2 ******************************************************************************
3 * Copyright (C) 2007, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
8 package com.ibm.icu.impl.duration;
10 import com.ibm.icu.impl.duration.impl.DataRecord.ETimeLimit;
13 * Represents an approximate duration in multiple TimeUnits. Each unit,
14 * if set, has a count (which can be fractional and must be non-negative).
15 * In addition Period can either represent the duration as being into the past
16 * or future, and as being more or less than the defined value.
18 * Use a PeriodFormatter to convert a Period to a String.
20 * Periods are immutable. Mutating operations return the new
21 * result leaving the original unchanged.
24 * Period p1 = Period.at(3, WEEK).and(2, DAY).inFuture();
25 * Period p2 = p1.and(12, HOUR);</pre>
27 public final class Period {
29 final boolean inFuture;
33 * Constructs a Period representing a duration of
34 * count units extending into the past.
35 * @param count the number of units, must be non-negative
36 * @param unit the unit
37 * @return the new Period
39 public static Period at(float count, TimeUnit unit) {
41 return new Period(ETimeLimit.NOLIMIT, false, count, unit);
45 * Constructs a Period representing a duration more than
46 * count units extending into the past.
47 * @param count the number of units. must be non-negative
48 * @param unit the unit
49 * @return the new Period
51 public static Period moreThan(float count, TimeUnit unit) {
53 return new Period(ETimeLimit.MT, false, count, unit);
57 * Constructs a Period representing a duration
58 * less than count units extending into the past.
59 * @param count the number of units. must be non-negative
60 * @param unit the unit
61 * @return the new Period
63 public static Period lessThan(float count, TimeUnit unit) {
65 return new Period(ETimeLimit.LT, false, count, unit);
69 * Set the given unit to have the given count. Marks the
70 * unit as having been set. This can be used to set
71 * multiple units, or to reset a unit to have a new count.
72 * This does <b>not</b> add the count to an existing count
75 * @param count the number of units. must be non-negative
76 * @param unit the unit
77 * @return the new Period
79 public Period and(float count, TimeUnit unit) {
81 return setTimeUnitValue(unit, count);
85 * Mark the given unit as not being set.
87 * @param unit the unit to unset
88 * @return the new Period
90 public Period omit(TimeUnit unit) {
91 return setTimeUnitInternalValue(unit, 0);
95 * Mark the duration as being at the defined duration.
97 * @return the new Period
100 return setTimeLimit(ETimeLimit.NOLIMIT);
104 * Mark the duration as being more than the defined duration.
106 * @return the new Period
108 public Period moreThan() {
109 return setTimeLimit(ETimeLimit.MT);
113 * Mark the duration as being less than the defined duration.
115 * @return the new Period
117 public Period lessThan() {
118 return setTimeLimit(ETimeLimit.LT);
122 * Mark the time as being in the future.
124 * @return the new Period
126 public Period inFuture() {
127 return setFuture(true);
131 * Mark the duration as extending into the past.
133 * @return the new Period
135 public Period inPast() {
136 return setFuture(false);
140 * Mark the duration as extending into the future if
141 * future is true, and into the past otherwise.
143 * @param future true if the time is in the future
144 * @return the new Period
146 public Period inFuture(boolean future) {
147 return setFuture(future);
151 * Mark the duration as extending into the past if
152 * past is true, and into the future otherwise.
154 * @param past true if the time is in the past
155 * @return the new Period
157 public Period inPast(boolean past) {
158 return setFuture(!past);
162 * Returns true if any unit is set.
163 * @return true if any unit is set
165 public boolean isSet() {
166 for (int i = 0; i < counts.length; ++i) {
167 if (counts[i] != 0) {
175 * Returns true if the given unit is set.
176 * @param unit the unit to test
177 * @return true if the given unit is set.
179 public boolean isSet(TimeUnit unit) {
180 return counts[unit.ordinal] > 0;
184 * Returns the count for the specified unit. If the
185 * unit is not set, returns 0.
186 * @param unit the unit to test
189 public float getCount(TimeUnit unit) {
190 int ord = unit.ordinal;
191 if (counts[ord] == 0) {
194 return (counts[ord] - 1)/1000f;
198 * Returns true if this represents a
199 * duration into the future.
200 * @return true if this represents a
201 * duration into the future.
203 public boolean isInFuture() {
208 * Returns true if this represents a
209 * duration into the past
210 * @return true if this represents a
211 * duration into the past
213 public boolean isInPast () {
218 * Returns true if this represents a duration in
219 * excess of the defined duration.
220 * @return true if this represents a duration in
221 * excess of the defined duration.
223 public boolean isMoreThan() {
224 return timeLimit == ETimeLimit.MT;
228 * Returns true if this represents a duration
229 * less than the defined duration.
230 * @return true if this represents a duration
231 * less than the defined duration.
233 public boolean isLessThan() {
234 return timeLimit == ETimeLimit.LT;
238 * Returns true if rhs extends Period and
239 * the two Periods are equal.
240 * @param rhs the object to compare to
241 * @return true if rhs is a Period and is equal to this
243 public boolean equals(Object rhs) {
245 return equals((Period)rhs);
247 catch (ClassCastException e) {
253 * Returns true if the same units are defined with
254 * the same counts, both extend into the future or both into the
255 * past, and if the limits (at, more than, less than) are the same.
256 * Note that this means that a period of 1000ms and a period of 1sec
257 * will not compare equal.
259 * @param rhs the period to compare to
260 * @return true if the two periods are equal
262 public boolean equals(Period rhs) {
264 this.timeLimit == rhs.timeLimit &&
265 this.inFuture == rhs.inFuture) {
266 for (int i = 0; i < counts.length; ++i) {
267 if (counts[i] != rhs.counts[i]) {
277 * Returns the hashCode.
278 * @return the hashCode
280 public int hashCode() {
281 int hc = (timeLimit << 1) | (inFuture ? 1 : 0);
282 for (int i = 0; i < counts.length; ++i) {
283 hc = (hc << 2) ^ counts[i];
289 * Private constructor used by static factory methods.
291 private Period(int limit, boolean future, float count, TimeUnit unit) {
292 this.timeLimit = (byte) limit;
293 this.inFuture = future;
294 this.counts = new int[TimeUnit.units.length];
295 this.counts[unit.ordinal] = (int)(count * 1000) + 1;
299 * Package private constructor used by setters and factory.
301 Period(int timeLimit, boolean inFuture, int[] counts) {
302 this.timeLimit = (byte) timeLimit;
303 this.inFuture = inFuture;
304 this.counts = counts;
308 * Set the unit's internal value, converting from float to int.
310 private Period setTimeUnitValue(TimeUnit unit, float value) {
312 throw new IllegalArgumentException("value: " + value);
314 return setTimeUnitInternalValue(unit, (int)(value * 1000) + 1);
318 * Sets the period to have the provided value, 1/1000 of the
319 * unit plus 1. Thus unset values are '0', 1' is the set value '0',
320 * 2 is the set value '1/1000', 3 is the set value '2/1000' etc.
321 * @param p the period to change
322 * @param value the int value as described above.
323 * @eturn the new Period object.
325 private Period setTimeUnitInternalValue(TimeUnit unit, int value) {
326 int ord = unit.ordinal;
327 if (counts[ord] != value) {
328 int[] newCounts = new int[counts.length];
329 for (int i = 0; i < counts.length; ++i) {
330 newCounts[i] = counts[i];
332 newCounts[ord] = value;
333 return new Period(timeLimit, inFuture, newCounts);
339 * Sets whether this defines a future time.
340 * @param future true if the time is in the future
341 * @return the new Period
343 private Period setFuture(boolean future) {
344 if (this.inFuture != future) {
345 return new Period(timeLimit, future, counts);
351 * Sets whether this is more than, less than, or
352 * 'about' the specified time.
353 * @param limit the kind of limit
354 * @return the new Period
356 private Period setTimeLimit(byte limit) {
357 if (this.timeLimit != limit) {
358 return new Period(limit, inFuture, counts);
367 private static void checkCount(float count) {
369 throw new IllegalArgumentException("count (" + count +
370 ") cannot be negative");