1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 public class TimeOffset
58 implements Comparable<TimeOffset>, Serializable {
59
60
61 public static final TimeOffset ZERO = new TimeOffset(0L, 0L);
62
63
64 public static final TimeOffset ATTOSECOND = new TimeOffset(0L, 1L);
65
66
67 public static final TimeOffset FEMTOSECOND = new TimeOffset(0L, 1000L);
68
69
70 public static final TimeOffset PICOSECOND = new TimeOffset(0L, 1000000L);
71
72
73 public static final TimeOffset NANOSECOND = new TimeOffset(0L, 1000000000L);
74
75
76 public static final TimeOffset MICROSECOND = new TimeOffset(0L, 1000000000000L);
77
78
79 public static final TimeOffset MILLISECOND = new TimeOffset(0L, 1000000000000000L);
80
81
82 public static final TimeOffset SECOND = new TimeOffset(1L, 0L);
83
84
85 public static final TimeOffset MINUTE = new TimeOffset(60L, 0L);
86
87
88 public static final TimeOffset HOUR = new TimeOffset(3600L, 0L);
89
90
91 public static final TimeOffset DAY = new TimeOffset(86400L, 0L);
92
93
94 public static final TimeOffset DAY_WITH_POSITIVE_LEAP = new TimeOffset(86401L, 0L);
95
96
97
98 public static final TimeOffset NaN = new TimeOffset(Double.NaN);
99
100
101
102 public static final TimeOffset NEGATIVE_INFINITY = new TimeOffset(Double.NEGATIVE_INFINITY);
103
104
105 public static final TimeOffset POSITIVE_INFINITY = new TimeOffset(Double.POSITIVE_INFINITY);
106
107
108 private static final long NAN_INDICATOR = -0XFFL;
109
110
111 private static final long POSITIVE_INFINITY_INDICATOR = -0XFF00L;
112
113
114 private static final long NEGATIVE_INFINITY_INDICATOR = -0XFF0000L;
115
116
117 private static final long MILLIS_IN_SECOND = 1000L;
118
119
120 private static final long MICROS_IN_SECOND = 1000000L;
121
122
123 private static final long NANOS_IN_SECOND = 1000000000L;
124
125
126 private static final long ATTOS_IN_SECOND = 1000000000000000000L;
127
128
129 private static final long ATTOS_IN_HALF_SECOND = 500000000000000000L;
130
131
132
133
134
135
136 private static final long SPLIT = 1000000000L;
137
138
139 private static final int DIGITS_ATTOS = 18;
140
141
142
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
165
166
167
168
169 private static final FastLongFormatter SECONDS_FORMATTER = new FastLongFormatter(1, false);
170
171
172
173
174 private static final FastLongFormatter ATTOSECONDS_FORMATTER = new FastLongFormatter(18, true);
175
176
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
186 private static final long serialVersionUID = 20240711L;
187
188
189 private final long seconds;
190
191
192 private final long attoSeconds;
193
194
195
196
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
210
211
212
213
214
215
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
231
232
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
261
262
263
264
265
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
273
274
275
276
277
278
279
280
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
289
290
291
292
293
294
295
296
297
298
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
308
309
310
311
312
313
314
315
316
317
318
319
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
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
362
363
364
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
456
457
458 private TimeOffset(final TimeOffset time) {
459 seconds = time.seconds;
460 attoSeconds = time.attoSeconds;
461 }
462
463
464
465
466 public boolean isZero() {
467 return seconds == 0L && attoSeconds == 0L;
468 }
469
470
471
472
473
474
475
476
477 public boolean isFinite() {
478 return attoSeconds >= 0L;
479 }
480
481
482
483
484
485
486
487
488 public boolean isNaN() {
489 return attoSeconds == NAN_INDICATOR;
490 }
491
492
493
494
495
496
497
498
499 public boolean isInfinite() {
500 return isPositiveInfinity() || isNegativeInfinity();
501 }
502
503
504
505
506
507
508
509
510 public boolean isPositiveInfinity() {
511 return attoSeconds == POSITIVE_INFINITY_INDICATOR;
512 }
513
514
515
516
517
518
519
520
521 public boolean isNegativeInfinity() {
522 return attoSeconds == NEGATIVE_INFINITY_INDICATOR;
523 }
524
525
526
527
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
537
538
539
540 public TimeOffset subtract(final TimeOffset t) {
541 if (attoSeconds < 0 || t.attoSeconds < 0) {
542
543 if (isNaN() ||
544 t.isNaN() ||
545 isPositiveInfinity() && t.isPositiveInfinity() ||
546 isNegativeInfinity() && t.isNegativeInfinity()) {
547 return NaN;
548 } else if (isInfinite()) {
549
550 return this;
551 } else {
552
553 return t.isPositiveInfinity() ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
554 }
555 } else {
556
557 return new TimeOffset(seconds - t.seconds, attoSeconds - t.attoSeconds);
558 }
559 }
560
561
562
563
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
586 if (pHigh * sHigh != 0 || ps1 / SPLIT != 0) {
587 throw new OrekitException(LocalizedCoreFormats.OVERFLOW_IN_MULTIPLICATION, abs.seconds, p);
588 }
589
590
591 final TimeOffset mul = new TimeOffset(SPLIT * ps1 + ps0 + pa2 + pa1High, SPLIT * pa1Low + pa0);
592 return seconds < 0 ? mul.negate() : mul;
593 } else {
594
595 return p == 0 ? TimeOffset.NaN : this;
596 }
597 }
598
599
600
601
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
618
619 new TimeOffset(0L, (attoSeconds + rSec * rK) / q));
620 } else {
621
622 return this;
623 }
624 }
625
626
627
628
629 public TimeOffset negate() {
630
631 if (attoSeconds < 0) {
632
633 return isNaN() ? this : (seconds < 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY);
634 } else {
635
636 return new TimeOffset(-seconds, -attoSeconds);
637 }
638 }
639
640
641
642
643
644
645 public long getRoundedTime(final TimeUnit unit) {
646
647
648 if (attoSeconds < 0) {
649
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
678
679
680
681
682
683
684
685
686
687
688
689 public TimeOffset getRoundedOffset(final int fractionDigits) {
690
691
692 if (attoSeconds < 0) {
693
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
703
704
705 public long getSeconds() {
706 return seconds;
707 }
708
709
710
711
712
713
714
715
716
717
718
719 public long getAttoSeconds() {
720 return attoSeconds;
721 }
722
723
724
725
726
727
728
729
730 public double toDouble() {
731 if (isFinite()) {
732
733 long closeSeconds = seconds;
734 long signedAttoSeconds = attoSeconds;
735 if (attoSeconds > ATTOS_IN_HALF_SECOND) {
736
737
738
739 closeSeconds++;
740 signedAttoSeconds -= ATTOS_IN_SECOND;
741 }
742 return closeSeconds + ((double) signedAttoSeconds) / ATTOS_IN_SECOND;
743 } else {
744
745 return isNaN() ? Double.NaN : FastMath.copySign(Double.POSITIVE_INFINITY, seconds);
746 }
747 }
748
749
750
751
752
753
754
755
756
757
758
759 public static TimeOffset parse(final String s) {
760
761
762
763
764
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
780 final char c = s.charAt(index);
781
782 if (Character.isDigit(c)) {
783 if (separatorIndex == length) {
784
785 ++digitsBefore;
786 beforeSeparator = beforeSeparator * 10 + c - '0';
787 if (digitsBefore > 19 || beforeSeparator < 0) {
788
789 break;
790 }
791 } else if (exponentIndex == length) {
792
793 if (digitsAfter < DIGITS_ATTOS) {
794
795 afterSeparator = afterSeparator * 10 + c - '0';
796 ++digitsAfter;
797 }
798 } else {
799
800 ++digitsExponent;
801 exponent = exponent * 10 + c - '0';
802 if (digitsExponent > 10 || exponent < 0) {
803
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
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
852 long seconds;
853 long attoseconds;
854 if (exponentSign < 0) {
855
856 if (exponent >= SCALING.length) {
857 seconds = 0L;
858 if (exponent - DIGITS_ATTOS >= SCALING.length) {
859
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
871 afterSeparator /= 10;
872 digitsAfter--;
873 }
874 final long attoAfterMultiplier = SCALING[DIGITS_ATTOS - exponent - digitsAfter];
875 attoseconds += afterSeparator * attoAfterMultiplier;
876 }
877 } else {
878
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
907
908
909
910
911
912
913
914
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
924 return other.isNegativeInfinity() ? 1 : -1;
925 }
926 } else {
927
928 if (isNaN()) {
929
930 return other.isNaN() ? 0 : 1;
931 } else if (other.isNaN()) {
932 return -1;
933 } else {
934
935
936 return Long.compare(seconds, other.seconds);
937 }
938 }
939 }
940
941
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
955 @Override
956 public int hashCode() {
957 return Long.hashCode(seconds) ^ Long.hashCode(attoSeconds);
958 }
959
960
961 @Override
962 public String toString() {
963 try {
964 if (attoSeconds < 0) {
965
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
989 throw new OrekitInternalError(ioe);
990 }
991 }
992
993
994 private static class RunningSum {
995
996
997 private static final int COUNT_DOWN_MAX = 9;
998
999
1000 private long seconds;
1001
1002
1003 private long attoSeconds;
1004
1005
1006 private boolean addedNaN;
1007
1008
1009 private boolean addedPositiveInfinity;
1010
1011
1012 private boolean addedNegativeInfinity;
1013
1014
1015 private int countDown;
1016
1017
1018
1019 RunningSum() {
1020 countDown = COUNT_DOWN_MAX;
1021 }
1022
1023
1024
1025
1026 public void add(final TimeOffset term) {
1027 if (term.isFinite()) {
1028
1029 seconds += term.seconds;
1030 attoSeconds += term.attoSeconds;
1031 if (--countDown == 0) {
1032
1033
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
1046
1047
1048 public TimeOffset normalize() {
1049
1050
1051 countDown = COUNT_DOWN_MAX - 1;
1052
1053 if (addedNaN || addedNegativeInfinity && addedPositiveInfinity) {
1054
1055 seconds = NaN.seconds;
1056 attoSeconds = NaN.attoSeconds;
1057 return NaN;
1058 } else if (addedNegativeInfinity) {
1059
1060 seconds = NEGATIVE_INFINITY.seconds;
1061 attoSeconds = NEGATIVE_INFINITY.attoSeconds;
1062 return NEGATIVE_INFINITY;
1063 } else if (addedPositiveInfinity) {
1064
1065 seconds = POSITIVE_INFINITY.seconds;
1066 attoSeconds = POSITIVE_INFINITY.attoSeconds;
1067 return POSITIVE_INFINITY;
1068 } else {
1069
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 }