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