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