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