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