1 /* Copyright 2022-2025 Luc Maisonobe
2 * Licensed to CS GROUP (CS) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * CS licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.orekit.time;
18
19 import org.hipparchus.exception.LocalizedCoreFormats;
20 import org.hipparchus.util.FastMath;
21 import org.orekit.errors.OrekitException;
22 import org.orekit.errors.OrekitMessages;
23
24 import java.io.Serializable;
25 import java.util.concurrent.TimeUnit;
26
27 /** This class represents a time range split into seconds and attoseconds.
28 * <p>
29 * Instances of this class may either be interpreted as offsets from a reference
30 * date, or they may be interpreted as durations. Negative values represent
31 * dates earlier than the reference date in the first interpretation, and
32 * negative durations in the second interpretation.
33 * </p>
34 * <p>
35 * The whole number of seconds is stored as signed primitive long, so the range
36 * of dates that can be represented is ±292 billion years. The fractional part
37 * within the second is stored as non-negative primitive long with fixed precision
38 * at a resolution of one attosecond (10⁻¹⁸s). The choice of attoseconds allows
39 * to represent exactly all important offsets (between TT and TAI, or between UTC
40 * and TAI during the linear eras), as well as all times converted from standard
41 * Java Instant, Date or TimeUnit classes. It also allows simple computation as
42 * adding or subtracting a few values in attoseconds that are less than one second
43 * does not overflow (a primitive long could hold any values between ±9.22s in
44 * attoseconds so simple additions and subtractions followed by handling a carry
45 * to bring the value back between 0 and 10¹⁸ is straightforward). There are also
46 * special encodings (internally using negative longs in the fractional part) to
47 * represent {@link #NaN}, {@link #POSITIVE_INFINITY} and {@link #NEGATIVE_INFINITY}.
48 * </p>
49 * @author Luc Maisonobe
50 * @see AbsoluteDate
51 * @see FieldAbsoluteDate
52 * @since 13.0
53 */
54 public class TimeOffset
55 implements Comparable<TimeOffset>, Serializable {
56
57 /** Split time representing 0. */
58 public static final TimeOffset ZERO = new TimeOffset(0L, 0L);
59
60 /** Split time representing 1 attosecond. */
61 public static final TimeOffset ATTOSECOND = new TimeOffset(0L, 1L);
62
63 /** Split time representing 1 femtosecond. */
64 public static final TimeOffset FEMTOSECOND = new TimeOffset(0L, 1000L);
65
66 /** Split time representing 1 picosecond. */
67 public static final TimeOffset PICOSECOND = new TimeOffset(0L, 1000000L);
68
69 /** Split time representing 1 nanosecond. */
70 public static final TimeOffset NANOSECOND = new TimeOffset(0L, 1000000000L);
71
72 /** Split time representing 1 microsecond. */
73 public static final TimeOffset MICROSECOND = new TimeOffset(0L, 1000000000000L);
74
75 /** Split time representing 1 millisecond. */
76 public static final TimeOffset MILLISECOND = new TimeOffset(0L, 1000000000000000L);
77
78 /** Split time representing 1 second. */
79 public static final TimeOffset SECOND = new TimeOffset(1L, 0L);
80
81 /** Split time representing 1 minute. */
82 public static final TimeOffset MINUTE = new TimeOffset(60L, 0L);
83
84 /** Split time representing 1 hour. */
85 public static final TimeOffset HOUR = new TimeOffset(3600L, 0L);
86
87 /** Split time representing 1 day. */
88 public static final TimeOffset DAY = new TimeOffset(86400L, 0L);
89
90 /** Split time representing 1 day that includes an additional leap second. */
91 public static final TimeOffset DAY_WITH_POSITIVE_LEAP = new TimeOffset(86401L, 0L);
92
93 // CHECKSTYLE: stop ConstantName
94 /** Split time representing a NaN. */
95 public static final TimeOffset NaN = new TimeOffset(Double.NaN);
96 // CHECKSTYLE: resume ConstantName
97
98 /** Split time representing negative infinity. */
99 public static final TimeOffset NEGATIVE_INFINITY = new TimeOffset(Double.NEGATIVE_INFINITY);
100
101 /** Split time representing positive infinity. */
102 public static final TimeOffset POSITIVE_INFINITY = new TimeOffset(Double.POSITIVE_INFINITY);
103
104 /** Indicator for NaN time (bits pattern arbitrarily selected to avoid hashcode collisions). */
105 private static final long NAN_INDICATOR = -0XFFL;
106
107 /** Indicator for positive infinite time (bits pattern arbitrarily selected to avoid hashcode collisions). */
108 private static final long POSITIVE_INFINITY_INDICATOR = -0XFF00L;
109
110 /** Indicator for negative infinite time (bits pattern arbitrarily selected to avoid hashcode collisions). */
111 private static final long NEGATIVE_INFINITY_INDICATOR = -0XFF0000L;
112
113 /** Milliseconds in one second. */
114 private static final long MILLIS_IN_SECOND = 1000L;
115
116 /** Microseconds in one second. */
117 private static final long MICROS_IN_SECOND = 1000000L;
118
119 /** Nanoseconds in one second. */
120 private static final long NANOS_IN_SECOND = 1000000000L;
121
122 /** Attoseconds in one second. */
123 private static final long ATTOS_IN_SECOND = 1000000000000000000L;
124
125 /** Attoseconds in one half-second. */
126 private static final long ATTOS_IN_HALF_SECOND = 500000000000000000L;
127
128 /** Factor to split long for multiplications.
129 * <p>
130 * It is important that SPLIT * SPLIT = ATTOS_IN_SECOND.
131 * </p>
132 */
133 private static final long SPLIT = 1000000000L;
134
135 /** Number of digits after separator for attoseconds. */
136 private static final int DIGITS_ATTOS = 18;
137
138 /** Multipliers for parsing partial strings. */
139 // CHECKSTYLE: stop Indentation check
140 private static final long[] MULTIPLIERS = new long[] {
141 1L,
142 10L,
143 100L,
144 1000L,
145 10000L,
146 100000L,
147 1000000L,
148 10000000L,
149 100000000L,
150 1000000000L,
151 10000000000L,
152 100000000000L,
153 1000000000000L,
154 10000000000000L,
155 100000000000000L,
156 1000000000000000L,
157 10000000000000000L,
158 100000000000000000L,
159 1000000000000000000L
160 };
161 // CHECKSTYLE: resume Indentation check
162
163 /** Serializable UID. */
164 private static final long serialVersionUID = 20240711L;
165
166 /** Seconds part. */
167 private final long seconds;
168
169 /** AttoSeconds part. */
170 private final long attoSeconds;
171
172 /**
173 * Build a time by adding several times.
174 * @param times times to add
175 */
176 public TimeOffset(final TimeOffset... times) {
177 final RunningSum runningSum = new RunningSum();
178 for (final TimeOffset time : times) {
179 runningSum.add(time);
180 }
181 final TimeOffset sum = runningSum.normalize();
182 this.seconds = sum.getSeconds();
183 this.attoSeconds = sum.getAttoSeconds();
184 }
185
186 /**
187 * Build a time from its components.
188 * <p>
189 * The components will be normalized so that {@link #getAttoSeconds()}
190 * returns a value between {@code 0L} and {1000000000000000000L}
191 * </p>
192 * @param seconds seconds part
193 * @param attoSeconds attoseconds part
194 */
195 public TimeOffset(final long seconds, final long attoSeconds) {
196 final long qAtto = attoSeconds / ATTOS_IN_SECOND;
197 final long rAtto = attoSeconds - qAtto * ATTOS_IN_SECOND;
198 if (rAtto < 0L) {
199 this.seconds = seconds + qAtto - 1L;
200 this.attoSeconds = ATTOS_IN_SECOND + rAtto;
201 } else {
202 this.seconds = seconds + qAtto;
203 this.attoSeconds = rAtto;
204 }
205 }
206
207 /**
208 * Build a time from a value in seconds.
209 *
210 * @param time time
211 */
212 public TimeOffset(final double time) {
213 if (Double.isNaN(time)) {
214 seconds = 0L;
215 attoSeconds = NAN_INDICATOR;
216 } else if (time < Long.MIN_VALUE || time > Long.MAX_VALUE) {
217 if (time < 0L) {
218 seconds = Long.MIN_VALUE;
219 attoSeconds = NEGATIVE_INFINITY_INDICATOR;
220 } else {
221 seconds = Long.MAX_VALUE;
222 attoSeconds = POSITIVE_INFINITY_INDICATOR;
223 }
224 } else {
225 final double tiSeconds = FastMath.rint(time);
226 final double subSeconds = time - tiSeconds;
227 if (subSeconds < 0L) {
228 seconds = (long) tiSeconds - 1L;
229 attoSeconds = FastMath.round(subSeconds * ATTOS_IN_SECOND) + ATTOS_IN_SECOND;
230 } else {
231 seconds = (long) tiSeconds;
232 attoSeconds = FastMath.round(subSeconds * ATTOS_IN_SECOND);
233 }
234 }
235 }
236
237 /**
238 * Multiplicative constructor.
239 * <p>
240 * This constructors builds a split time corresponding to {@code factor} ⨉ {@code time}
241 * </p>
242 * @param factor multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
243 * @param time base time
244 */
245 public TimeOffset(final long factor, final TimeOffset time) {
246 this(factor < 0 ? time.multiply(-factor).negate() : time.multiply(factor));
247 }
248
249 /**
250 * Linear combination constructor.
251 * <p>
252 * This constructors builds a split time corresponding to
253 * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2}
254 * </p>
255 * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
256 * @param t1 first base time
257 * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
258 * @param t2 second base time
259 */
260 public TimeOffset(final long f1, final TimeOffset t1,
261 final long f2, final TimeOffset t2) {
262 this(new TimeOffset(f1, t1).add(new TimeOffset(f2, t2)));
263 }
264
265 /**
266 * Linear combination constructor.
267 * <p>
268 * This constructors builds a split time corresponding to
269 * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3}
270 * </p>
271 * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
272 * @param t1 first base time
273 * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
274 * @param t2 second base time
275 * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
276 * @param t3 third base time
277 */
278 public TimeOffset(final long f1, final TimeOffset t1,
279 final long f2, final TimeOffset t2,
280 final long f3, final TimeOffset t3) {
281 this(new TimeOffset(f1, t1).add(new TimeOffset(f2, t2)).add(new TimeOffset(f3, t3)));
282 }
283
284 /**
285 * Linear combination constructor.
286 * <p>
287 * This constructors builds a split time corresponding to
288 * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3} + {@code f4} ⨉ {@code t4}
289 * </p>
290 * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
291 * @param t1 first base time
292 * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
293 * @param t2 second base time
294 * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
295 * @param t3 third base time
296 * @param f4 fourth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
297 * @param t4 fourth base time
298 */
299 public TimeOffset(final long f1, final TimeOffset t1,
300 final long f2, final TimeOffset t2,
301 final long f3, final TimeOffset t3,
302 final long f4, final TimeOffset t4) {
303 this(new TimeOffset(f1, t1).
304 add(new TimeOffset(f2, t2)).
305 add(new TimeOffset(f3, t3)).
306 add(new TimeOffset(f4, t4)));
307 }
308
309 /**
310 * Linear combination constructor.
311 * <p>
312 * This constructors builds a split time corresponding to
313 * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3} + {@code f4} ⨉ {@code t4} + {@code f5} ⨉ {@code t5}
314 * </p>
315 * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
316 * @param t1 first base time
317 * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
318 * @param t2 second base time
319 * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
320 * @param t3 third base time
321 * @param f4 fourth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
322 * @param t4 fourth base time
323 * @param f5 fifth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
324 * @param t5 fifth base time
325 */
326 public TimeOffset(final long f1, final TimeOffset t1,
327 final long f2, final TimeOffset t2,
328 final long f3, final TimeOffset t3,
329 final long f4, final TimeOffset t4,
330 final long f5, final TimeOffset t5) {
331 this(new TimeOffset(f1, t1).
332 add(new TimeOffset(f2, t2)).
333 add(new TimeOffset(f3, t3)).
334 add(new TimeOffset(f4, t4)).
335 add(new TimeOffset(f5, t5)));
336 }
337
338 /**
339 * Build a time from a value defined in some time unit.
340 *
341 * @param time time
342 * @param unit time unit in which {@code time} is expressed
343 */
344 public TimeOffset(final long time, final TimeUnit unit) {
345 switch (unit) {
346 case DAYS: {
347 final long limit = (Long.MAX_VALUE - DAY.seconds / 2) / DAY.seconds;
348 if (time < -limit) {
349 seconds = Long.MIN_VALUE;
350 attoSeconds = NEGATIVE_INFINITY_INDICATOR;
351 } else if (time > limit) {
352 seconds = Long.MAX_VALUE;
353 attoSeconds = POSITIVE_INFINITY_INDICATOR;
354 } else {
355 seconds = time * DAY.seconds;
356 attoSeconds = 0L;
357 }
358 break;
359 }
360 case HOURS: {
361 final long limit = (Long.MAX_VALUE - HOUR.seconds / 2) / HOUR.seconds;
362 if (time < -limit) {
363 seconds = Long.MIN_VALUE;
364 attoSeconds = NEGATIVE_INFINITY_INDICATOR;
365 } else if (time > limit) {
366 seconds = Long.MAX_VALUE;
367 attoSeconds = POSITIVE_INFINITY_INDICATOR;
368 } else {
369 seconds = time * HOUR.seconds;
370 attoSeconds = 0L;
371 }
372 break;
373 }
374 case MINUTES: {
375 final long limit = (Long.MAX_VALUE - MINUTE.seconds / 2) / MINUTE.seconds;
376 if (time < -limit) {
377 seconds = Long.MIN_VALUE;
378 attoSeconds = NEGATIVE_INFINITY_INDICATOR;
379 } else if (time > limit) {
380 seconds = Long.MAX_VALUE;
381 attoSeconds = POSITIVE_INFINITY_INDICATOR;
382 } else {
383 seconds = time * MINUTE.seconds;
384 attoSeconds = 0L;
385 }
386 break;
387 }
388 case SECONDS:
389 seconds = time;
390 attoSeconds = 0L;
391 break;
392 case MILLISECONDS: {
393 final long s = time / MILLIS_IN_SECOND;
394 final long r = (time - s * MILLIS_IN_SECOND) * MILLISECOND.attoSeconds;
395 if (r < 0L) {
396 seconds = s - 1L;
397 attoSeconds = ATTOS_IN_SECOND + r;
398 } else {
399 seconds = s;
400 attoSeconds = r;
401 }
402 break;
403 }
404 case MICROSECONDS: {
405 final long s = time / MICROS_IN_SECOND;
406 final long r = (time - s * MICROS_IN_SECOND) * MICROSECOND.attoSeconds;
407 if (r < 0L) {
408 seconds = s - 1L;
409 attoSeconds = ATTOS_IN_SECOND + r;
410 } else {
411 seconds = s;
412 attoSeconds = r;
413 }
414 break;
415 }
416 case NANOSECONDS: {
417 final long s = time / NANOS_IN_SECOND;
418 final long r = (time - s * NANOS_IN_SECOND) * NANOSECOND.attoSeconds;
419 if (r < 0L) {
420 seconds = s - 1L;
421 attoSeconds = ATTOS_IN_SECOND + r;
422 } else {
423 seconds = s;
424 attoSeconds = r;
425 }
426 break;
427 }
428 default:
429 throw new OrekitException(OrekitMessages.UNKNOWN_UNIT, unit.name());
430 }
431 }
432
433 /** Copy constructor, for internal use only.
434 * @param time time to copy
435 */
436 private TimeOffset(final TimeOffset time) {
437 seconds = time.seconds;
438 attoSeconds = time.attoSeconds;
439 }
440
441 /** check if the time is zero.
442 * @return true if the time is zero
443 */
444 public boolean isZero() {
445 return seconds == 0L && attoSeconds == 0L;
446 }
447
448 /** Check if time is finite (i.e. neither {@link #isNaN() NaN} nor {@link #isInfinite() infinite)}.
449 * @return true if time is finite
450 * @see #isNaN()
451 * @see #isInfinite()
452 * @see #isNegativeInfinity()
453 * @see #isPositiveInfinity()
454 */
455 public boolean isFinite() {
456 return attoSeconds >= 0L;
457 }
458
459 /** Check if time is NaN.
460 * @return true if time is NaN
461 * @see #isFinite()
462 * @see #isInfinite()
463 * @see #isNegativeInfinity()
464 * @see #isPositiveInfinity()
465 */
466 public boolean isNaN() {
467 return attoSeconds == NAN_INDICATOR;
468 }
469
470 /** Check if time is infinity.
471 * @return true if time is infinity
472 * @see #isFinite()
473 * @see #isNaN()
474 * @see #isNegativeInfinity()
475 * @see #isPositiveInfinity()
476 */
477 public boolean isInfinite() {
478 return isPositiveInfinity() || isNegativeInfinity();
479 }
480
481 /** Check if time is positive infinity.
482 * @return true if time is positive infinity
483 * @see #isFinite()
484 * @see #isNaN()
485 * @see #isInfinite()
486 * @see #isNegativeInfinity()
487 */
488 public boolean isPositiveInfinity() {
489 return attoSeconds == POSITIVE_INFINITY_INDICATOR;
490 }
491
492 /** Check if time is negative infinity.
493 * @return true if time is negative infinity
494 * @see #isFinite()
495 * @see #isNaN()
496 * @see #isInfinite()
497 * @see #isPositiveInfinity()
498 */
499 public boolean isNegativeInfinity() {
500 return attoSeconds == NEGATIVE_INFINITY_INDICATOR;
501 }
502
503 /** Build a time by adding two times.
504 * @param t time to add
505 * @return this+t
506 */
507 public TimeOffset add(final TimeOffset t) {
508 final RunningSum runningSum = new RunningSum();
509 runningSum.add(this);
510 runningSum.add(t);
511 return runningSum.normalize();
512 }
513
514 /** Build a time by subtracting one time from the instance.
515 * @param t time to subtract
516 * @return this-t
517 */
518 public TimeOffset subtract(final TimeOffset t) {
519 if (attoSeconds < 0 || t.attoSeconds < 0) {
520 // gather all special cases in one big check to avoid rare multiple tests
521 if (isNaN() ||
522 t.isNaN() ||
523 isPositiveInfinity() && t.isPositiveInfinity() ||
524 isNegativeInfinity() && t.isNegativeInfinity()) {
525 return NaN;
526 } else if (isInfinite()) {
527 // t is either a finite time or the infinity opposite to this
528 return this;
529 } else {
530 // this is either a finite time or the infinity opposite to t
531 return t.isPositiveInfinity() ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
532 }
533 } else {
534 // regular subtraction between two finite times
535 return new TimeOffset(seconds - t.seconds, attoSeconds - t.attoSeconds);
536 }
537 }
538
539 /** Multiply the instance by a positive or zero constant.
540 * @param p multiplication factor (must be positive)
541 * @return this ⨉ p
542 */
543 public TimeOffset multiply(final long p) {
544 if (p < 0) {
545 throw new OrekitException(OrekitMessages.NOT_POSITIVE, p);
546 }
547 if (isFinite()) {
548 final TimeOffset abs = seconds < 0 ? negate() : this;
549 final long pHigh = p / SPLIT;
550 final long pLow = p - pHigh * SPLIT;
551 final long sHigh = abs.seconds / SPLIT;
552 final long sLow = abs.seconds - sHigh * SPLIT;
553 final long aHigh = abs.attoSeconds / SPLIT;
554 final long aLow = abs.attoSeconds - aHigh * SPLIT;
555 final long ps1 = pHigh * sLow + pLow * sHigh;
556 final long ps0 = pLow * sLow;
557 final long pa2 = pHigh * aHigh;
558 final long pa1 = pHigh * aLow + pLow * aHigh;
559 final long pa1High = pa1 / SPLIT;
560 final long pa1Low = pa1 - pa1High * SPLIT;
561 final long pa0 = pLow * aLow;
562
563 // check for overflow
564 if (pHigh * sHigh != 0 || ps1 / SPLIT != 0) {
565 throw new OrekitException(LocalizedCoreFormats.OVERFLOW_IN_MULTIPLICATION, abs.seconds, p);
566 }
567
568 // here we use the fact that SPLIT * SPLIT = ATTOS_IN_SECOND
569 final TimeOffset mul = new TimeOffset(SPLIT * ps1 + ps0 + pa2 + pa1High, SPLIT * pa1Low + pa0);
570 return seconds < 0 ? mul.negate() : mul;
571 } else {
572 // already NaN, +∞ or -∞, unchanged except 0 ⨉ ±∞ = NaN
573 return p == 0 ? TimeOffset.NaN : this;
574 }
575 }
576
577 /** Divide the instance by a positive constant.
578 * @param q division factor (must be strictly positive)
579 * @return this ÷ q
580 */
581 public TimeOffset divide(final int q) {
582 if (q <= 0) {
583 throw new OrekitException(OrekitMessages.NOT_STRICTLY_POSITIVE, q);
584 }
585 if (isFinite()) {
586 final long sSec = seconds / q;
587 final long rSec = seconds - sSec * q;
588 final long sK = ATTOS_IN_SECOND / q;
589 final long rK = ATTOS_IN_SECOND - sK * q;
590 final TimeOffset tsSec = new TimeOffset(0L, sSec);
591 final TimeOffset trSec = new TimeOffset(0L, rSec);
592 return new TimeOffset(tsSec.multiply(sK).multiply(q),
593 tsSec.multiply(rK),
594 trSec.multiply(sK),
595 // here, we use the fact q is a positive int (not a long!)
596 // hence rSec * rK < q² does not overflow
597 new TimeOffset(0L, (attoSeconds + rSec * rK) / q));
598 } else {
599 // already NaN, +∞ or -∞, unchanged as q > 0
600 return this;
601 }
602 }
603
604 /** Negate the instance.
605 * @return new instance corresponding to opposite time
606 */
607 public TimeOffset negate() {
608 // handle special cases
609 if (attoSeconds < 0) {
610 // gather all special cases in one big check to avoid rare multiple tests
611 return isNaN() ? this : (seconds < 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY);
612 } else {
613 // the negative number of attoseconds will be normalized back to positive by the constructor
614 return new TimeOffset(-seconds, -attoSeconds);
615 }
616 }
617
618 /** Get the time in some unit.
619 * @param unit time unit
620 * @return time in this unit, rounded to the closest long,
621 * returns arbitrarily {@link Long#MAX_VALUE} for {@link #isNaN() NaN times}
622 */
623 public long getRoundedTime(final TimeUnit unit) {
624
625 // handle special cases
626 if (attoSeconds < 0) {
627 // gather all special cases in one big check to avoid rare multiple tests
628 return (isNaN() || seconds >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE;
629 }
630
631 final long sign = seconds < 0L ? -1L : 1L;
632 switch (unit) {
633 case DAYS:
634 return sign * ((sign * seconds + DAY.seconds / 2) / DAY.seconds);
635 case HOURS:
636 return sign * ((sign * seconds + HOUR.seconds / 2) / HOUR.seconds);
637 case MINUTES:
638 return sign * ((sign * seconds + MINUTE.seconds / 2) / MINUTE.seconds);
639 case SECONDS:
640 return seconds + ((attoSeconds >= ATTOS_IN_SECOND / 2) ? 1 : 0);
641 case MILLISECONDS:
642 return seconds * MILLIS_IN_SECOND +
643 (attoSeconds + MILLISECOND.attoSeconds / 2) / MILLISECOND.attoSeconds;
644 case MICROSECONDS:
645 return seconds * MICROS_IN_SECOND +
646 (attoSeconds + MICROSECOND.attoSeconds / 2) / MICROSECOND.attoSeconds;
647 case NANOSECONDS:
648 return seconds * NANOS_IN_SECOND +
649 (attoSeconds + NANOSECOND.attoSeconds / 2) / NANOSECOND.attoSeconds;
650 default:
651 throw new OrekitException(OrekitMessages.UNKNOWN_UNIT, unit.name());
652 }
653 }
654
655 /** Get the normalized seconds part of the time.
656 * @return normalized seconds part of the time (may be negative)
657 */
658 public long getSeconds() {
659 return seconds;
660 }
661
662 /** Get the normalized attoseconds part of the time.
663 * <p>
664 * The normalized attoseconds is always between {@code 0L} and
665 * {@code 1000000000000000000L} for <em>finite</em> ranges. Note that it
666 * may reach {@code 1000000000000000000L} if for example the time is less
667 * than 1 attosecond <em>before</em> a whole second. It is negative
668 * for {@link #isNaN() NaN} or {@link #isInfinite() infinite} times.
669 * </p>
670 * @return normalized attoseconds part of the time
671 */
672 public long getAttoSeconds() {
673 return attoSeconds;
674 }
675
676 /** Get the time collapsed into a single double.
677 * <p>
678 * Beware that lots of accuracy is lost when combining {@link #getSeconds()} and {@link #getAttoSeconds()}
679 * into a single double.
680 * </p>
681 * @return time as a single double
682 */
683 public double toDouble() {
684 if (isFinite()) {
685 // regular value
686 long closeSeconds = seconds;
687 long signedAttoSeconds = attoSeconds;
688 if (attoSeconds > ATTOS_IN_HALF_SECOND) {
689 // we are closer to next second than to previous one
690 // take this into account in the computation
691 // in order to avoid losing precision
692 closeSeconds++;
693 signedAttoSeconds -= ATTOS_IN_SECOND;
694 }
695 return closeSeconds + ((double) signedAttoSeconds) / ATTOS_IN_SECOND;
696 } else {
697 // special values
698 return isNaN() ? Double.NaN : FastMath.copySign(Double.POSITIVE_INFINITY, seconds);
699 }
700 }
701
702 /** Parse a string to produce an accurate split time.
703 * <p>
704 * This method is more accurate than parsing the string as a double and then
705 * calling {@link TimeOffset#TimeOffset(double)} because it reads the before
706 * separator and after separator parts in decimal, hence avoiding problems like
707 * for example 0.1 not being an exact IEEE754 number.
708 * </p>
709 * @param s string to parse
710 * @return parsed split time
711 */
712 public static TimeOffset parse(final String s) {
713
714 // decompose the string
715 // we use neither Long.parseLong nor Integer.parseInt because we want to avoid
716 // performing several loops over the characters as we need to keep track of
717 // delimiters decimal point and exponent marker positions
718 final int length = s.length();
719 long significandSign = 1L;
720 int exponentSign = 1;
721 int separatorIndex = length;
722 int exponentIndex = length;
723 long beforeSeparator = 0L;
724 long afterSeparator = 0L;
725 int exponent = 0;
726 int digitsBefore = 0;
727 int digitsAfter = 0;
728 int digitsExponent = 0;
729 int index = 0;
730 while (index < length) {
731
732 // current character
733 final char c = s.charAt(index);
734
735 if (Character.isDigit(c)) {
736 if (separatorIndex == length) {
737 // we are parsing the part before separator
738 ++digitsBefore;
739 beforeSeparator = beforeSeparator * 10 + c - '0';
740 if (digitsBefore > 19 || beforeSeparator < 0) {
741 // overflow occurred
742 break;
743 }
744 } else if (exponentIndex == length) {
745 // we are parsing the part between separator and exponent
746 if (digitsAfter < DIGITS_ATTOS) {
747 // we never overflow here, we just ignore extra digits
748 afterSeparator = afterSeparator * 10 + c - '0';
749 ++digitsAfter;
750 }
751 } else {
752 // we are parsing the exponent
753 ++digitsExponent;
754 exponent = exponent * 10 + c - '0';
755 if (digitsExponent > 10 || exponent < 0) {
756 // overflow occurred
757 break;
758 }
759 }
760 } else if (c == '.' && separatorIndex == length) {
761 separatorIndex = index;
762 } else if ((c == 'e' || c == 'E') && exponentIndex == length) {
763 if (separatorIndex == length) {
764 separatorIndex = index;
765 }
766 exponentIndex = index;
767 } else if (c == '-') {
768 if (index == 0) {
769 significandSign = -1L;
770 } else if (index == exponentIndex + 1) {
771 exponentSign = -1;
772 } else {
773 break;
774 }
775 } else if (c == '+') {
776 if (index == 0) {
777 significandSign = 1L;
778 } else if (index == exponentIndex + 1) {
779 exponentSign = 1;
780 } else {
781 break;
782 }
783 } else {
784 break;
785 }
786
787 ++index;
788
789 }
790
791 if (length == 0 || index < length) {
792 // decomposition failed, either it is a special case or an unparsable string
793 if (s.equals("-∞")) {
794 return TimeOffset.NEGATIVE_INFINITY;
795 } else if (s.equals("+∞")) {
796 return TimeOffset.POSITIVE_INFINITY;
797 } else if (s.equalsIgnoreCase("NaN")) {
798 return TimeOffset.NaN;
799 } else {
800 throw new OrekitException(OrekitMessages.CANNOT_PARSE_DATA, s);
801 }
802 }
803
804 // decomposition was successful, build the split time
805 long seconds;
806 long attoseconds;
807 if (exponentSign < 0) {
808 // the part before separator must be split into seconds and attoseconds
809 if (exponent >= MULTIPLIERS.length) {
810 seconds = 0L;
811 if (exponent - DIGITS_ATTOS >= MULTIPLIERS.length) {
812 // underflow
813 attoseconds = 0L;
814 } else {
815 attoseconds = beforeSeparator / MULTIPLIERS[exponent - DIGITS_ATTOS];
816 }
817 } else {
818 final long secondsMultiplier = MULTIPLIERS[exponent];
819 final long attoBeforeMultiplier = MULTIPLIERS[DIGITS_ATTOS - exponent];
820 seconds = beforeSeparator / secondsMultiplier;
821 attoseconds = (beforeSeparator - seconds * secondsMultiplier) * attoBeforeMultiplier;
822 while (digitsAfter + exponent > DIGITS_ATTOS) {
823 // drop least significant digits below one attosecond
824 afterSeparator /= 10;
825 digitsAfter--;
826 }
827 final long attoAfterMultiplier = MULTIPLIERS[DIGITS_ATTOS - exponent - digitsAfter];
828 attoseconds += afterSeparator * attoAfterMultiplier;
829 }
830 } else {
831 // the part after separator must be split into seconds and attoseconds
832 if (exponent >= MULTIPLIERS.length) {
833 if (beforeSeparator == 0L && afterSeparator == 0L) {
834 return TimeOffset.ZERO;
835 } else if (significandSign < 0) {
836 return TimeOffset.NEGATIVE_INFINITY;
837 } else {
838 return TimeOffset.POSITIVE_INFINITY;
839 }
840 } else {
841 final long secondsMultiplier = MULTIPLIERS[exponent];
842 seconds = beforeSeparator * secondsMultiplier;
843 if (exponent > digitsAfter) {
844 seconds += afterSeparator * MULTIPLIERS[exponent - digitsAfter];
845 attoseconds = 0L;
846 } else {
847 final long q = afterSeparator / MULTIPLIERS[digitsAfter - exponent];
848 seconds += q;
849 attoseconds = (afterSeparator - q * MULTIPLIERS[digitsAfter - exponent]) *
850 MULTIPLIERS[DIGITS_ATTOS - digitsAfter + exponent];
851 }
852 }
853 }
854
855 return new TimeOffset(significandSign * seconds, significandSign * attoseconds);
856
857 }
858
859 /** Compare the instance with another one.
860 * <p>
861 * Not that in order to be consistent with {@code Double#compareTo(Double)},
862 * NaN is considered equal to itself and greater than positive infinity.
863 * </p>
864 * @param other other time to compare the instance to
865 * @return a negative integer, zero, or a positive integer if applying this time
866 * to reference date would result in a date being before, simultaneous, or after
867 * the date obtained by applying the other time to the same reference date.
868 */
869 public int compareTo(final TimeOffset other) {
870 if (isFinite()) {
871 if (other.isFinite()) {
872 return seconds == other.seconds ?
873 Long.compare(attoSeconds, other.attoSeconds) :
874 Long.compare(seconds, other.seconds);
875 } else {
876 // if other is ±∞ or NaN, and NaN is considered larger than +∞
877 return other.isNegativeInfinity() ? 1 : -1;
878 }
879 } else {
880 // instance is ±∞ or NaN, and NaN is considered larger than +∞
881 if (isNaN()) {
882 // for consistency with Double.compareTo, NaN is considered equal to itself
883 return other.isNaN() ? 0 : 1;
884 } else if (other.isNaN()) {
885 return -1;
886 } else {
887 // instance is ±∞, other is either finite or ±∞ but not NaN
888 // at infinity, seconds are set to either Long.MIN_VALUE or Long.MAX_VALUE
889 return Long.compare(seconds, other.seconds);
890 }
891 }
892 }
893
894 /** {@inheritDoc} */
895 @Override
896 public boolean equals(final Object o) {
897 if (this == o) {
898 return true;
899 }
900 if (o == null || o.getClass() != this.getClass()) {
901 return false;
902 }
903 final TimeOffset timeOffset = (TimeOffset) o;
904 return seconds == timeOffset.seconds && attoSeconds == timeOffset.attoSeconds;
905 }
906
907 /** {@inheritDoc} */
908 @Override
909 public int hashCode() {
910 return Long.hashCode(seconds) ^ Long.hashCode(attoSeconds);
911 }
912
913 /** Local class for summing several instances. */
914 private static class RunningSum {
915
916 /** Number of terms that can be added before normalization is needed. */
917 private static final int COUNT_DOWN_MAX = 9;
918
919 /** Seconds part. */
920 private long seconds;
921
922 /** AttoSeconds part. */
923 private long attoSeconds;
924
925 /** Indicator for NaN presence. */
926 private boolean addedNaN;
927
928 /** Indicator for +∞ presence. */
929 private boolean addedPositiveInfinity;
930
931 /** Indicator for -∞ presence. */
932 private boolean addedNegativeInfinity;
933
934 /** Countdown for checking carry. */
935 private int countDown;
936
937 /** Simple constructor.
938 */
939 RunningSum() {
940 countDown = COUNT_DOWN_MAX;
941 }
942
943 /** Add one term.
944 * @param term term to add
945 */
946 public void add(final TimeOffset term) {
947 if (term.isFinite()) {
948 // regular addition
949 seconds += term.seconds;
950 attoSeconds += term.attoSeconds;
951 if (--countDown == 0) {
952 // we have added several terms, we should normalize
953 // the fields before attoseconds overflow (it may overflow after 9 additions)
954 normalize();
955 }
956 } else if (term.isNegativeInfinity()) {
957 addedNegativeInfinity = true;
958 } else if (term.isPositiveInfinity()) {
959 addedPositiveInfinity = true;
960 } else {
961 addedNaN = true;
962 }
963 }
964
965 /** Normalize current running sum.
966 * @return normalized value
967 */
968 public TimeOffset normalize() {
969
970 // after normalization, we will have the equivalent of one entry processed
971 countDown = COUNT_DOWN_MAX - 1;
972
973 if (addedNaN || addedNegativeInfinity && addedPositiveInfinity) {
974 // we have built a NaN
975 seconds = NaN.seconds;
976 attoSeconds = NaN.attoSeconds;
977 return NaN;
978 } else if (addedNegativeInfinity) {
979 // we have built -∞
980 seconds = NEGATIVE_INFINITY.seconds;
981 attoSeconds = NEGATIVE_INFINITY.attoSeconds;
982 return NEGATIVE_INFINITY;
983 } else if (addedPositiveInfinity) {
984 // we have built +∞
985 seconds = POSITIVE_INFINITY.seconds;
986 attoSeconds = POSITIVE_INFINITY.attoSeconds;
987 return POSITIVE_INFINITY;
988 } else {
989 // this is a regular time
990 final TimeOffset regular = new TimeOffset(seconds, attoSeconds);
991 seconds = regular.seconds;
992 attoSeconds = regular.attoSeconds;
993 return regular;
994 }
995 }
996
997 }
998
999 }