TimeOffset.java

  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. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.hipparchus.util.FastMath;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitInternalError;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.utils.formatting.FastLongFormatter;

  24. import java.io.IOException;
  25. import java.io.Serializable;
  26. import java.util.concurrent.TimeUnit;

  27. /** This class represents a time range split into seconds and attoseconds.
  28.  * <p>
  29.  * Instances of this class may either be interpreted as offsets from a reference
  30.  * date, or they may be interpreted as durations. Negative values represent
  31.  * dates earlier than the reference date in the first interpretation, and
  32.  * negative durations in the second interpretation.
  33.  * </p>
  34.  * <p>
  35.  * The whole number of seconds is stored as signed primitive long, so the range
  36.  * of dates that can be represented is ±292 billion years. The fractional part
  37.  * within the second is stored as non-negative primitive long with fixed precision
  38.  * at a resolution of one attosecond (10⁻¹⁸s). The choice of attoseconds allows
  39.  * to represent exactly all important offsets (between TT and TAI, or between UTC
  40.  * and TAI during the linear eras), as well as all times converted from standard
  41.  * Java Instant, Date or TimeUnit classes. It also allows simple computation as
  42.  * adding or subtracting a few values in attoseconds that are less than one second
  43.  * does not overflow (a primitive long could hold any values between ±9.22s in
  44.  * attoseconds so simple additions and subtractions followed by handling a carry
  45.  * to bring the value back between 0 and 10¹⁸ is straightforward). There are also
  46.  * special encodings (internally using negative longs in the fractional part) to
  47.  * represent {@link #NaN}, {@link #POSITIVE_INFINITY} and {@link #NEGATIVE_INFINITY}.
  48.  * </p>
  49.  * @author Luc Maisonobe
  50.  * @see AbsoluteDate
  51.  * @see FieldAbsoluteDate
  52.  * @since 13.0
  53.  */
  54. public class TimeOffset
  55.     implements Comparable<TimeOffset>, Serializable {

  56.     /** Split time representing 0. */
  57.     public static final TimeOffset ZERO = new TimeOffset(0L, 0L);

  58.     /** Split time representing 1 attosecond. */
  59.     public static final TimeOffset ATTOSECOND = new TimeOffset(0L, 1L);

  60.     /** Split time representing 1 femtosecond. */
  61.     public static final TimeOffset FEMTOSECOND = new TimeOffset(0L, 1000L);

  62.     /** Split time representing 1 picosecond. */
  63.     public static final TimeOffset PICOSECOND = new TimeOffset(0L, 1000000L);

  64.     /** Split time representing 1 nanosecond. */
  65.     public static final TimeOffset NANOSECOND = new TimeOffset(0L, 1000000000L);

  66.     /** Split time representing 1 microsecond. */
  67.     public static final TimeOffset MICROSECOND = new TimeOffset(0L, 1000000000000L);

  68.     /** Split time representing 1 millisecond. */
  69.     public static final TimeOffset MILLISECOND = new TimeOffset(0L, 1000000000000000L);

  70.     /** Split time representing 1 second. */
  71.     public static final TimeOffset SECOND = new TimeOffset(1L, 0L);

  72.     /** Split time representing 1 minute. */
  73.     public static final TimeOffset MINUTE = new TimeOffset(60L, 0L);

  74.     /** Split time representing 1 hour. */
  75.     public static final TimeOffset HOUR = new TimeOffset(3600L, 0L);

  76.     /** Split time representing 1 day. */
  77.     public static final TimeOffset DAY = new TimeOffset(86400L, 0L);

  78.     /** Split time representing 1 day that includes an additional leap second. */
  79.     public static final TimeOffset DAY_WITH_POSITIVE_LEAP = new TimeOffset(86401L, 0L);

  80.     // CHECKSTYLE: stop ConstantName
  81.     /** Split time representing a NaN. */
  82.     public static final TimeOffset NaN = new TimeOffset(Double.NaN);
  83.     // CHECKSTYLE: resume ConstantName

  84.     /** Split time representing negative infinity. */
  85.     public static final TimeOffset NEGATIVE_INFINITY = new TimeOffset(Double.NEGATIVE_INFINITY);

  86.     /** Split time representing positive infinity. */
  87.     public static final TimeOffset POSITIVE_INFINITY = new TimeOffset(Double.POSITIVE_INFINITY);

  88.     /** Indicator for NaN time (bits pattern arbitrarily selected to avoid hashcode collisions). */
  89.     private static final long NAN_INDICATOR      = -0XFFL;

  90.     /** Indicator for positive infinite time (bits pattern arbitrarily selected to avoid hashcode collisions). */
  91.     private static final long POSITIVE_INFINITY_INDICATOR = -0XFF00L;

  92.     /** Indicator for negative infinite time (bits pattern arbitrarily selected to avoid hashcode collisions). */
  93.     private static final long NEGATIVE_INFINITY_INDICATOR = -0XFF0000L;

  94.     /** Milliseconds in one second. */
  95.     private static final long MILLIS_IN_SECOND = 1000L;

  96.     /** Microseconds in one second. */
  97.     private static final long MICROS_IN_SECOND = 1000000L;

  98.     /** Nanoseconds in one second. */
  99.     private static final long NANOS_IN_SECOND = 1000000000L;

  100.     /** Attoseconds in one second. */
  101.     private static final long ATTOS_IN_SECOND = 1000000000000000000L;

  102.     /** Attoseconds in one half-second. */
  103.     private static final long ATTOS_IN_HALF_SECOND = 500000000000000000L;

  104.     /** Factor to split long for multiplications.
  105.      * <p>
  106.      * It is important that SPLIT * SPLIT = ATTOS_IN_SECOND.
  107.      * </p>
  108.      */
  109.     private static final long SPLIT = 1000000000L;

  110.     /** Number of digits after separator for attoseconds. */
  111.     private static final int DIGITS_ATTOS = 18;

  112.     /** Scaling factors used for parsing partial strings and for rounding. */
  113.     // CHECKSTYLE: stop Indentation check
  114.     private static final long[] SCALING = new long[] {
  115.                          1L,
  116.                         10L,
  117.                        100L,
  118.                       1000L,
  119.                      10000L,
  120.                     100000L,
  121.                    1000000L,
  122.                   10000000L,
  123.                  100000000L,
  124.                 1000000000L,
  125.                10000000000L,
  126.               100000000000L,
  127.              1000000000000L,
  128.             10000000000000L,
  129.            100000000000000L,
  130.           1000000000000000L,
  131.          10000000000000000L,
  132.         100000000000000000L,
  133.        1000000000000000000L
  134.     };
  135.     // CHECKSTYLE: resume Indentation check

  136.     /** Formatter for seconds.
  137.      * @since 13.0.3
  138.      */
  139.     private static final FastLongFormatter SECONDS_FORMATTER = new FastLongFormatter(1, false);

  140.     /** Formatter for attoseconds.
  141.      * @since 13.0.3
  142.      */
  143.     private static final FastLongFormatter ATTOSECONDS_FORMATTER = new FastLongFormatter(18, true);

  144.     /** NaN. */
  145.     private static final String NAN_STRING = "NaN";

  146.     /** +∞. */
  147.     private static final String POSITIVE_INFINITY_STRING = "+∞";

  148.     /** -∞. */
  149.     private static final String NEGATIVE_INTINITY_STRING = "-∞";

  150.     /** Serializable UID. */
  151.     private static final long serialVersionUID = 20240711L;

  152.     /** Seconds part. */
  153.     private final long seconds;

  154.     /** AttoSeconds part. */
  155.     private final long attoSeconds;

  156.     /**
  157.      * Build a time by adding several times.
  158.      * @param times times to add
  159.      */
  160.     public TimeOffset(final TimeOffset... times) {
  161.         final RunningSum runningSum = new RunningSum();
  162.         for (final TimeOffset time : times) {
  163.             runningSum.add(time);
  164.         }
  165.         final TimeOffset sum = runningSum.normalize();
  166.         this.seconds     = sum.getSeconds();
  167.         this.attoSeconds = sum.getAttoSeconds();
  168.     }

  169.     /**
  170.      * Build a time from its components.
  171.      * <p>
  172.      * The components will be normalized so that {@link #getAttoSeconds()}
  173.      * returns a value between {@code 0L} and {1000000000000000000L}
  174.      * </p>
  175.      * @param seconds seconds part
  176.      * @param attoSeconds attoseconds part
  177.      */
  178.     public TimeOffset(final long seconds, final long attoSeconds) {
  179.         final long qAtto = attoSeconds / ATTOS_IN_SECOND;
  180.         final long rAtto = attoSeconds - qAtto * ATTOS_IN_SECOND;
  181.         if (rAtto < 0L) {
  182.             this.seconds     = seconds + qAtto - 1L;
  183.             this.attoSeconds = ATTOS_IN_SECOND + rAtto;
  184.         } else {
  185.             this.seconds     = seconds + qAtto;
  186.             this.attoSeconds = rAtto;
  187.         }
  188.     }

  189.     /**
  190.      * Build a time from a value in seconds.
  191.      *
  192.      * @param time time
  193.      */
  194.     public TimeOffset(final double time) {
  195.         if (Double.isNaN(time)) {
  196.             seconds     = 0L;
  197.             attoSeconds = NAN_INDICATOR;
  198.         } else if (time < Long.MIN_VALUE || time > Long.MAX_VALUE) {
  199.             if (time < 0L) {
  200.                 seconds     = Long.MIN_VALUE;
  201.                 attoSeconds = NEGATIVE_INFINITY_INDICATOR;
  202.             } else {
  203.                 seconds     = Long.MAX_VALUE;
  204.                 attoSeconds = POSITIVE_INFINITY_INDICATOR;
  205.             }
  206.         } else {
  207.             final double tiSeconds  = FastMath.rint(time);
  208.             final double subSeconds = time - tiSeconds;
  209.             if (subSeconds < 0L) {
  210.                 seconds     = (long) tiSeconds - 1L;
  211.                 attoSeconds = FastMath.round(subSeconds * ATTOS_IN_SECOND) + ATTOS_IN_SECOND;
  212.             } else {
  213.                 seconds     = (long) tiSeconds;
  214.                 attoSeconds = FastMath.round(subSeconds * ATTOS_IN_SECOND);
  215.             }
  216.         }
  217.     }

  218.     /**
  219.      * Multiplicative constructor.
  220.      * <p>
  221.      * This constructor builds a split time corresponding to {@code factor} ⨉ {@code time}
  222.      * </p>
  223.      * @param factor multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  224.      * @param time base time
  225.      */
  226.     public TimeOffset(final long factor, final TimeOffset time) {
  227.         this(factor < 0 ? time.multiply(-factor).negate() : time.multiply(factor));
  228.     }

  229.     /**
  230.      * Linear combination constructor.
  231.      * <p>
  232.      * This constructor builds a split time corresponding to
  233.      * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2}
  234.      * </p>
  235.      * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  236.      * @param t1 first base time
  237.      * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  238.      * @param t2 second base time
  239.      */
  240.     public TimeOffset(final long f1, final TimeOffset t1,
  241.                       final long f2, final TimeOffset t2) {
  242.         this(new TimeOffset(f1, t1).add(new TimeOffset(f2, t2)));
  243.     }

  244.     /**
  245.      * Linear combination constructor.
  246.      * <p>
  247.      * This constructor builds a split time corresponding to
  248.      * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3}
  249.      * </p>
  250.      * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  251.      * @param t1 first base time
  252.      * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  253.      * @param t2 second base time
  254.      * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  255.      * @param t3 third base time
  256.      */
  257.     public TimeOffset(final long f1, final TimeOffset t1,
  258.                       final long f2, final TimeOffset t2,
  259.                       final long f3, final TimeOffset t3) {
  260.         this(new TimeOffset(f1, t1).add(new TimeOffset(f2, t2)).add(new TimeOffset(f3, t3)));
  261.     }

  262.     /**
  263.      * Linear combination constructor.
  264.      * <p>
  265.      * This constructor builds a split time corresponding to
  266.      * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3} + {@code f4} ⨉ {@code t4}
  267.      * </p>
  268.      * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  269.      * @param t1 first base time
  270.      * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  271.      * @param t2 second base time
  272.      * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  273.      * @param t3 third base time
  274.      * @param f4 fourth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  275.      * @param t4 fourth base time
  276.      */
  277.     public TimeOffset(final long f1, final TimeOffset t1,
  278.                       final long f2, final TimeOffset t2,
  279.                       final long f3, final TimeOffset t3,
  280.                       final long f4, final TimeOffset t4) {
  281.         this(new TimeOffset(f1, t1).
  282.              add(new TimeOffset(f2, t2)).
  283.              add(new TimeOffset(f3, t3)).
  284.              add(new TimeOffset(f4, t4)));
  285.     }

  286.     /**
  287.      * Linear combination constructor.
  288.      * <p>
  289.      * This constructor builds a split time corresponding to
  290.      * {@code f1} ⨉ {@code t1} + {@code f2} ⨉ {@code t2} + {@code f3} ⨉ {@code t3} + {@code f4} ⨉ {@code t4} + {@code f5} ⨉ {@code t5}
  291.      * </p>
  292.      * @param f1 first multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  293.      * @param t1 first base time
  294.      * @param f2 second multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  295.      * @param t2 second base time
  296.      * @param f3 third multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  297.      * @param t3 third base time
  298.      * @param f4 fourth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  299.      * @param t4 fourth base time
  300.      * @param f5 fifth multiplicative factor (negative values allowed here, contrary to {@link #multiply(long)})
  301.      * @param t5 fifth base time
  302.      */
  303.     public TimeOffset(final long f1, final TimeOffset t1,
  304.                       final long f2, final TimeOffset t2,
  305.                       final long f3, final TimeOffset t3,
  306.                       final long f4, final TimeOffset t4,
  307.                       final long f5, final TimeOffset t5) {
  308.         this(new TimeOffset(f1, t1).
  309.              add(new TimeOffset(f2, t2)).
  310.              add(new TimeOffset(f3, t3)).
  311.              add(new TimeOffset(f4, t4)).
  312.              add(new TimeOffset(f5, t5)));
  313.     }

  314.     /**
  315.      * Build a time from a value defined in some time unit.
  316.      *
  317.      * @param time time
  318.      * @param unit   time unit in which {@code time} is expressed
  319.      */
  320.     public TimeOffset(final long time, final TimeUnit unit) {
  321.         switch (unit) {
  322.             case DAYS: {
  323.                 final long limit = (Long.MAX_VALUE - DAY.seconds / 2) / DAY.seconds;
  324.                 if (time < -limit) {
  325.                     seconds     = Long.MIN_VALUE;
  326.                     attoSeconds = NEGATIVE_INFINITY_INDICATOR;
  327.                 } else if (time > limit) {
  328.                     seconds     = Long.MAX_VALUE;
  329.                     attoSeconds = POSITIVE_INFINITY_INDICATOR;
  330.                 } else {
  331.                     seconds = time * DAY.seconds;
  332.                     attoSeconds = 0L;
  333.                 }
  334.                 break;
  335.             }
  336.             case HOURS: {
  337.                 final long limit = (Long.MAX_VALUE - HOUR.seconds / 2) / HOUR.seconds;
  338.                 if (time < -limit) {
  339.                     seconds     = Long.MIN_VALUE;
  340.                     attoSeconds = NEGATIVE_INFINITY_INDICATOR;
  341.                 } else if (time > limit) {
  342.                     seconds     = Long.MAX_VALUE;
  343.                     attoSeconds = POSITIVE_INFINITY_INDICATOR;
  344.                 } else {
  345.                     seconds     = time * HOUR.seconds;
  346.                     attoSeconds = 0L;
  347.                 }
  348.                 break;
  349.             }
  350.             case MINUTES: {
  351.                 final long limit = (Long.MAX_VALUE - MINUTE.seconds / 2) / MINUTE.seconds;
  352.                 if (time < -limit) {
  353.                     seconds     = Long.MIN_VALUE;
  354.                     attoSeconds = NEGATIVE_INFINITY_INDICATOR;
  355.                 } else if (time > limit) {
  356.                     seconds     = Long.MAX_VALUE;
  357.                     attoSeconds = POSITIVE_INFINITY_INDICATOR;
  358.                 } else {
  359.                     seconds     = time * MINUTE.seconds;
  360.                     attoSeconds = 0L;
  361.                 }
  362.                 break;
  363.             }
  364.             case SECONDS:
  365.                 seconds     = time;
  366.                 attoSeconds = 0L;
  367.                 break;
  368.             case MILLISECONDS: {
  369.                 final long s = time / MILLIS_IN_SECOND;
  370.                 final long r = (time - s * MILLIS_IN_SECOND) * MILLISECOND.attoSeconds;
  371.                 if (r < 0L) {
  372.                     seconds     = s - 1L;
  373.                     attoSeconds = ATTOS_IN_SECOND + r;
  374.                 } else {
  375.                     seconds     = s;
  376.                     attoSeconds = r;
  377.                 }
  378.                 break;
  379.             }
  380.             case MICROSECONDS: {
  381.                 final long s = time / MICROS_IN_SECOND;
  382.                 final long r = (time - s * MICROS_IN_SECOND) * MICROSECOND.attoSeconds;
  383.                 if (r < 0L) {
  384.                     seconds     = s - 1L;
  385.                     attoSeconds = ATTOS_IN_SECOND + r;
  386.                 } else {
  387.                     seconds     = s;
  388.                     attoSeconds = r;
  389.                 }
  390.                 break;
  391.             }
  392.             case NANOSECONDS: {
  393.                 final long s = time / NANOS_IN_SECOND;
  394.                 final long r = (time - s * NANOS_IN_SECOND) * NANOSECOND.attoSeconds;
  395.                 if (r < 0L) {
  396.                     seconds     = s - 1L;
  397.                     attoSeconds = ATTOS_IN_SECOND + r;
  398.                 } else {
  399.                     seconds     = s;
  400.                     attoSeconds = r;
  401.                 }
  402.                 break;
  403.             }
  404.             default:
  405.                 throw new OrekitException(OrekitMessages.UNKNOWN_UNIT, unit.name());
  406.         }
  407.     }

  408.     /** Copy constructor, for internal use only.
  409.      * @param time time to copy
  410.      */
  411.     private TimeOffset(final TimeOffset time) {
  412.         seconds     = time.seconds;
  413.         attoSeconds = time.attoSeconds;
  414.     }

  415.     /** check if the time is zero.
  416.      * @return true if the time is zero
  417.      */
  418.     public boolean isZero() {
  419.         return seconds == 0L && attoSeconds == 0L;
  420.     }

  421.     /** Check if time is finite (i.e. neither {@link #isNaN() NaN} nor {@link #isInfinite() infinite)}.
  422.      * @return true if time is finite
  423.      * @see #isNaN()
  424.      * @see #isInfinite()
  425.      * @see #isNegativeInfinity()
  426.      * @see #isPositiveInfinity()
  427.      */
  428.     public boolean isFinite() {
  429.         return attoSeconds >= 0L;
  430.     }

  431.     /** Check if time is NaN.
  432.      * @return true if time is NaN
  433.      * @see #isFinite()
  434.      * @see #isInfinite()
  435.      * @see #isNegativeInfinity()
  436.      * @see #isPositiveInfinity()
  437.      */
  438.     public boolean isNaN() {
  439.         return attoSeconds == NAN_INDICATOR;
  440.     }

  441.     /** Check if time is infinity.
  442.      * @return true if time is infinity
  443.      * @see #isFinite()
  444.      * @see #isNaN()
  445.      * @see #isNegativeInfinity()
  446.      * @see #isPositiveInfinity()
  447.      */
  448.     public boolean isInfinite() {
  449.         return isPositiveInfinity() || isNegativeInfinity();
  450.     }

  451.     /** Check if time is positive infinity.
  452.      * @return true if time is positive infinity
  453.      * @see #isFinite()
  454.      * @see #isNaN()
  455.      * @see #isInfinite()
  456.      * @see #isNegativeInfinity()
  457.      */
  458.     public boolean isPositiveInfinity() {
  459.         return attoSeconds == POSITIVE_INFINITY_INDICATOR;
  460.     }

  461.     /** Check if time is negative infinity.
  462.      * @return true if time is negative infinity
  463.      * @see #isFinite()
  464.      * @see #isNaN()
  465.      * @see #isInfinite()
  466.      * @see #isPositiveInfinity()
  467.      */
  468.     public boolean isNegativeInfinity() {
  469.         return attoSeconds == NEGATIVE_INFINITY_INDICATOR;
  470.     }

  471.     /** Build a time by adding two times.
  472.      * @param t time to add
  473.      * @return this+t
  474.      */
  475.     public TimeOffset add(final TimeOffset t) {
  476.         final RunningSum runningSum = new RunningSum();
  477.         runningSum.add(this);
  478.         runningSum.add(t);
  479.         return runningSum.normalize();
  480.     }

  481.     /** Build a time by subtracting one time from the instance.
  482.      * @param t time to subtract
  483.      * @return this-t
  484.      */
  485.     public TimeOffset subtract(final TimeOffset t) {
  486.         if (attoSeconds < 0 || t.attoSeconds < 0) {
  487.             // gather all special cases in one big check to avoid rare multiple tests
  488.             if (isNaN() ||
  489.                 t.isNaN() ||
  490.                 isPositiveInfinity() && t.isPositiveInfinity() ||
  491.                 isNegativeInfinity() && t.isNegativeInfinity()) {
  492.                 return NaN;
  493.             } else if (isInfinite()) {
  494.                 // t is either a finite time or the infinity opposite to this
  495.                 return this;
  496.             } else {
  497.                 // this is either a finite time or the infinity opposite to t
  498.                 return t.isPositiveInfinity() ? NEGATIVE_INFINITY : POSITIVE_INFINITY;
  499.             }
  500.         } else {
  501.             // regular subtraction between two finite times
  502.             return new TimeOffset(seconds - t.seconds, attoSeconds - t.attoSeconds);
  503.         }
  504.     }

  505.     /** Multiply the instance by a positive or zero constant.
  506.      * @param p multiplication factor (must be positive)
  507.      * @return this ⨉ p
  508.      */
  509.     public TimeOffset multiply(final long p) {
  510.         if (p < 0) {
  511.             throw new OrekitException(OrekitMessages.NOT_POSITIVE, p);
  512.         }
  513.         if (isFinite()) {
  514.             final TimeOffset abs   = seconds < 0 ? negate() : this;
  515.             final long pHigh   = p / SPLIT;
  516.             final long pLow    = p - pHigh * SPLIT;
  517.             final long sHigh   = abs.seconds / SPLIT;
  518.             final long sLow    = abs.seconds - sHigh * SPLIT;
  519.             final long aHigh   = abs.attoSeconds / SPLIT;
  520.             final long aLow    = abs.attoSeconds - aHigh * SPLIT;
  521.             final long ps1     = pHigh * sLow + pLow * sHigh;
  522.             final long ps0     = pLow * sLow;
  523.             final long pa2     = pHigh * aHigh;
  524.             final long pa1     = pHigh * aLow + pLow * aHigh;
  525.             final long pa1High = pa1 / SPLIT;
  526.             final long pa1Low  = pa1 - pa1High * SPLIT;
  527.             final long pa0     = pLow * aLow;

  528.             // check for overflow
  529.             if (pHigh * sHigh != 0 || ps1 / SPLIT != 0) {
  530.                 throw new OrekitException(LocalizedCoreFormats.OVERFLOW_IN_MULTIPLICATION, abs.seconds, p);
  531.             }

  532.             // here we use the fact that SPLIT * SPLIT = ATTOS_IN_SECOND
  533.             final TimeOffset mul = new TimeOffset(SPLIT * ps1 + ps0 + pa2 + pa1High, SPLIT * pa1Low + pa0);
  534.             return seconds < 0 ? mul.negate() : mul;
  535.         } else {
  536.             // already NaN, +∞ or -∞, unchanged except 0 ⨉ ±∞ = NaN
  537.             return p == 0 ? TimeOffset.NaN : this;
  538.         }
  539.     }

  540.     /** Divide the instance by a positive constant.
  541.      * @param q division factor (must be strictly positive)
  542.      * @return this ÷ q
  543.      */
  544.     public TimeOffset divide(final int q) {
  545.         if (q <= 0) {
  546.             throw new OrekitException(OrekitMessages.NOT_STRICTLY_POSITIVE, q);
  547.         }
  548.         if (isFinite()) {
  549.             final long      sSec  = seconds         / q;
  550.             final long      rSec  = seconds         - sSec * q;
  551.             final long      sK    = ATTOS_IN_SECOND / q;
  552.             final long      rK    = ATTOS_IN_SECOND - sK * q;
  553.             final TimeOffset tsSec = new TimeOffset(0L, sSec);
  554.             final TimeOffset trSec = new TimeOffset(0L, rSec);
  555.             return new TimeOffset(tsSec.multiply(sK).multiply(q),
  556.                                   tsSec.multiply(rK),
  557.                                   trSec.multiply(sK),
  558.                                   // here, we use the fact q is a positive int (not a long!)
  559.                                   // hence rSec * rK < q² does not overflow
  560.                                   new TimeOffset(0L, (attoSeconds + rSec * rK) / q));
  561.         } else {
  562.             // already NaN, +∞ or -∞, unchanged as q > 0
  563.             return this;
  564.         }
  565.     }

  566.     /** Negate the instance.
  567.      * @return new instance corresponding to opposite time
  568.      */
  569.     public TimeOffset negate() {
  570.         // handle special cases
  571.         if (attoSeconds < 0) {
  572.             // gather all special cases in one big check to avoid rare multiple tests
  573.             return isNaN() ? this : (seconds < 0 ? POSITIVE_INFINITY : NEGATIVE_INFINITY);
  574.         } else {
  575.             // the negative number of attoseconds will be normalized back to positive by the constructor
  576.             return new TimeOffset(-seconds, -attoSeconds);
  577.         }
  578.     }

  579.     /** Get the time in some unit.
  580.      * @param unit time unit
  581.      * @return time in this unit, rounded to the closest long,
  582.      * returns arbitrarily {@link Long#MAX_VALUE} for {@link #isNaN() NaN times}
  583.      */
  584.     public long getRoundedTime(final TimeUnit unit) {

  585.         // handle special cases
  586.         if (attoSeconds < 0) {
  587.             // gather all special cases in one big check to avoid rare multiple tests
  588.             return (isNaN() || seconds >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE;
  589.         }

  590.         final long sign = seconds < 0L ? -1L : 1L;
  591.         switch (unit) {
  592.             case DAYS:
  593.                 return sign * ((sign * seconds + DAY.seconds / 2) / DAY.seconds);
  594.             case HOURS:
  595.                 return sign * ((sign * seconds + HOUR.seconds / 2) / HOUR.seconds);
  596.             case MINUTES:
  597.                 return sign * ((sign * seconds + MINUTE.seconds / 2) / MINUTE.seconds);
  598.             case SECONDS:
  599.                 return seconds + ((attoSeconds >= ATTOS_IN_SECOND / 2) ? 1 : 0);
  600.             case MILLISECONDS:
  601.                 return seconds * MILLIS_IN_SECOND +
  602.                        (attoSeconds + MILLISECOND.attoSeconds / 2) / MILLISECOND.attoSeconds;
  603.             case MICROSECONDS:
  604.                 return seconds * MICROS_IN_SECOND +
  605.                        (attoSeconds + MICROSECOND.attoSeconds / 2) / MICROSECOND.attoSeconds;
  606.             case NANOSECONDS:
  607.                 return seconds * NANOS_IN_SECOND +
  608.                        (attoSeconds + NANOSECOND.attoSeconds / 2) / NANOSECOND.attoSeconds;
  609.             default:
  610.                 throw new OrekitException(OrekitMessages.UNKNOWN_UNIT, unit.name());
  611.         }
  612.     }

  613.     /** Round to specified accuracy.
  614.      * <p>
  615.      * For simplicity of implementation, the tiebreaking rule applied here is to round half
  616.      * towards positive infinity. This implies that rounding to 3 fraction digits an
  617.      * offset of exactly 2.0025s implies adding 0.0005s so the rounded value becomes 2.003s, whereas
  618.      * rounding to 3 fraction digits an offset of exactly -2.0025s also implies adding 0.0005s
  619.      * so the rounded value becomes -2.002s.
  620.      * </p>
  621.      * @param fractionDigits the number of decimal digits after the decimal point in the seconds number
  622.      * @return rounded time offset
  623.      * @since 13.0.3
  624.      */
  625.     public TimeOffset getRoundedOffset(final int fractionDigits) {

  626.         // handle special cases
  627.         if (attoSeconds < 0) {
  628.             // gather all special cases in one big check to avoid rare multiple tests
  629.             return ZERO;
  630.         }

  631.         final long scaling = SCALING[FastMath.min(18, FastMath.max(0, 18 - fractionDigits))];
  632.         return new TimeOffset(seconds, ((attoSeconds + scaling / 2) / scaling) * scaling);

  633.     }

  634.     /** Get the normalized seconds part of the time.
  635.      * @return normalized seconds part of the time (may be negative)
  636.      */
  637.     public long getSeconds() {
  638.         return seconds;
  639.     }

  640.     /** Get the normalized attoseconds part of the time.
  641.      * <p>
  642.      * The normalized attoseconds is always between {@code 0L} and
  643.      * {@code 1000000000000000000L} for <em>finite</em> ranges. Note that it
  644.      * may reach {@code 1000000000000000000L} if for example the time is less
  645.      * than 1 attosecond <em>before</em> a whole second. It is negative
  646.      * for {@link #isNaN() NaN} or {@link #isInfinite() infinite} times.
  647.      * </p>
  648.      * @return normalized attoseconds part of the time
  649.      */
  650.     public long getAttoSeconds() {
  651.         return attoSeconds;
  652.     }

  653.     /** Get the time collapsed into a single double.
  654.      * <p>
  655.      * Beware that lots of accuracy is lost when combining {@link #getSeconds()} and {@link #getAttoSeconds()}
  656.      * into a single double.
  657.      * </p>
  658.      * @return time as a single double
  659.      */
  660.     public double toDouble() {
  661.         if (isFinite()) {
  662.             // regular value
  663.             long closeSeconds      = seconds;
  664.             long signedAttoSeconds = attoSeconds;
  665.             if (attoSeconds > ATTOS_IN_HALF_SECOND) {
  666.                 // we are closer to next second than to previous one
  667.                 // take this into account in the computation
  668.                 // in order to avoid losing precision
  669.                 closeSeconds++;
  670.                 signedAttoSeconds -= ATTOS_IN_SECOND;
  671.             }
  672.             return closeSeconds + ((double) signedAttoSeconds) / ATTOS_IN_SECOND;
  673.         } else {
  674.             // special values
  675.             return isNaN() ? Double.NaN : FastMath.copySign(Double.POSITIVE_INFINITY, seconds);
  676.         }
  677.     }

  678.     /** Parse a string to produce an accurate split time.
  679.      * <p>
  680.      * This method is more accurate than parsing the string as a double and then
  681.      * calling {@link TimeOffset#TimeOffset(double)} because it reads the before
  682.      * separator and after separator parts in decimal, hence avoiding problems like
  683.      * for example 0.1 not being an exact IEEE754 number.
  684.      * </p>
  685.      * @param s string to parse
  686.      * @return parsed split time
  687.      */
  688.     public static TimeOffset parse(final String s) {

  689.         // decompose the string
  690.         // we use neither Long.parseLong nor Integer.parseInt because we want to avoid
  691.         // performing several loops over the characters as we need to keep track of
  692.         // delimiters decimal point and exponent marker positions
  693.         final int length = s.length();
  694.         long significandSign = 1L;
  695.         int  exponentSign    = 1;
  696.         int  separatorIndex  = length;
  697.         int  exponentIndex   = length;
  698.         long beforeSeparator = 0L;
  699.         long afterSeparator  = 0L;
  700.         int  exponent        = 0;
  701.         int  digitsBefore    = 0;
  702.         int  digitsAfter     = 0;
  703.         int  digitsExponent  = 0;
  704.         int index = 0;
  705.         while (index < length) {

  706.             // current character
  707.             final char c = s.charAt(index);

  708.             if (Character.isDigit(c)) {
  709.                 if (separatorIndex == length) {
  710.                     // we are parsing the part before separator
  711.                     ++digitsBefore;
  712.                     beforeSeparator = beforeSeparator * 10 + c - '0';
  713.                     if (digitsBefore > 19 || beforeSeparator < 0) {
  714.                         // overflow occurred
  715.                         break;
  716.                     }
  717.                 } else if (exponentIndex == length) {
  718.                     // we are parsing the part between separator and exponent
  719.                     if (digitsAfter < DIGITS_ATTOS) {
  720.                         // we never overflow here, we just ignore extra digits
  721.                         afterSeparator = afterSeparator * 10 + c - '0';
  722.                         ++digitsAfter;
  723.                     }
  724.                 } else {
  725.                     // we are parsing the exponent
  726.                     ++digitsExponent;
  727.                     exponent = exponent * 10 + c - '0';
  728.                     if (digitsExponent > 10 || exponent < 0) {
  729.                         // overflow occurred
  730.                         break;
  731.                     }
  732.                 }
  733.             } else if (c == '.' && separatorIndex == length) {
  734.                 separatorIndex = index;
  735.             } else if ((c == 'e' || c == 'E') && exponentIndex == length) {
  736.                 if (separatorIndex == length) {
  737.                     separatorIndex = index;
  738.                 }
  739.                 exponentIndex = index;
  740.             } else if (c == '-') {
  741.                 if (index == 0) {
  742.                     significandSign = -1L;
  743.                 } else if (index == exponentIndex + 1) {
  744.                     exponentSign = -1;
  745.                 } else {
  746.                     break;
  747.                 }
  748.             } else if (c == '+') {
  749.                 if (index == 0) {
  750.                     significandSign = 1L;
  751.                 } else if (index == exponentIndex + 1) {
  752.                     exponentSign = 1;
  753.                 } else {
  754.                     break;
  755.                 }
  756.             } else {
  757.                 break;
  758.             }

  759.             ++index;

  760.         }

  761.         if (length == 0 || index < length) {
  762.             // decomposition failed, either it is a special case or an unparsable string
  763.             if (s.equals(NEGATIVE_INTINITY_STRING)) {
  764.                 return TimeOffset.NEGATIVE_INFINITY;
  765.             } else if (s.equals(POSITIVE_INFINITY_STRING)) {
  766.                 return TimeOffset.POSITIVE_INFINITY;
  767.             } else if (s.equalsIgnoreCase(NAN_STRING)) {
  768.                 return TimeOffset.NaN;
  769.             } else {
  770.                 throw new OrekitException(OrekitMessages.CANNOT_PARSE_DATA, s);
  771.             }
  772.         }

  773.         // decomposition was successful, build the split time
  774.         long seconds;
  775.         long attoseconds;
  776.         if (exponentSign < 0) {
  777.             // the part before separator must be split into seconds and attoseconds
  778.             if (exponent >= SCALING.length) {
  779.                 seconds = 0L;
  780.                 if (exponent - DIGITS_ATTOS >= SCALING.length) {
  781.                     // underflow
  782.                     attoseconds = 0L;
  783.                 } else {
  784.                     attoseconds = beforeSeparator / SCALING[exponent - DIGITS_ATTOS];
  785.                 }
  786.             } else {
  787.                 final long secondsMultiplier    = SCALING[exponent];
  788.                 final long attoBeforeMultiplier = SCALING[DIGITS_ATTOS - exponent];
  789.                 seconds     = beforeSeparator / secondsMultiplier;
  790.                 attoseconds = (beforeSeparator - seconds * secondsMultiplier) * attoBeforeMultiplier;
  791.                 while (digitsAfter + exponent > DIGITS_ATTOS) {
  792.                     // drop least significant digits below one attosecond
  793.                     afterSeparator /= 10;
  794.                     digitsAfter--;
  795.                 }
  796.                 final long attoAfterMultiplier = SCALING[DIGITS_ATTOS - exponent - digitsAfter];
  797.                 attoseconds += afterSeparator * attoAfterMultiplier;
  798.             }
  799.         } else {
  800.             // the part after separator must be split into seconds and attoseconds
  801.             if (exponent >= SCALING.length) {
  802.                 if (beforeSeparator == 0L && afterSeparator == 0L) {
  803.                     return TimeOffset.ZERO;
  804.                 } else if (significandSign < 0) {
  805.                     return TimeOffset.NEGATIVE_INFINITY;
  806.                 } else {
  807.                     return TimeOffset.POSITIVE_INFINITY;
  808.                 }
  809.             } else {
  810.                 final long secondsMultiplier = SCALING[exponent];
  811.                 seconds = beforeSeparator * secondsMultiplier;
  812.                 if (exponent > digitsAfter) {
  813.                     seconds += afterSeparator * SCALING[exponent - digitsAfter];
  814.                     attoseconds = 0L;
  815.                 } else {
  816.                     final long q = afterSeparator / SCALING[digitsAfter - exponent];
  817.                     seconds    += q;
  818.                     attoseconds = (afterSeparator - q * SCALING[digitsAfter - exponent]) *
  819.                                   SCALING[DIGITS_ATTOS - digitsAfter + exponent];
  820.                 }
  821.             }
  822.         }

  823.         return new TimeOffset(significandSign * seconds, significandSign * attoseconds);

  824.     }

  825.     /** Compare the instance with another one.
  826.      * <p>
  827.      * Not that in order to be consistent with {@code Double#compareTo(Double)},
  828.      * NaN is considered equal to itself and greater than positive infinity.
  829.      * </p>
  830.      * @param other other time to compare the instance to
  831.      * @return a negative integer, zero, or a positive integer if applying this time
  832.      * to reference date would result in a date being before, simultaneous, or after
  833.      * the date obtained by applying the other time to the same reference date.
  834.      */
  835.     public int compareTo(final TimeOffset other) {
  836.         if (isFinite()) {
  837.             if (other.isFinite()) {
  838.                 return seconds == other.seconds ?
  839.                        Long.compare(attoSeconds, other.attoSeconds) :
  840.                        Long.compare(seconds, other.seconds);
  841.             } else {
  842.                 // if other is ±∞ or NaN, and NaN is considered larger than +∞
  843.                 return other.isNegativeInfinity() ? 1 : -1;
  844.             }
  845.         } else {
  846.             // instance is ±∞ or NaN, and NaN is considered larger than +∞
  847.             if (isNaN()) {
  848.                 // for consistency with Double.compareTo, NaN is considered equal to itself
  849.                 return other.isNaN() ? 0 : 1;
  850.             } else if (other.isNaN()) {
  851.                 return -1;
  852.             } else {
  853.                 // instance is ±∞, other is either finite or ±∞ but not NaN
  854.                 // at infinity, seconds are set to either Long.MIN_VALUE or Long.MAX_VALUE
  855.                 return Long.compare(seconds, other.seconds);
  856.             }
  857.         }
  858.     }

  859.     /** {@inheritDoc} */
  860.     @Override
  861.     public boolean equals(final Object o) {
  862.         if (this == o) {
  863.             return true;
  864.         }
  865.         if (o == null || o.getClass() != this.getClass()) {
  866.             return false;
  867.         }
  868.         final TimeOffset timeOffset = (TimeOffset) o;
  869.         return seconds == timeOffset.seconds && attoSeconds == timeOffset.attoSeconds;
  870.     }

  871.     /** {@inheritDoc} */
  872.     @Override
  873.     public int hashCode() {
  874.         return Long.hashCode(seconds) ^ Long.hashCode(attoSeconds);
  875.     }

  876.     /** {@inheritDoc} */
  877.     @Override
  878.     public String toString() {
  879.         try {
  880.             if (attoSeconds < 0) {
  881.                 // gather all special cases in one big check to avoid rare multiple tests
  882.                 if (isNaN()) {
  883.                     return NAN_STRING;
  884.                 } else if (isPositiveInfinity()) {
  885.                     return POSITIVE_INFINITY_STRING;
  886.                 } else {
  887.                     return NEGATIVE_INTINITY_STRING;
  888.                 }
  889.             } else {
  890.                 final StringBuilder builder = new StringBuilder();
  891.                 final TimeOffset abs;
  892.                 if (seconds < 0L) {
  893.                     builder.append('-');
  894.                     abs = negate();
  895.                 } else {
  896.                     abs = this;
  897.                 }
  898.                 SECONDS_FORMATTER.appendTo(builder, abs.seconds);
  899.                 builder.append('.');
  900.                 ATTOSECONDS_FORMATTER.appendTo(builder, abs.attoSeconds);
  901.                 return builder.toString();
  902.             }
  903.         } catch (IOException ioe) {
  904.             // this should never happen
  905.             throw new OrekitInternalError(ioe);
  906.         }
  907.     }

  908.     /** Local class for summing several instances. */
  909.     private static class RunningSum {

  910.         /** Number of terms that can be added before normalization is needed. */
  911.         private static final int COUNT_DOWN_MAX = 9;

  912.         /** Seconds part. */
  913.         private long seconds;

  914.         /** AttoSeconds part. */
  915.         private long attoSeconds;

  916.         /** Indicator for NaN presence. */
  917.         private boolean addedNaN;

  918.         /** Indicator for +∞ presence. */
  919.         private boolean addedPositiveInfinity;

  920.         /** Indicator for -∞ presence. */
  921.         private boolean addedNegativeInfinity;

  922.         /** Countdown for checking carry. */
  923.         private int countDown;

  924.         /** Simple constructor.
  925.          */
  926.         RunningSum() {
  927.             countDown = COUNT_DOWN_MAX;
  928.         }

  929.         /** Add one term.
  930.          * @param term term to add
  931.          */
  932.         public void add(final TimeOffset term) {
  933.             if (term.isFinite()) {
  934.                 // regular addition
  935.                 seconds     += term.seconds;
  936.                 attoSeconds += term.attoSeconds;
  937.                 if (--countDown == 0) {
  938.                     // we have added several terms, we should normalize
  939.                     // the fields before attoseconds overflow (it may overflow after 9 additions)
  940.                     normalize();
  941.                 }
  942.             } else if (term.isNegativeInfinity()) {
  943.                 addedNegativeInfinity = true;
  944.             } else if (term.isPositiveInfinity()) {
  945.                 addedPositiveInfinity = true;
  946.             } else {
  947.                 addedNaN = true;
  948.             }
  949.         }

  950.         /** Normalize current running sum.
  951.          * @return normalized value
  952.          */
  953.         public TimeOffset normalize() {

  954.             // after normalization, we will have the equivalent of one entry processed
  955.             countDown = COUNT_DOWN_MAX - 1;

  956.             if (addedNaN || addedNegativeInfinity && addedPositiveInfinity) {
  957.                 // we have built a NaN
  958.                 seconds     = NaN.seconds;
  959.                 attoSeconds = NaN.attoSeconds;
  960.                 return NaN;
  961.             } else if (addedNegativeInfinity) {
  962.                 // we have built -∞
  963.                 seconds     = NEGATIVE_INFINITY.seconds;
  964.                 attoSeconds = NEGATIVE_INFINITY.attoSeconds;
  965.                 return NEGATIVE_INFINITY;
  966.             } else if (addedPositiveInfinity) {
  967.                 // we have built +∞
  968.                 seconds     = POSITIVE_INFINITY.seconds;
  969.                 attoSeconds = POSITIVE_INFINITY.attoSeconds;
  970.                 return POSITIVE_INFINITY;
  971.             } else {
  972.                 // this is a regular time
  973.                 final TimeOffset regular = new TimeOffset(seconds, attoSeconds);
  974.                 seconds     = regular.seconds;
  975.                 attoSeconds = regular.attoSeconds;
  976.                 return regular;
  977.             }
  978.         }

  979.     }

  980. }