1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.util;
29
30 import java.text.ParseException;
31 import java.time.Duration;
32 import java.time.temporal.ChronoUnit;
33 import java.util.Locale;
34 import java.util.Objects;
35 import java.util.concurrent.TimeUnit;
36
37 import org.apache.hc.core5.annotation.Contract;
38 import org.apache.hc.core5.annotation.ThreadingBehavior;
39
40
41
42
43
44
45 @Contract(threading = ThreadingBehavior.IMMUTABLE)
46 public class TimeValue implements Comparable<TimeValue> {
47
48 static final int INT_UNDEFINED = -1;
49
50
51
52
53 public static final TimeValue MAX_VALUE = ofDays(Long.MAX_VALUE);
54
55
56
57
58 public static final TimeValue NEG_ONE_MILLISECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.MILLISECONDS);
59
60
61
62
63 public static final TimeValue NEG_ONE_SECOND = TimeValue.of(INT_UNDEFINED, TimeUnit.SECONDS);
64
65
66
67
68 public static final TimeValue ZERO_MILLISECONDS = TimeValue.of(0, TimeUnit.MILLISECONDS);
69
70
71
72
73
74
75
76
77
78
79
80
81 public static int asBoundInt(final long value) {
82 if (value > Integer.MAX_VALUE) {
83 return Integer.MAX_VALUE;
84 } else if (value < Integer.MIN_VALUE) {
85 return Integer.MIN_VALUE;
86 }
87 return (int) value;
88 }
89
90
91
92
93
94
95
96
97
98 public static <T extends TimeValue> T defaultsTo(final T timeValue, final T defaultValue) {
99 return timeValue != null ? timeValue : defaultValue;
100 }
101
102
103
104
105
106
107
108
109 public static TimeValue defaultsToNegativeOneMillisecond(final TimeValue timeValue) {
110 return defaultsTo(timeValue, NEG_ONE_MILLISECOND);
111 }
112
113
114
115
116
117
118
119
120 public static TimeValue defaultsToNegativeOneSecond(final TimeValue timeValue) {
121 return defaultsTo(timeValue, NEG_ONE_SECOND);
122 }
123
124
125
126
127
128
129
130
131 public static TimeValue defaultsToZeroMilliseconds(final TimeValue timeValue) {
132 return defaultsTo(timeValue, ZERO_MILLISECONDS);
133 }
134
135 public static boolean isNonNegative(final TimeValue timeValue) {
136 return timeValue != null && timeValue.getDuration() >= 0;
137 }
138
139 public static boolean isPositive(final TimeValue timeValue) {
140 return timeValue != null && timeValue.getDuration() > 0;
141 }
142
143
144
145
146
147
148
149
150 public static TimeValue of(final long duration, final TimeUnit timeUnit) {
151 return new TimeValue(duration, timeUnit);
152 }
153
154
155
156
157
158
159
160
161 public static TimeValue of(final Duration duration) {
162 final long seconds = duration.getSeconds();
163 final long nanoOfSecond = duration.getNano();
164 if (seconds == 0) {
165
166 return of(nanoOfSecond, TimeUnit.NANOSECONDS);
167 } else if (nanoOfSecond == 0) {
168
169 return of(seconds, TimeUnit.SECONDS);
170 }
171
172 try {
173 return of(duration.toNanos(), TimeUnit.NANOSECONDS);
174 } catch (final ArithmeticException e) {
175 try {
176 return of(duration.toMillis(), TimeUnit.MILLISECONDS);
177 } catch (final ArithmeticException e1) {
178
179 return of(seconds, TimeUnit.SECONDS);
180 }
181 }
182 }
183
184 public static TimeValue ofDays(final long days) {
185 return of(days, TimeUnit.DAYS);
186 }
187
188 public static TimeValue ofHours(final long hours) {
189 return of(hours, TimeUnit.HOURS);
190 }
191
192 public static TimeValue ofMicroseconds(final long microseconds) {
193 return of(microseconds, TimeUnit.MICROSECONDS);
194 }
195
196 public static TimeValue ofMilliseconds(final long millis) {
197 return of(millis, TimeUnit.MILLISECONDS);
198 }
199
200 public static TimeValue ofMinutes(final long minutes) {
201 return of(minutes, TimeUnit.MINUTES);
202 }
203
204 public static TimeValue ofNanoseconds(final long nanoseconds) {
205 return of(nanoseconds, TimeUnit.NANOSECONDS);
206 }
207
208 public static TimeValue ofSeconds(final long seconds) {
209 return of(seconds, TimeUnit.SECONDS);
210 }
211
212
213
214
215
216
217 static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
218 switch (Objects.requireNonNull(timeUnit)) {
219 case NANOSECONDS:
220 return ChronoUnit.NANOS;
221 case MICROSECONDS:
222 return ChronoUnit.MICROS;
223 case MILLISECONDS:
224 return ChronoUnit.MILLIS;
225 case SECONDS:
226 return ChronoUnit.SECONDS;
227 case MINUTES:
228 return ChronoUnit.MINUTES;
229 case HOURS:
230 return ChronoUnit.HOURS;
231 case DAYS:
232 return ChronoUnit.DAYS;
233 default:
234 throw new IllegalArgumentException(timeUnit.toString());
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255 public static TimeValue parse(final String value) throws ParseException {
256 final String split[] = value.trim().split("\\s+");
257 if (split.length < 2) {
258 throw new IllegalArgumentException(
259 String.format("Expected format for <Long><SPACE><java.util.concurrent.TimeUnit>: %s", value));
260 }
261 final String clean0 = split[0].trim();
262 final String clean1 = split[1].trim().toUpperCase(Locale.ROOT);
263 final String timeUnitStr = clean1.endsWith("S") ? clean1 : clean1 + "S";
264 return TimeValue.of(Long.parseLong(clean0), TimeUnit.valueOf(timeUnitStr));
265 }
266
267 private final long duration;
268
269 private final TimeUnit timeUnit;
270
271 TimeValue(final long duration, final TimeUnit timeUnit) {
272 super();
273 this.duration = duration;
274 this.timeUnit = Args.notNull(timeUnit, "timeUnit");
275 }
276
277 public long convert(final TimeUnit targetTimeUnit) {
278 Args.notNull(targetTimeUnit, "timeUnit");
279 return targetTimeUnit.convert(duration, timeUnit);
280 }
281
282 @Override
283 public boolean equals(final Object obj) {
284 if (this == obj) {
285 return true;
286 }
287 if (obj instanceof TimeValue) {
288 final TimeValue that = (TimeValue) obj;
289 final long thisDuration = this.convert(TimeUnit.NANOSECONDS);
290 final long thatDuration = that.convert(TimeUnit.NANOSECONDS);
291 return thisDuration == thatDuration;
292 }
293 return false;
294 }
295
296
297
298
299
300
301
302
303
304
305 public TimeValue divide(final long divisor) {
306 final long newDuration = duration / divisor;
307 return of(newDuration, timeUnit);
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321 public TimeValue divide(final long divisor, final TimeUnit targetTimeUnit) {
322 return of(convert(targetTimeUnit) / divisor, targetTimeUnit);
323 }
324
325 public long getDuration() {
326 return duration;
327 }
328
329 public TimeUnit getTimeUnit() {
330 return timeUnit;
331 }
332
333 @Override
334 public int hashCode() {
335 int hash = LangUtils.HASH_SEED;
336 hash = LangUtils.hashCode(hash, this.convert(TimeUnit.NANOSECONDS));
337 return hash;
338 }
339
340 public TimeValue min(final TimeValue other) {
341 return this.compareTo(other) > 0 ? other : this;
342 }
343
344 private TimeUnit min(final TimeUnit other) {
345 return scale() > scale(other) ? other : getTimeUnit();
346 }
347
348 private int scale() {
349 return scale(timeUnit);
350 }
351
352
353
354
355
356
357
358
359 private int scale(final TimeUnit tUnit) {
360 switch (tUnit) {
361 case NANOSECONDS:
362 return 1;
363 case MICROSECONDS:
364 return 2;
365 case MILLISECONDS:
366 return 3;
367 case SECONDS:
368 return 4;
369 case MINUTES:
370 return 5;
371 case HOURS:
372 return 6;
373 case DAYS:
374 return 7;
375 default:
376
377 throw new IllegalStateException();
378 }
379 }
380
381 public void sleep() throws InterruptedException {
382 timeUnit.sleep(duration);
383 }
384
385 public void timedJoin(final Thread thread) throws InterruptedException {
386 timeUnit.timedJoin(thread, duration);
387 }
388
389 public void timedWait(final Object obj) throws InterruptedException {
390 timeUnit.timedWait(obj, duration);
391 }
392
393 public long toDays() {
394 return timeUnit.toDays(duration);
395 }
396
397
398
399
400
401
402
403 public Duration toDuration() {
404 return duration == 0 ? Duration.ZERO : Duration.of(duration, toChronoUnit(timeUnit));
405 }
406
407 public long toHours() {
408 return timeUnit.toHours(duration);
409 }
410
411 public long toMicroseconds() {
412 return timeUnit.toMicros(duration);
413 }
414
415 public long toMilliseconds() {
416 return timeUnit.toMillis(duration);
417 }
418
419 public int toMillisecondsIntBound() {
420 return asBoundInt(toMilliseconds());
421 }
422
423 public long toMinutes() {
424 return timeUnit.toMinutes(duration);
425 }
426
427 public long toNanoseconds() {
428 return timeUnit.toNanos(duration);
429 }
430
431 public long toSeconds() {
432 return timeUnit.toSeconds(duration);
433 }
434
435 public int toSecondsIntBound() {
436 return asBoundInt(toSeconds());
437 }
438
439 @Override
440 public int compareTo(final TimeValue other) {
441 final TimeUnit targetTimeUnit = min(other.getTimeUnit());
442 return Long.compare(convert(targetTimeUnit), other.convert(targetTimeUnit));
443 }
444
445 @Override
446 public String toString() {
447 return String.format("%d %s", duration, timeUnit);
448 }
449
450 public Timeout toTimeout() {
451 return Timeout.of(duration, timeUnit);
452 }
453
454 }