FieldAbsoluteDate.java

  1. /* Copyright 2002-2024 CS GROUP
  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 java.time.Instant;
  19. import java.time.LocalDateTime;
  20. import java.time.ZoneOffset;
  21. import java.time.format.DateTimeFormatter;
  22. import java.util.Date;
  23. import java.util.TimeZone;

  24. import java.util.concurrent.TimeUnit;
  25. import org.hipparchus.CalculusFieldElement;
  26. import org.hipparchus.Field;
  27. import org.hipparchus.FieldElement;
  28. import org.hipparchus.analysis.differentiation.Derivative;
  29. import org.hipparchus.complex.Complex;
  30. import org.hipparchus.util.FastMath;
  31. import org.hipparchus.util.MathUtils;
  32. import org.hipparchus.util.MathUtils.FieldSumAndResidual;
  33. import org.hipparchus.util.MathUtils.SumAndResidual;
  34. import org.orekit.annotation.DefaultDataContext;
  35. import org.orekit.data.DataContext;
  36. import org.orekit.utils.Constants;

  37. /** This class represents a specific instant in time.

  38.  * <p>Instances of this class are considered to be absolute in the sense
  39.  * that each one represent the occurrence of some event and can be compared
  40.  * to other instances or located in <em>any</em> {@link TimeScale time scale}. In
  41.  * other words the different locations of an event with respect to two different
  42.  * time scales (say {@link TAIScale TAI} and {@link UTCScale UTC} for example) are
  43.  * simply different perspective related to a single object. Only one
  44.  * <code>FieldAbsoluteDate&lt;T&gt;</code> instance is needed, both representations being available
  45.  * from this single instance by specifying the time scales as parameter when calling
  46.  * the ad-hoc methods.</p>
  47.  *
  48.  * <p>Since an instance is not bound to a specific time-scale, all methods related
  49.  * to the location of the date within some time scale require to provide the time
  50.  * scale as an argument. It is therefore possible to define a date in one time scale
  51.  * and to use it in another one. An example of such use is to read a date from a file
  52.  * in UTC and write it in another file in TAI. This can be done as follows:</p>
  53.  * <pre>
  54.  *   DateTimeComponents utcComponents = readNextDate();
  55.  *   FieldAbsoluteDate&lt;T&gt; date = new FieldAbsoluteDate&lt;&gt;(utcComponents, TimeScalesFactory.getUTC());
  56.  *   writeNextDate(date.getComponents(TimeScalesFactory.getTAI()));
  57.  * </pre>
  58.  *
  59.  * <p>Two complementary views are available:</p>
  60.  * <ul>
  61.  *   <li><p>location view (mainly for input/output or conversions)</p>
  62.  *   <p>locations represent the coordinate of one event with respect to a
  63.  *   {@link TimeScale time scale}. The related methods are {@link
  64.  *   #FieldAbsoluteDate(Field, DateComponents, TimeComponents, TimeScale)}, {@link
  65.  *   #FieldAbsoluteDate(Field, int, int, int, int, int, double, TimeScale)}, {@link
  66.  *   #FieldAbsoluteDate(Field, int, int, int, TimeScale)}, {@link #FieldAbsoluteDate(Field,
  67.  *   Date, TimeScale)}, {@link #createGPSDate(int, CalculusFieldElement)}, {@link
  68.  *   #parseCCSDSCalendarSegmentedTimeCode(byte, byte[])}, {@link #toDate(TimeScale)},
  69.  *   {@link #toString(TimeScale) toString(timeScale)}, {@link #toString()},
  70.  *   and {@link #timeScalesOffset}.</p>
  71.  *   </li>
  72.  *   <li><p>offset view (mainly for physical computation)</p>
  73.  *   <p>offsets represent either the flow of time between two events
  74.  *   (two instances of the class) or durations. They are counted in seconds,
  75.  *   are continuous and could be measured using only a virtually perfect stopwatch.
  76.  *   The related methods are {@link #FieldAbsoluteDate(FieldAbsoluteDate, double)},
  77.  *   {@link #parseCCSDSUnsegmentedTimeCode(Field, byte, byte, byte[], FieldAbsoluteDate)},
  78.  *   {@link #parseCCSDSDaySegmentedTimeCode(Field, byte, byte[], DateComponents)},
  79.  *   {@link #durationFrom(FieldAbsoluteDate)}, {@link #compareTo(FieldAbsoluteDate)}, {@link #equals(Object)}
  80.  *   and {@link #hashCode()}.</p>
  81.  *   </li>
  82.  * </ul>
  83.  * <p>
  84.  * A few reference epochs which are commonly used in space systems have been defined. These
  85.  * epochs can be used as the basis for offset computation. The supported epochs are:
  86.  * {@link #getJulianEpoch(Field)}, {@link #getModifiedJulianEpoch(Field)}, {@link #getFiftiesEpoch(Field)},
  87.  * {@link #getCCSDSEpoch(Field)}, {@link #getGalileoEpoch(Field)}, {@link #getGPSEpoch(Field)},
  88.  * {@link #getJ2000Epoch(Field)}, {@link #getJavaEpoch(Field)}. There are also two factory methods
  89.  * {@link #createJulianEpoch(CalculusFieldElement)} and {@link #createBesselianEpoch(CalculusFieldElement)}
  90.  * that can be used to compute other reference epochs like J1900.0 or B1950.0.
  91.  * In addition to these reference epochs, two other constants are defined for convenience:
  92.  * {@link #getPastInfinity(Field)} and {@link #getFutureInfinity(Field)}, which can be used either
  93.  * as dummy dates when a date is not yet initialized, or for initialization of loops searching for
  94.  * a min or max date.
  95.  * </p>
  96.  * <p>
  97.  * Instances of the <code>FieldAbsoluteDate&lt;T&gt;</code> class are guaranteed to be immutable.
  98.  * </p>
  99.  * @author Luc Maisonobe
  100.  * @see TimeScale
  101.  * @see TimeStamped
  102.  * @see ChronologicalComparator
  103.  * @param <T> type of the field elements
  104.  */
  105. public class FieldAbsoluteDate<T extends CalculusFieldElement<T>>
  106.         implements FieldTimeStamped<T>, FieldTimeShiftable<FieldAbsoluteDate<T>, T>, Comparable<FieldAbsoluteDate<T>> {

  107.     /** Reference epoch in seconds from 2000-01-01T12:00:00 TAI.
  108.      * <p>Beware, it is not {@link #getJ2000Epoch(Field)} since it is in TAI and not in TT.</p> */
  109.     private final long epoch;

  110.     /** Offset from the reference epoch in seconds. */
  111.     private final  T offset;

  112.     /** Field used by default.*/
  113.     private final Field<T> field;

  114.     /** Build an instance from an AbsoluteDate.
  115.      * @param field used by default
  116.      * @param date AbsoluteDate to instantiate as a FieldAbsoluteDate
  117.      */
  118.     public FieldAbsoluteDate(final Field<T> field, final AbsoluteDate date) {
  119.         this.field  = field;
  120.         this.epoch  = date.getEpoch();
  121.         this.offset = field.getZero().add(date.getOffset());
  122.     }

  123.     /** Create an instance with a default value ({@link #getJ2000Epoch(Field)}).
  124.      *
  125.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  126.      *
  127.      * @param field field used by default
  128.      * @see #FieldAbsoluteDate(Field, AbsoluteDate)
  129.      */
  130.     @DefaultDataContext
  131.     public FieldAbsoluteDate(final Field<T> field) {
  132.         final FieldAbsoluteDate<T> j2000 = getJ2000Epoch(field);
  133.         this.field  = j2000.field;
  134.         this.epoch  = j2000.epoch;
  135.         this.offset = j2000.offset;
  136.     }

  137.     /** Build an instance from an elapsed duration since to another instant.
  138.      * <p>It is important to note that the elapsed duration is <em>not</em>
  139.      * the difference between two readings on a time scale. As an example,
  140.      * the duration between the two instants leading to the readings
  141.      * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC}
  142.      * time scale is <em>not</em> 1 second, but a stop watch would have measured
  143.      * an elapsed duration of 2 seconds between these two instances because a leap
  144.      * second was introduced at the end of 2005 in this time scale.</p>
  145.      * <p>This constructor is the reverse of the {@link #durationFrom(FieldAbsoluteDate)}
  146.      * method.</p>
  147.      * @param since start instant of the measured duration
  148.      * @param elapsedDuration physically elapsed duration from the <code>since</code>
  149.      * instant, as measured in a regular time scale
  150.      * @see #durationFrom(FieldAbsoluteDate)
  151.      */
  152.     public FieldAbsoluteDate(final FieldAbsoluteDate<T> since, final T elapsedDuration) {
  153.         this.field = since.field;
  154.         // Use 2Sum for high precision.
  155.         final FieldSumAndResidual<T> sumAndResidual = MathUtils.twoSum(since.offset, elapsedDuration);
  156.         if (Double.isInfinite(sumAndResidual.getSum().getReal())) {
  157.             offset = sumAndResidual.getSum();
  158.             epoch = (sumAndResidual.getSum().getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
  159.         } else {
  160.             final long dl = (long) FastMath.floor(sumAndResidual.getSum().getReal());
  161.             final T regularOffset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
  162.             if (regularOffset.getReal() >= 0) {
  163.                 // regular case, the offset is between 0.0 and 1.0
  164.                 offset = regularOffset;
  165.                 epoch = since.epoch + dl;
  166.             } else {
  167.                 // very rare case, the offset is just before a whole second
  168.                 // we will loose some bits of accuracy when adding 1 second
  169.                 // but this will ensure the offset remains in the [0.0; 1.0] interval
  170.                 offset = regularOffset.add(1.0);
  171.                 epoch  = since.epoch + dl - 1;
  172.             }
  173.         }
  174.     }

  175.     /** Build an instance from a location (parsed from a string) in a {@link TimeScale time scale}.
  176.      * <p>
  177.      * The supported formats for location are mainly the ones defined in ISO-8601 standard,
  178.      * the exact subset is explained in {@link DateTimeComponents#parseDateTime(String)},
  179.      * {@link DateComponents#parseDate(String)} and {@link TimeComponents#parseTime(String)}.
  180.      * </p>
  181.      * <p>
  182.      * As CCSDS ASCII calendar segmented time code is a trimmed down version of ISO-8601,
  183.      * it is also supported by this constructor.
  184.      * </p>
  185.      * @param field field utilized by default
  186.      * @param location location in the time scale, must be in a supported format
  187.      * @param timeScale time scale
  188.      * @exception IllegalArgumentException if location string is not in a supported format
  189.      */
  190.     public FieldAbsoluteDate(final Field<T> field, final String location, final TimeScale timeScale) {
  191.         this(field, DateTimeComponents.parseDateTime(location), timeScale);
  192.     }

  193.     /** Build an instance from a location in a {@link TimeScale time scale}.
  194.      * @param field field utilized by default
  195.      * @param location location in the time scale
  196.      * @param timeScale time scale
  197.      */
  198.     public FieldAbsoluteDate(final Field<T> field, final DateTimeComponents location, final TimeScale timeScale) {
  199.         this(field, location.getDate(), location.getTime(), timeScale);
  200.     }

  201.     /** Build an instance from a location in a {@link TimeScale time scale}.
  202.      * @param field field utilized by default
  203.      * @param date date location in the time scale
  204.      * @param time time location in the time scale
  205.      * @param timeScale time scale
  206.      */
  207.     public FieldAbsoluteDate(final Field<T> field, final DateComponents date, final TimeComponents time,
  208.                              final TimeScale timeScale) {
  209.         final double seconds  = time.getSecond();
  210.         final double tsOffset = timeScale.offsetToTAI(date, time);

  211.         // Use 2Sum for high precision.
  212.         final SumAndResidual sumAndResidual = MathUtils.twoSum(seconds, tsOffset);
  213.         final long dl = (long) FastMath.floor(sumAndResidual.getSum());
  214.         final T regularOffset = field.getZero().add((sumAndResidual.getSum() - dl) + sumAndResidual.getResidual());
  215.         if (regularOffset.getReal() >= 0) {
  216.             // regular case, the offset is between 0.0 and 1.0
  217.             offset = regularOffset;
  218.             epoch  = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L +
  219.                             time.getMinute() - time.getMinutesFromUTC() - 720L) + dl;
  220.         } else {
  221.             // very rare case, the offset is just before a whole second
  222.             // we will loose some bits of accuracy when adding 1 second
  223.             // but this will ensure the offset remains in the [0.0; 1.0] interval
  224.             offset = regularOffset.add(1.0);
  225.             epoch  = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L +
  226.                             time.getMinute() - time.getMinutesFromUTC() - 720L) + dl - 1;
  227.         }
  228.         this.field = field;

  229.     }

  230.     /** Build an instance from a location in a {@link TimeScale time scale}.
  231.      * @param field field utilized by default
  232.      * @param year year number (may be 0 or negative for BC years)
  233.      * @param month month number from 1 to 12
  234.      * @param day day number from 1 to 31
  235.      * @param hour hour number from 0 to 23
  236.      * @param minute minute number from 0 to 59
  237.      * @param second second number from 0.0 to 60.0 (excluded)
  238.      * @param timeScale time scale
  239.      * @exception IllegalArgumentException if inconsistent arguments
  240.      * are given (parameters out of range)
  241.      */
  242.     public FieldAbsoluteDate(final Field<T> field, final int year, final int month, final int day,
  243.                              final int hour, final int minute, final double second,
  244.                              final TimeScale timeScale) throws IllegalArgumentException {
  245.         this(field, new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
  246.     }

  247.     /** Build an instance from a location in a {@link TimeScale time scale}.
  248.      * @param field field utilized by default
  249.      * @param year year number (may be 0 or negative for BC years)
  250.      * @param month month enumerate
  251.      * @param day day number from 1 to 31
  252.      * @param hour hour number from 0 to 23
  253.      * @param minute minute number from 0 to 59
  254.      * @param second second number from 0.0 to 60.0 (excluded)
  255.      * @param timeScale time scale
  256.      * @exception IllegalArgumentException if inconsistent arguments
  257.      * are given (parameters out of range)
  258.      */
  259.     public FieldAbsoluteDate(final Field<T> field, final int year, final Month month, final int day,
  260.                              final int hour, final int minute, final double second,
  261.                              final TimeScale timeScale) throws IllegalArgumentException {
  262.         this(field, new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
  263.     }

  264.     /** Build an instance from a location in a {@link TimeScale time scale}.
  265.      * <p>The hour is set to 00:00:00.000.</p>
  266.      * @param field field utilized by default
  267.      * @param date date location in the time scale
  268.      * @param timeScale time scale
  269.      * @exception IllegalArgumentException if inconsistent arguments
  270.      * are given (parameters out of range)
  271.      */
  272.     public FieldAbsoluteDate(final Field<T> field, final DateComponents date, final TimeScale timeScale)
  273.                     throws IllegalArgumentException {
  274.         this(field, date, TimeComponents.H00, timeScale);
  275.     }

  276.     /** Build an instance from a location in a {@link TimeScale time scale}.
  277.      * <p>The hour is set to 00:00:00.000.</p>
  278.      * @param field field utilized by default
  279.      * @param year year number (may be 0 or negative for BC years)
  280.      * @param month month number from 1 to 12
  281.      * @param day day number from 1 to 31
  282.      * @param timeScale time scale
  283.      * @exception IllegalArgumentException if inconsistent arguments
  284.      * are given (parameters out of range)
  285.      */
  286.     public FieldAbsoluteDate(final Field<T> field, final int year, final int month, final int day,
  287.                              final TimeScale timeScale) throws IllegalArgumentException {
  288.         this(field, new DateComponents(year, month, day), TimeComponents.H00, timeScale);
  289.     }

  290.     /** Build an instance from a location in a {@link TimeScale time scale}.
  291.      * <p>The hour is set to 00:00:00.000.</p>
  292.      * @param field field utilized by default
  293.      * @param year year number (may be 0 or negative for BC years)
  294.      * @param month month enumerate
  295.      * @param day day number from 1 to 31
  296.      * @param timeScale time scale
  297.      * @exception IllegalArgumentException if inconsistent arguments
  298.      * are given (parameters out of range)
  299.      */
  300.     public FieldAbsoluteDate(final Field<T> field, final int year, final Month month, final int day,
  301.                              final TimeScale timeScale) throws IllegalArgumentException {
  302.         this(field, new DateComponents(year, month, day), TimeComponents.H00, timeScale);
  303.     }

  304.     /** Build an instance from a location in a {@link TimeScale time scale}.
  305.      * @param field field utilized as default
  306.      * @param location location in the time scale
  307.      * @param timeScale time scale
  308.      */
  309.     public FieldAbsoluteDate(final Field<T> field, final Date location, final TimeScale timeScale) {
  310.         this(field, new DateComponents(DateComponents.JAVA_EPOCH,
  311.                                        (int) (location.getTime() / 86400000L)),
  312.              new TimeComponents(0.001 * (location.getTime() % 86400000L)),
  313.              timeScale);
  314.     }

  315.     /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}.
  316.      * @param field field utilized as default
  317.      * @param instant instant in the time scale
  318.      * @param timeScale time scale
  319.      * @since 12.0
  320.      */
  321.     public FieldAbsoluteDate(final Field<T> field, final Instant instant, final TimeScale timeScale) {
  322.         this(field, new DateComponents(DateComponents.JAVA_EPOCH,
  323.                                        (int) (instant.getEpochSecond() / 86400L)),
  324.              instantToTimeComponents(instant),
  325.              timeScale);
  326.     }

  327.     /** Build an instance from an {@link Instant instant} in utc time scale.
  328.      * @param field field utilized as default
  329.      * @param instant instant in the utc timescale
  330.      * @since 12.1
  331.      */
  332.     @DefaultDataContext
  333.     public FieldAbsoluteDate(final Field<T> field, final Instant instant) {
  334.         this(field, instant, TimeScalesFactory.getUTC());
  335.     }

  336.     /** Build an instance from an {@link Instant instant} in the {@link UTCScale time scale}.
  337.      * @param field field utilized as default
  338.      * @param instant instant in the time scale
  339.      * @param utcScale utc time scale
  340.      * @since 12.1
  341.      */
  342.     public FieldAbsoluteDate(final Field<T> field, final Instant instant, final UTCScale utcScale) {
  343.         this(field, new DateComponents(DateComponents.JAVA_EPOCH,
  344.                 (int) (instant.getEpochSecond() / 86400l)),
  345.             instantToTimeComponents(instant),
  346.             utcScale);
  347.     }

  348.     /** Build an instance from an elapsed duration since to another instant.
  349.      * <p>It is important to note that the elapsed duration is <em>not</em>
  350.      * the difference between two readings on a time scale.
  351.      * @param since start instant of the measured duration
  352.      * @param elapsedDuration physically elapsed duration from the <code>since</code>
  353.      * instant, as measured in a regular time scale
  354.      */
  355.     public FieldAbsoluteDate(final FieldAbsoluteDate<T> since, final double elapsedDuration) {
  356.         this(since.epoch, elapsedDuration, since.offset);
  357.     }

  358.     /** Build an instance from an elapsed duration since to another instant.
  359.      * <p>It is important to note that the elapsed duration is <em>not</em>
  360.      * the difference between two readings on a time scale.
  361.      * @param since start instant of the measured duration
  362.      * @param elapsedDuration physically elapsed duration from the <code>since</code>
  363.      * instant, as measured in a regular time scale
  364.      * @param timeUnit {@link TimeUnit} of the elapsed duration
  365.      * @since 12.1
  366.      */
  367.     public FieldAbsoluteDate(final FieldAbsoluteDate<T> since, final long elapsedDuration, final TimeUnit timeUnit) {
  368.         this(since.epoch, elapsedDuration, timeUnit, since.offset);
  369.     }


  370.     /** Build an instance from an elapsed duration since to another instant.
  371.      * <p>It is important to note that the elapsed duration is <em>not</em>
  372.      * the difference between two readings on a time scale.
  373.      * @param since start instant of the measured duration
  374.      * @param elapsedDuration physically elapsed duration from the <code>since</code>
  375.      * instant, as measured in a regular time scale
  376.      */
  377.     public FieldAbsoluteDate(final AbsoluteDate since, final T elapsedDuration) {
  378.         this(since.getEpoch(), since.getOffset(), elapsedDuration);
  379.     }

  380.     /** Build an instance from an elapsed duration since to another instant.
  381.      * <p>It is important to note that the elapsed duration is <em>not</em>
  382.      * the difference between two readings on a time scale.
  383.      * @param since start instant of the measured duration
  384.      * @param elapsedDuration physically elapsed duration from the <code>since</code>
  385.      * instant, as measured in a regular time scale
  386.      * @param timeUnit {@link TimeUnit} of the elapsed duration
  387.      * @param field field utilized by default
  388.      * @since 12.1
  389.      */
  390.     public FieldAbsoluteDate(final AbsoluteDate since,  final long elapsedDuration, final TimeUnit timeUnit, final Field<T> field) {
  391.         this.field = field;

  392.         final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(elapsedDuration, timeUnit);
  393.         final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1);
  394.         final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1);
  395.         final T newOffset = field.getZero().add(since.getOffset()).add(deltaOffset);

  396.         if (newOffset.getReal() >= 1.0) {
  397.             // newOffset is in [1.0, 2.0]
  398.             this.epoch = since.getEpoch() + deltaEpoch + 1L;
  399.             this.offset = newOffset.subtract(1.0);
  400.         } else if (newOffset.getReal() < 0) {
  401.             this.epoch = since.getEpoch() + deltaEpoch - 1L;
  402.             this.offset = newOffset.add(1.0);
  403.         } else {
  404.             this.epoch = since.getEpoch() + deltaEpoch;
  405.             this.offset = newOffset;
  406.         }
  407.     }

  408.     /** Build an instance from an apparent clock offset with respect to another
  409.      * instant <em>in the perspective of a specific {@link TimeScale time scale}</em>.
  410.      * <p>It is important to note that the apparent clock offset <em>is</em> the
  411.      * difference between two readings on a time scale and <em>not</em> an elapsed
  412.      * duration. As an example, the apparent clock offset between the two instants
  413.      * leading to the readings 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the
  414.      * {@link UTCScale UTC} time scale is 1 second, but the elapsed duration is 2
  415.      * seconds because a leap second has been introduced at the end of 2005 in this
  416.      * time scale.</p>
  417.      * <p>This constructor is the reverse of the {@link #offsetFrom(FieldAbsoluteDate,
  418.      * TimeScale)} method.</p>
  419.      * @param reference reference instant
  420.      * @param apparentOffset apparent clock offset from the reference instant
  421.      * (difference between two readings in the specified time scale)
  422.      * @param timeScale time scale with respect to which the offset is defined
  423.      * @see #offsetFrom(FieldAbsoluteDate, TimeScale)
  424.      */
  425.     public FieldAbsoluteDate(final FieldAbsoluteDate<T> reference, final double apparentOffset, final TimeScale timeScale) {
  426.         this(reference.field, new DateTimeComponents(reference.getComponents(timeScale), apparentOffset),
  427.              timeScale);
  428.     }

  429.     /** Build an instance from mixed double and field raw components.
  430.      * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI
  431.      * @param tA double part of offset since reference epoch
  432.      * @param tB field part of offset since reference epoch
  433.      * @since 9.3
  434.      */
  435.     private FieldAbsoluteDate(final long epoch, final double tA, final T tB) {
  436.         this.field = tB.getField();
  437.         // Use 2Sum for high precision.
  438.         final FieldSumAndResidual<T> sumAndResidual = MathUtils.twoSum(field.getZero().add(tA), tB);
  439.         if (Double.isInfinite(sumAndResidual.getSum().getReal())) {
  440.             this.offset = sumAndResidual.getSum();
  441.             this.epoch  = (sumAndResidual.getSum().getReal() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
  442.         } else {
  443.             final long dl = (long) FastMath.floor(sumAndResidual.getSum().getReal());
  444.             final T regularOffset = sumAndResidual.getSum().subtract(dl).add(sumAndResidual.getResidual());
  445.             if (regularOffset.getReal() >= 0) {
  446.                 // regular case, the offset is between 0.0 and 1.0
  447.                 this.offset = regularOffset;
  448.                 this.epoch  = epoch + dl;
  449.             } else {
  450.                 // very rare case, the offset is just before a whole second
  451.                 // we will lose some bits of accuracy when adding 1 second
  452.                 // but this will ensure the offset remains in the [0.0; 1.0) interval
  453.                 this.offset = regularOffset.add(1.0);
  454.                 this.epoch  = epoch + dl - 1;
  455.             }
  456.         }
  457.     }

  458.     /** Build an instance from mixed double and field raw components.
  459.      * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI
  460.      * @param tA numeric part of offset since reference epoch
  461.      * @param tATimeUnit {@link TimeUnit} for tA
  462.      * @param tB field part of offset since reference epoch
  463.      * @since 12.1
  464.      */
  465.     private FieldAbsoluteDate(final long epoch, final long tA, final TimeUnit tATimeUnit, final T tB) {
  466.         this.field = tB.getField();

  467.         final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(tA, tATimeUnit);
  468.         final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1);
  469.         final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1);
  470.         final T newOffset = field.getZero().add(tB).add(deltaOffset);

  471.         if (newOffset.getReal() >= 1.0) {
  472.             // newOffset is in [1.0, 2.0]
  473.             this.epoch = epoch + deltaEpoch + 1L;
  474.             offset = newOffset.subtract(1.0);
  475.         } else if (newOffset.getReal() < 0) {
  476.             this.epoch = epoch + deltaEpoch - 1L;
  477.             offset = newOffset.add(1.0);
  478.         } else {
  479.             this.epoch = epoch + deltaEpoch;
  480.             offset = newOffset;
  481.         }
  482.     }

  483.     /** Extract time components from an instant within the day.
  484.      * @param instant instant to extract the number of seconds within the day
  485.      * @return time components
  486.      */
  487.     private static TimeComponents instantToTimeComponents(final Instant instant) {
  488.         final int secInDay = (int) (instant.getEpochSecond() % 86400L);
  489.         return new TimeComponents(secInDay, 1.0e-9 * instant.getNano());
  490.     }

  491.     /** Build an instance from a CCSDS Unsegmented Time Code (CUC).
  492.      * <p>
  493.      * CCSDS Unsegmented Time Code is defined in the blue book:
  494.      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
  495.      * </p>
  496.      * <p>
  497.      * If the date to be parsed is formatted using version 3 of the standard
  498.      * (CCSDS 301.0-B-3 published in 2002) or if the extension of the preamble
  499.      * field introduced in version 4 of the standard is not used, then the
  500.      * {@code preambleField2} parameter can be set to 0.
  501.      * </p>
  502.      *
  503.      * <p>This method uses the {@link DataContext#getDefault() default data context} if
  504.      * the CCSDS epoch is used.
  505.      *
  506.      * @param field field for the components
  507.      * @param preambleField1 first byte of the field specifying the format, often
  508.      * not transmitted in data interfaces, as it is constant for a given data interface
  509.      * @param preambleField2 second byte of the field specifying the format
  510.      * (added in revision 4 of the CCSDS standard in 2010), often not transmitted in data
  511.      * interfaces, as it is constant for a given data interface (value ignored if presence
  512.      * not signaled in {@code preambleField1})
  513.      * @param timeField byte array containing the time code
  514.      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
  515.      * specifies the {@link #getCCSDSEpoch(Field) CCSDS reference epoch} is used (and hence
  516.      * may be null in this case)
  517.      * @return an instance corresponding to the specified date
  518.      * @param <T> the type of the field elements
  519.      * @see #parseCCSDSUnsegmentedTimeCode(Field, byte, byte, byte[], FieldAbsoluteDate,
  520.      * FieldAbsoluteDate)
  521.      */
  522.     @DefaultDataContext
  523.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> parseCCSDSUnsegmentedTimeCode(final Field<T> field,
  524.                                                                                                          final byte preambleField1,
  525.                                                                                                          final byte preambleField2,
  526.                                                                                                          final byte[] timeField,
  527.                                                                                                          final FieldAbsoluteDate<T> agencyDefinedEpoch) {
  528.         return parseCCSDSUnsegmentedTimeCode(field, preambleField1, preambleField2,
  529.                                              timeField, agencyDefinedEpoch,
  530.                                              new FieldAbsoluteDate<>(
  531.                                                              field,
  532.                                                              DataContext.getDefault().getTimeScales().getCcsdsEpoch()));
  533.     }

  534.     /**
  535.      * Build an instance from a CCSDS Unsegmented Time Code (CUC).
  536.      * <p>
  537.      * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format
  538.      * (CCSDS 301.0-B-4) published in November 2010
  539.      * </p>
  540.      * <p>
  541.      * If the date to be parsed is formatted using version 3 of the standard (CCSDS
  542.      * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced
  543.      * in version 4 of the standard is not used, then the {@code preambleField2} parameter
  544.      * can be set to 0.
  545.      * </p>
  546.      *
  547.      * @param <T>                the type of the field elements
  548.      * @param field              field for the components
  549.      * @param preambleField1     first byte of the field specifying the format, often not
  550.      *                           transmitted in data interfaces, as it is constant for a
  551.      *                           given data interface
  552.      * @param preambleField2     second byte of the field specifying the format (added in
  553.      *                           revision 4 of the CCSDS standard in 2010), often not
  554.      *                           transmitted in data interfaces, as it is constant for a
  555.      *                           given data interface (value ignored if presence not
  556.      *                           signaled in {@code preambleField1})
  557.      * @param timeField          byte array containing the time code
  558.      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies
  559.      *                           the CCSDS reference epoch is used (and hence may be null
  560.      *                           in this case)
  561.      * @param ccsdsEpoch         reference epoch, ignored if the preamble field specifies
  562.      *                           the agency epoch is used.
  563.      * @return an instance corresponding to the specified date
  564.      * @since 10.1
  565.      */
  566.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> parseCCSDSUnsegmentedTimeCode(final Field<T> field,
  567.                                                                                                          final byte preambleField1,
  568.                                                                                                          final byte preambleField2,
  569.                                                                                                          final byte[] timeField,
  570.                                                                                                          final FieldAbsoluteDate<T> agencyDefinedEpoch,
  571.                                                                                                          final FieldAbsoluteDate<T> ccsdsEpoch) {
  572.         final CcsdsUnsegmentedTimeCode<FieldAbsoluteDate<T>> timeCode =
  573.             new CcsdsUnsegmentedTimeCode<>(preambleField1, preambleField2, timeField,
  574.                                            agencyDefinedEpoch, ccsdsEpoch);
  575.         return new FieldAbsoluteDate<>(timeCode.getEpoch(), timeCode.getSeconds()).
  576.                shiftedBy(timeCode.getSubSecond());
  577.     }

  578.     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
  579.      * <p>
  580.      * CCSDS Day Segmented Time Code is defined in the blue book:
  581.      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
  582.      * </p>
  583.      *
  584.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  585.      *
  586.      * @param field field for the components
  587.      * @param preambleField field specifying the format, often not transmitted in
  588.      * data interfaces, as it is constant for a given data interface
  589.      * @param timeField byte array containing the time code
  590.      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
  591.      * specifies the {@link #getCCSDSEpoch(Field) CCSDS reference epoch} is used (and hence
  592.      * may be null in this case)
  593.      * @return an instance corresponding to the specified date
  594.      * @param <T> the type of the field elements
  595.      * @see #parseCCSDSDaySegmentedTimeCode(Field, byte, byte[], DateComponents,
  596.      * TimeScale)
  597.      */
  598.     @DefaultDataContext
  599.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> parseCCSDSDaySegmentedTimeCode(final Field<T> field,
  600.                                                                                                           final byte preambleField, final byte[] timeField,
  601.                                                                                                           final DateComponents agencyDefinedEpoch) {
  602.         return parseCCSDSDaySegmentedTimeCode(field, preambleField, timeField,
  603.                                               agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC());
  604.     }

  605.     /**
  606.      * Build an instance from a CCSDS Day Segmented Time Code (CDS).
  607.      * <p>
  608.      * CCSDS Day Segmented Time Code is defined in the blue book: CCSDS Time Code Format
  609.      * (CCSDS 301.0-B-4) published in November 2010
  610.      * </p>
  611.      *
  612.      * @param <T>                the type of the field elements
  613.      * @param field              field for the components
  614.      * @param preambleField      field specifying the format, often not transmitted in
  615.      *                           data interfaces, as it is constant for a given data
  616.      *                           interface
  617.      * @param timeField          byte array containing the time code
  618.      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies
  619.      *                           the {@link #getCCSDSEpoch(Field) CCSDS reference epoch}
  620.      *                           is used (and hence may be null in this case)
  621.      * @param utc                time scale used to compute date and time components.
  622.      * @return an instance corresponding to the specified date
  623.      * @since 10.1
  624.      */
  625.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> parseCCSDSDaySegmentedTimeCode(final Field<T> field,
  626.                                                                                                           final byte preambleField,
  627.                                                                                                           final byte[] timeField,
  628.                                                                                                           final DateComponents agencyDefinedEpoch,
  629.                                                                                                           final TimeScale utc) {

  630.         final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField,
  631.                                                                            agencyDefinedEpoch);
  632.         return new FieldAbsoluteDate<>(field, timeCode.getDate(), timeCode.getTime(), utc).
  633.                shiftedBy(timeCode.getSubSecond());
  634.     }

  635.     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
  636.      * <p>
  637.      * CCSDS Calendar Segmented Time Code is defined in the blue book:
  638.      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
  639.      * </p>
  640.      *
  641.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  642.      *
  643.      * @param preambleField field specifying the format, often not transmitted in
  644.      * data interfaces, as it is constant for a given data interface
  645.      * @param timeField byte array containing the time code
  646.      * @return an instance corresponding to the specified date
  647.      * @see #parseCCSDSCalendarSegmentedTimeCode(byte, byte[], TimeScale)
  648.      */
  649.     @DefaultDataContext
  650.     public FieldAbsoluteDate<T> parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField) {
  651.         return parseCCSDSCalendarSegmentedTimeCode(preambleField, timeField,
  652.                                                    DataContext.getDefault().getTimeScales().getUTC());
  653.     }

  654.     /**
  655.      * Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
  656.      * <p>
  657.      * CCSDS Calendar Segmented Time Code is defined in the blue book: CCSDS Time Code
  658.      * Format (CCSDS 301.0-B-4) published in November 2010
  659.      * </p>
  660.      *
  661.      * @param preambleField field specifying the format, often not transmitted in data
  662.      *                      interfaces, as it is constant for a given data interface
  663.      * @param timeField     byte array containing the time code
  664.      * @param utc           time scale used to compute date and time components.
  665.      * @return an instance corresponding to the specified date
  666.      * @since 10.1
  667.      */
  668.     public FieldAbsoluteDate<T> parseCCSDSCalendarSegmentedTimeCode(final byte preambleField,
  669.                                                                     final byte[] timeField,
  670.                                                                     final TimeScale utc) {
  671.         final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField);
  672.         return new FieldAbsoluteDate<>(field, timeCode.getDate(), timeCode.getTime(), utc).
  673.                shiftedBy(timeCode.getSubSecond());
  674.     }

  675.     /** Build an instance corresponding to a Julian Day date.
  676.      * @param jd Julian day
  677.      * @param secondsSinceNoon seconds in the Julian day
  678.      * (BEWARE, Julian days start at noon, so 0.0 is noon)
  679.      * @param timeScale time scale in which the seconds in day are defined
  680.      * @return a new instant
  681.      * @param <T> the type of the field elements
  682.      */
  683.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createJDDate(final int jd, final T secondsSinceNoon,
  684.                                                                                         final TimeScale timeScale) {
  685.         return new FieldAbsoluteDate<>(secondsSinceNoon.getField(), new DateComponents(DateComponents.JULIAN_EPOCH, jd),
  686.                         TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon);
  687.     }

  688.     /** Build an instance corresponding to a Julian Day date.
  689.      * <p>
  690.      * This function should be preferred to {@link #createJDDate(int, CalculusFieldElement, TimeScale)} when the target time scale
  691.      * has a non-constant offset with respect to TAI.
  692.      * <p>
  693.      * The idea is to introduce a pivot time scale that is close to the target time scale but has a constant bias with TAI.
  694.      * <p>
  695.      * For example, to get a date from an MJD in TDB time scale, it's advised to use the TT time scale
  696.      * as a pivot scale. TT is very close to TDB and has constant offset to TAI.
  697.      * </p>
  698.      * @param jd Julian day
  699.      * @param secondsSinceNoon seconds in the Julian day
  700.      * (BEWARE, Julian days start at noon, so 0.0 is noon)
  701.      * @param timeScale time scale in which the seconds in day are defined
  702.      * @param pivotTimeScale pivot timescale used as intermediate timescale
  703.      * @return a new instant
  704.      * @param <T> the type of the field elements
  705.      */
  706.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createJDDate(final int jd, final T secondsSinceNoon,
  707.                                                                                         final TimeScale timeScale,
  708.                                                                                         final TimeScale pivotTimeScale) {
  709.         // Get the date in pivot timescale
  710.         final FieldAbsoluteDate<T> dateInPivotTimeScale = createJDDate(jd, secondsSinceNoon, pivotTimeScale);

  711.         // Compare offsets to TAI of the two time scales
  712.         final T offsetFromTAI = timeScale.offsetFromTAI(dateInPivotTimeScale).
  713.                 subtract(pivotTimeScale.offsetFromTAI(dateInPivotTimeScale));

  714.         // Return date in desired timescale
  715.         return dateInPivotTimeScale.shiftedBy(offsetFromTAI.multiply(-1.));
  716.     }

  717.     /** Build an instance corresponding to a Modified Julian Day date.
  718.      * @param mjd modified Julian day
  719.      * @param secondsInDay seconds in the day
  720.      * @param timeScale time scale in which the seconds in day are defined
  721.      * @return a new instant
  722.      * @param <T> the type of the field elements
  723.      */
  724.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createMJDDate(final int mjd, final T secondsInDay,
  725.                                                                                          final TimeScale timeScale) {
  726.         return new FieldAbsoluteDate<>(secondsInDay.getField(),
  727.                         new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
  728.                         TimeComponents.H00,
  729.                         timeScale).shiftedBy(secondsInDay);
  730.     }

  731.     /** Build an instance corresponding to a GPS date.
  732.      *
  733.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  734.      *
  735.      * <p>GPS dates are provided as a week number starting at
  736.      * {@link #getGPSEpoch(Field) GPS epoch} and as a number of milliseconds
  737.      * since week start.</p>
  738.      * @param weekNumber week number since {@link #getGPSEpoch(Field) GPS epoch}
  739.      * @param milliInWeek number of milliseconds since week start
  740.      * @return a new instant
  741.      * @param <T> the type of the field elements
  742.      * @see #createGPSDate(int, CalculusFieldElement, TimeScale)
  743.      */
  744.     @DefaultDataContext
  745.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createGPSDate(final int weekNumber, final T milliInWeek) {
  746.         return createGPSDate(weekNumber, milliInWeek,
  747.                              DataContext.getDefault().getTimeScales().getGPS());
  748.     }

  749.     /**
  750.      * Build an instance corresponding to a GPS date.
  751.      * <p>GPS dates are provided as a week number starting at
  752.      * {@link #getGPSEpoch(Field) GPS epoch} and as a number of milliseconds since week
  753.      * start.</p>
  754.      *
  755.      * @param <T>         the type of the field elements
  756.      * @param weekNumber  week number since {@link #getGPSEpoch(Field) GPS epoch}
  757.      * @param milliInWeek number of milliseconds since week start
  758.      * @param gps         GPS time scale.
  759.      * @return a new instant
  760.      * @since 10.1
  761.      */
  762.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createGPSDate(
  763.                                                                                          final int weekNumber,
  764.                                                                                          final T milliInWeek,
  765.                                                                                          final TimeScale gps) {

  766.         final int day = (int) FastMath.floor(milliInWeek.getReal() / (1000.0 * Constants.JULIAN_DAY));
  767.         final T secondsInDay = milliInWeek.divide(1000.0).subtract(day * Constants.JULIAN_DAY);
  768.         return new FieldAbsoluteDate<>(milliInWeek.getField(), new DateComponents(DateComponents.GPS_EPOCH, weekNumber * 7 + day),
  769.                         TimeComponents.H00, gps).shiftedBy(secondsInDay);
  770.     }

  771.     /** Build an instance corresponding to a Julian Epoch (JE).
  772.      * <p>According to Lieske paper: <a
  773.      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
  774.      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
  775.      * vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is related to Julian Ephemeris Date as:
  776.      * <pre>JE = 2000.0 + (JED - 2451545.0) / 365.25</pre>
  777.      * <p>This method reverts the formula above and computes an {@code FieldAbsoluteDate<T>} from the Julian Epoch.
  778.      *
  779.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  780.      *
  781.      * @param <T> the type of the field elements
  782.      * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference J2000.0
  783.      * @return a new instant
  784.      * @see #getJ2000Epoch(Field)
  785.      * @see #createBesselianEpoch(CalculusFieldElement)
  786.      * @see #createJulianEpoch(CalculusFieldElement, TimeScales)
  787.      */
  788.     @DefaultDataContext
  789.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createJulianEpoch(final T julianEpoch) {
  790.         return createJulianEpoch(julianEpoch,
  791.                                  DataContext.getDefault().getTimeScales());
  792.     }

  793.     /**
  794.      * Build an instance corresponding to a Julian Epoch (JE).
  795.      * <p>According to Lieske paper: <a
  796.      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
  797.      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>,
  798.      * Astronomy and Astrophysics, vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is
  799.      * related to Julian Ephemeris Date as:
  800.      * <pre>JE = 2000.0 + (JED - 2451545.0) / 365.25</pre>
  801.      * <p>This method reverts the formula above and computes an {@code
  802.      * FieldAbsoluteDate<T>} from the Julian Epoch.
  803.      *
  804.      * @param <T>         the type of the field elements
  805.      * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference
  806.      *                    J2000.0
  807.      * @param timeScales  used in the computation.
  808.      * @return a new instant
  809.      * @see #getJ2000Epoch(Field)
  810.      * @see #createBesselianEpoch(CalculusFieldElement)
  811.      * @see TimeScales#createJulianEpoch(double)
  812.      * @since 10.1
  813.      */
  814.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createJulianEpoch(
  815.                                                                                              final T julianEpoch,
  816.                                                                                              final TimeScales timeScales) {

  817.         final Field<T> field = julianEpoch.getField();
  818.         return new FieldAbsoluteDate<>(new FieldAbsoluteDate<>(field, timeScales.getJ2000Epoch()),
  819.                         julianEpoch.subtract(2000.0).multiply(Constants.JULIAN_YEAR));
  820.     }

  821.     /** Build an instance corresponding to a Besselian Epoch (BE).
  822.      * <p>According to Lieske paper: <a
  823.      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
  824.      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
  825.      * vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch is related to Julian Ephemeris Date as:</p>
  826.      * <pre>
  827.      * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781
  828.      * </pre>
  829.      * <p>
  830.      * This method reverts the formula above and computes an {@code FieldAbsoluteDate<T>} from the Besselian Epoch.
  831.      * </p>
  832.      *
  833.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  834.      *
  835.      * @param <T> the type of the field elements
  836.      * @param besselianEpoch Besselian epoch, like 1950 for defining the classical reference B1950.0
  837.      * @return a new instant
  838.      * @see #createJulianEpoch(CalculusFieldElement)
  839.      * @see #createBesselianEpoch(CalculusFieldElement, TimeScales)
  840.      */
  841.     @DefaultDataContext
  842.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createBesselianEpoch(final T besselianEpoch) {
  843.         return createBesselianEpoch(besselianEpoch,
  844.                                     DataContext.getDefault().getTimeScales());
  845.     }

  846.     /**
  847.      * Build an instance corresponding to a Besselian Epoch (BE).
  848.      * <p>According to Lieske paper: <a
  849.      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
  850.      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>,
  851.      * Astronomy and Astrophysics, vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch
  852.      * is related to Julian Ephemeris Date as:</p>
  853.      * <pre>
  854.      * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781
  855.      * </pre>
  856.      * <p>
  857.      * This method reverts the formula above and computes an {@code FieldAbsoluteDate<T>}
  858.      * from the Besselian Epoch.
  859.      * </p>
  860.      *
  861.      * @param <T>            the type of the field elements
  862.      * @param besselianEpoch Besselian epoch, like 1950 for defining the classical
  863.      *                       reference B1950.0
  864.      * @param timeScales     used in the computation.
  865.      * @return a new instant
  866.      * @see #createJulianEpoch(CalculusFieldElement)
  867.      * @see TimeScales#createBesselianEpoch(double)
  868.      * @since 10.1
  869.      */
  870.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> createBesselianEpoch(
  871.                                                                                                 final T besselianEpoch,
  872.                                                                                                 final TimeScales timeScales) {

  873.         final Field<T> field = besselianEpoch.getField();
  874.         return new FieldAbsoluteDate<>(new FieldAbsoluteDate<>(field, timeScales.getJ2000Epoch()),
  875.                         besselianEpoch.subtract(1900).multiply(Constants.BESSELIAN_YEAR).add(
  876.                                                                                              Constants.JULIAN_DAY * (-36525) + Constants.JULIAN_DAY * 0.31352));
  877.     }

  878.     /** Reference epoch for julian dates: -4712-01-01T12:00:00 Terrestrial Time.
  879.      * <p>Both <code>java.util.Date</code> and {@link DateComponents} classes
  880.      * follow the astronomical conventions and consider a year 0 between
  881.      * years -1 and +1, hence this reference date lies in year -4712 and not
  882.      * in year -4713 as can be seen in other documents or programs that obey
  883.      * a different convention (for example the <code>convcal</code> utility).</p>
  884.      *
  885.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  886.      *
  887.      * @param <T> the type of the field elements
  888.      * @param field field for the components
  889.      * @return the reference epoch for julian dates as a FieldAbsoluteDate
  890.      * @see AbsoluteDate#JULIAN_EPOCH
  891.      * @see TimeScales#getJulianEpoch()
  892.      */
  893.     @DefaultDataContext
  894.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getJulianEpoch(final Field<T> field) {
  895.         return new FieldAbsoluteDate<>(field,
  896.                         DataContext.getDefault().getTimeScales().getJulianEpoch());
  897.     }

  898.     /** Reference epoch for modified julian dates: 1858-11-17T00:00:00 Terrestrial Time.
  899.      *
  900.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  901.      *
  902.      * @param <T> the type of the field elements
  903.      * @param field field for the components
  904.      * @return the reference epoch for modified julian dates as a FieldAbsoluteDate
  905.      * @see AbsoluteDate#MODIFIED_JULIAN_EPOCH
  906.      * @see TimeScales#getModifiedJulianEpoch()
  907.      */
  908.     @DefaultDataContext
  909.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getModifiedJulianEpoch(final Field<T> field) {
  910.         return new FieldAbsoluteDate<>(field,
  911.                         DataContext.getDefault().getTimeScales().getModifiedJulianEpoch());
  912.     }

  913.     /** Reference epoch for 1950 dates: 1950-01-01T00:00:00 Terrestrial Time.
  914.      *
  915.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  916.      *
  917.      * @param <T> the type of the field elements
  918.      * @param field field for the components
  919.      * @return the reference epoch for 1950 dates as a FieldAbsoluteDate
  920.      * @see AbsoluteDate#FIFTIES_EPOCH
  921.      * @see TimeScales#getFiftiesEpoch()
  922.      */
  923.     @DefaultDataContext
  924.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getFiftiesEpoch(final Field<T> field) {
  925.         return new FieldAbsoluteDate<>(field,
  926.                         DataContext.getDefault().getTimeScales().getFiftiesEpoch());
  927.     }

  928.     /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4).
  929.      * <p>
  930.      * This method uses the {@link DataContext#getDefault() default data context}.
  931.      * </p>
  932.      * 1958-01-01T00:00:00 International Atomic Time (<em>not</em> UTC).
  933.      * @param <T> the type of the field elements
  934.      * @param field field for the components
  935.      * @return the reference epoch for CCSDS Time Code Format as a FieldAbsoluteDate
  936.      * @see AbsoluteDate#CCSDS_EPOCH
  937.      * @see TimeScales#getCcsdsEpoch()
  938.      */
  939.     @DefaultDataContext
  940.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getCCSDSEpoch(final Field<T> field) {
  941.         return new FieldAbsoluteDate<>(field,
  942.                         DataContext.getDefault().getTimeScales().getCcsdsEpoch());
  943.     }

  944.     /** Reference epoch for Galileo System Time: 1999-08-22T00:00:00 UTC.
  945.      *
  946.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  947.      *
  948.      * @param <T> the type of the field elements
  949.      * @param field field for the components
  950.      * @return the reference epoch for Galileo System Time as a FieldAbsoluteDate
  951.      * @see AbsoluteDate#GALILEO_EPOCH
  952.      * @see TimeScales#getGalileoEpoch()
  953.      */
  954.     @DefaultDataContext
  955.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getGalileoEpoch(final Field<T> field) {
  956.         return new FieldAbsoluteDate<>(field,
  957.                         DataContext.getDefault().getTimeScales().getGalileoEpoch());
  958.     }

  959.     /** Reference epoch for GPS weeks: 1980-01-06T00:00:00 GPS time.
  960.      *
  961.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  962.      *
  963.      * @param <T> the type of the field elements
  964.      * @param field field for the components
  965.      * @return the reference epoch for GPS weeks as a FieldAbsoluteDate
  966.      * @see AbsoluteDate#GPS_EPOCH
  967.      * @see TimeScales#getGpsEpoch()
  968.      */
  969.     @DefaultDataContext
  970.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getGPSEpoch(final Field<T> field) {
  971.         return new FieldAbsoluteDate<>(field,
  972.                         DataContext.getDefault().getTimeScales().getGpsEpoch());
  973.     }

  974.     /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (<em>not</em> UTC).
  975.      *
  976.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  977.      *
  978.      * @param <T> the type of the field elements
  979.      * @param field field for the components
  980.      * @return the J2000.0 reference epoch as a FieldAbsoluteDate
  981.      * @see #createJulianEpoch(CalculusFieldElement)
  982.      * @see AbsoluteDate#J2000_EPOCH
  983.      * @see TimeScales#getJ2000Epoch()
  984.      */
  985.     @DefaultDataContext
  986.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getJ2000Epoch(final Field<T> field) {
  987.         return new FieldAbsoluteDate<>(field,
  988.                         DataContext.getDefault().getTimeScales().getJ2000Epoch());
  989.     }

  990.     /** Java Reference epoch: 1970-01-01T00:00:00 Universal Time Coordinate.
  991.      *
  992.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  993.      *
  994.      * <p>
  995.      * Between 1968-02-01 and 1972-01-01, UTC-TAI = 4.213 170 0s + (MJD - 39 126) x 0.002 592s.
  996.      * As on 1970-01-01 MJD = 40587, UTC-TAI = 8.000082s
  997.      * </p>
  998.      * @param <T> the type of the field elements
  999.      * @param field field for the components
  1000.      * @return the Java reference epoch as a FieldAbsoluteDate
  1001.      * @see AbsoluteDate#JAVA_EPOCH
  1002.      * @see TimeScales#getJavaEpoch()
  1003.      */
  1004.     @DefaultDataContext
  1005.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getJavaEpoch(final Field<T> field) {
  1006.         return new FieldAbsoluteDate<>(field,
  1007.                         DataContext.getDefault().getTimeScales().getJavaEpoch());
  1008.     }

  1009.     /** Dummy date at infinity in the past direction.
  1010.      * @param <T> the type of the field elements
  1011.      * @param field field for the components
  1012.      * @return a dummy date at infinity in the past direction as a FieldAbsoluteDate
  1013.      * @see AbsoluteDate#PAST_INFINITY
  1014.      * @see TimeScales#getPastInfinity()
  1015.      */
  1016.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getPastInfinity(final Field<T> field) {
  1017.         return new FieldAbsoluteDate<>(field, AbsoluteDate.PAST_INFINITY);
  1018.     }

  1019.     /** Dummy date at infinity in the future direction.
  1020.      * @param <T> the type of the field elements
  1021.      * @param field field for the components
  1022.      * @return a dummy date at infinity in the future direction as a FieldAbsoluteDate
  1023.      * @see AbsoluteDate#FUTURE_INFINITY
  1024.      * @see TimeScales#getFutureInfinity()
  1025.      */
  1026.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getFutureInfinity(final Field<T> field) {
  1027.         return new FieldAbsoluteDate<>(field, AbsoluteDate.FUTURE_INFINITY);
  1028.     }

  1029.     /**
  1030.      * Get an arbitrary date. Useful when a non-null date is needed but its values does
  1031.      * not matter.
  1032.      *
  1033.      * @param <T>   the type of the field elements
  1034.      * @param field field for the components
  1035.      * @return an arbitrary date.
  1036.      */
  1037.     public static <T extends CalculusFieldElement<T>> FieldAbsoluteDate<T> getArbitraryEpoch(final Field<T> field) {

  1038.         return new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH);
  1039.     }


  1040.     /** Get a time-shifted date.
  1041.      * <p>
  1042.      * Calling this method is equivalent to call {@code new FieldAbsoluteDate&lt;&gt;(this, dt)}.
  1043.      * </p>
  1044.      * @param dt time shift in seconds
  1045.      * @return a new date, shifted with respect to instance (which is immutable)
  1046.      * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double)
  1047.      * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double)
  1048.      * @see org.orekit.orbits.FieldOrbit#shiftedBy(double)
  1049.      * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double)
  1050.      */
  1051.     @Override
  1052.     public FieldAbsoluteDate<T> shiftedBy(final T dt) {
  1053.         return new FieldAbsoluteDate<>(this, dt);
  1054.     }

  1055.     /** Compute the physically elapsed duration between two instants.
  1056.      * <p>The returned duration is the number of seconds physically
  1057.      * elapsed between the two instants, measured in a regular time
  1058.      * scale with respect to surface of the Earth (i.e either the {@link
  1059.      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
  1060.      * GPSScale GPS scale}). It is the only method that gives a
  1061.      * duration with a physical meaning.</p>
  1062.      * <p>This method gives the same result (with less computation)
  1063.      * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)}
  1064.      * with a second argument set to one of the regular scales cited
  1065.      * above.</p>
  1066.      * <p>This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate,
  1067.      * double)} constructor.</p>
  1068.      * @param instant instant to subtract from the instance
  1069.      * @return offset in seconds between the two instants (positive
  1070.      * if the instance is posterior to the argument)
  1071.      * @see #offsetFrom(FieldAbsoluteDate, TimeScale)
  1072.      * @see #FieldAbsoluteDate(FieldAbsoluteDate, double)
  1073.      */
  1074.     public T durationFrom(final FieldAbsoluteDate<T> instant) {
  1075.         return offset.subtract(instant.offset).add(epoch - instant.epoch);
  1076.     }

  1077.     /** Compute the physically elapsed duration between two instants.
  1078.      * <p>The returned duration is the number of seconds physically
  1079.      * elapsed between the two instants, measured in a regular time
  1080.      * scale with respect to surface of the Earth (i.e either the {@link
  1081.      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
  1082.      * GPSScale GPS scale}). It is the only method that gives a
  1083.      * duration with a physical meaning.</p>
  1084.      * <p>This method gives the same result (with less computation)
  1085.      * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)}
  1086.      * with a second argument set to one of the regular scales cited
  1087.      * above.</p>
  1088.      * <p>This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate,
  1089.      * double)} constructor.</p>
  1090.      * @param instant instant to subtract from the instance
  1091.      * @param timeUnit {@link TimeUnit} precision for the offset
  1092.      * @return offset in seconds between the two instants (positive
  1093.      * if the instance is posterior to the argument)
  1094.      * @see #offsetFrom(FieldAbsoluteDate, TimeScale)
  1095.      * @see #FieldAbsoluteDate(FieldAbsoluteDate, double)
  1096.      */
  1097.     public T durationFrom(final FieldAbsoluteDate<T> instant, final TimeUnit timeUnit) {
  1098.         final long deltaEpoch = timeUnit.convert(epoch - instant.epoch, TimeUnit.SECONDS);

  1099.         final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS);
  1100.         final T deltaOffset = offset.getField().getZero().add(offset.subtract(instant.offset).multiply(multiplier).round());

  1101.         return deltaOffset.add(deltaEpoch);
  1102.     }

  1103.     /** Compute the physically elapsed duration between two instants.
  1104.      * <p>The returned duration is the number of seconds physically
  1105.      * elapsed between the two instants, measured in a regular time
  1106.      * scale with respect to surface of the Earth (i.e either the {@link
  1107.      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
  1108.      * GPSScale GPS scale}). It is the only method that gives a
  1109.      * duration with a physical meaning.</p>
  1110.      * <p>This method gives the same result (with less computation)
  1111.      * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)}
  1112.      * with a second argument set to one of the regular scales cited
  1113.      * above.</p>
  1114.      * <p>This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate,
  1115.      * double)} constructor.</p>
  1116.      * @param instant instant to subtract from the instance
  1117.      * @return offset in seconds between the two instants (positive
  1118.      * if the instance is posterior to the argument)
  1119.      * @see #offsetFrom(FieldAbsoluteDate, TimeScale)
  1120.      * @see #FieldAbsoluteDate(FieldAbsoluteDate, double)
  1121.      */
  1122.     public T durationFrom(final AbsoluteDate instant) {
  1123.         return offset.subtract(instant.getOffset()).add(epoch - instant.getEpoch());
  1124.     }

  1125.     /** Compute the physically elapsed duration between two instants.
  1126.      * <p>The returned duration is the number of seconds physically
  1127.      * elapsed between the two instants, measured in a regular time
  1128.      * scale with respect to surface of the Earth (i.e either the {@link
  1129.      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
  1130.      * GPSScale GPS scale}). It is the only method that gives a
  1131.      * duration with a physical meaning.</p>
  1132.      * <p>This method gives the same result (with less computation)
  1133.      * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)}
  1134.      * with a second argument set to one of the regular scales cited
  1135.      * above.</p>
  1136.      * <p>This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate,
  1137.      * double)} constructor.</p>
  1138.      * @param instant instant to subtract from the instance
  1139.      * @param timeUnit {@link TimeUnit} precision for the offset
  1140.      * @return offset in the given timeunit between the two instants (positive
  1141.      * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit}
  1142.      * @see #FieldAbsoluteDate(FieldAbsoluteDate, long, TimeUnit)
  1143.      * @since 12.1
  1144.      */
  1145.     public T durationFrom(final AbsoluteDate instant, final TimeUnit timeUnit) {
  1146.         final long deltaEpoch = timeUnit.convert(epoch - instant.getEpoch(), TimeUnit.SECONDS);

  1147.         final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS);
  1148.         final T deltaOffset = offset.getField().getZero().add(offset.subtract(instant.getOffset()).multiply(multiplier).round());

  1149.         return deltaOffset.add(deltaEpoch);
  1150.     }

  1151.     /** Compute the apparent clock offset between two instant <em>in the
  1152.      * perspective of a specific {@link TimeScale time scale}</em>.
  1153.      * <p>The offset is the number of seconds counted in the given
  1154.      * time scale between the locations of the two instants, with
  1155.      * all time scale irregularities removed (i.e. considering all
  1156.      * days are exactly 86400 seconds long). This method will give
  1157.      * a result that may not have a physical meaning if the time scale
  1158.      * is irregular. For example since a leap second was introduced at
  1159.      * the end of 2005, the apparent offset between 2005-12-31T23:59:59
  1160.      * and 2006-01-01T00:00:00 is 1 second, but the physical duration
  1161.      * of the corresponding time interval as returned by the {@link
  1162.      * #durationFrom(FieldAbsoluteDate)} method is 2 seconds.</p>
  1163.      * <p>This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate,
  1164.      * double, TimeScale)} constructor.</p>
  1165.      * @param instant instant to subtract from the instance
  1166.      * @param timeScale time scale with respect to which the offset should
  1167.      * be computed
  1168.      * @return apparent clock offset in seconds between the two instants
  1169.      * (positive if the instance is posterior to the argument)
  1170.      * @see #durationFrom(FieldAbsoluteDate)
  1171.      * @see #FieldAbsoluteDate(FieldAbsoluteDate, double, TimeScale)
  1172.      */
  1173.     public T offsetFrom(final FieldAbsoluteDate<T> instant, final TimeScale timeScale) {
  1174.         final long   elapsedDurationA = epoch - instant.epoch;
  1175.         final T elapsedDurationB = offset.add(timeScale.offsetFromTAI(this)).
  1176.                         subtract(instant.offset.add(timeScale.offsetFromTAI(instant)));
  1177.         return  elapsedDurationB.add(elapsedDurationA);
  1178.     }

  1179.     /** Compute the offset between two time scales at the current instant.
  1180.      * <p>The offset is defined as <i>l₁-l₂</i>
  1181.      * where <i>l₁</i> is the location of the instant in
  1182.      * the <code>scale1</code> time scale and <i>l₂</i> is the
  1183.      * location of the instant in the <code>scale2</code> time scale.</p>
  1184.      * @param scale1 first time scale
  1185.      * @param scale2 second time scale
  1186.      * @return offset in seconds between the two time scales at the
  1187.      * current instant
  1188.      */
  1189.     public T timeScalesOffset(final TimeScale scale1, final TimeScale scale2) {
  1190.         return scale1.offsetFromTAI(this).subtract(scale2.offsetFromTAI(this));
  1191.     }

  1192.     /** Convert the instance to a Java {@link java.util.Date Date}.
  1193.      * <p>Conversion to the Date class induces a loss of precision because
  1194.      * the Date class does not provide sub-millisecond information. Java Dates
  1195.      * are considered to be locations in some times scales.</p>
  1196.      * @param timeScale time scale to use
  1197.      * @return a {@link java.util.Date Date} instance representing the location
  1198.      * of the instant in the time scale
  1199.      */
  1200.     public Date toDate(final TimeScale timeScale) {
  1201.         final double time = epoch + (offset.getReal() + timeScale.offsetFromTAI(this).getReal());
  1202.         return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000));
  1203.     }

  1204.     /**
  1205.      * Convert the instance to a Java {@link java.time.Instant Instant}.
  1206.      * Nanosecond precision is preserved during this conversion
  1207.      *
  1208.      * @return a {@link java.time.Instant Instant} instance representing the location
  1209.      * of the instant in the utc time scale
  1210.      * @since 12.1
  1211.      */
  1212.     @DefaultDataContext
  1213.     public Instant toInstant() {
  1214.         return toInstant(TimeScalesFactory.getTimeScales());
  1215.     }

  1216.     /**
  1217.      * Convert the instance to a Java {@link java.time.Instant Instant}.
  1218.      * Nanosecond precision is preserved during this conversion
  1219.      *
  1220.      * @param timeScales the timescales to use
  1221.      * @return a {@link java.time.Instant Instant} instance representing the location
  1222.      * of the instant in the utc time scale
  1223.      * @since 12.1
  1224.      */
  1225.     public Instant toInstant(final TimeScales timeScales) {
  1226.         final UTCScale utc = timeScales.getUTC();
  1227.         final String stringWithoutUtcOffset = toStringWithoutUtcOffset(utc, 9);

  1228.         final LocalDateTime localDateTime = LocalDateTime.parse(stringWithoutUtcOffset, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
  1229.         return localDateTime.toInstant(ZoneOffset.UTC);
  1230.     }

  1231.     /** Split the instance into date/time components.
  1232.      * @param timeScale time scale to use
  1233.      * @return date/time components
  1234.      */
  1235.     public DateTimeComponents getComponents(final TimeScale timeScale) {

  1236.         if (Double.isInfinite(offset.getReal())) {
  1237.             // special handling for past and future infinity
  1238.             if (offset.getReal() < 0) {
  1239.                 return new DateTimeComponents(DateComponents.MIN_EPOCH, TimeComponents.H00);
  1240.             } else {
  1241.                 return new DateTimeComponents(DateComponents.MAX_EPOCH,
  1242.                                               new TimeComponents(23, 59, 59.999));
  1243.             }
  1244.         }

  1245.         // Compute offset from 2000-01-01T00:00:00 in specified time scale.
  1246.         // Use 2Sum for high accuracy.
  1247.         final double taiOffset = timeScale.offsetFromTAI(this).getReal();
  1248.         final SumAndResidual sumAndResidual = MathUtils.twoSum(offset.getReal(), taiOffset);

  1249.         // split date and time
  1250.         final long   carry = (long) FastMath.floor(sumAndResidual.getSum());
  1251.         double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual();
  1252.         long   offset2000A = epoch + carry + 43200L;
  1253.         if (offset2000B < 0) {
  1254.             offset2000A -= 1;
  1255.             offset2000B += 1;
  1256.         }
  1257.         long time = offset2000A % 86400L;
  1258.         if (time < 0L) {
  1259.             time += 86400L;
  1260.         }
  1261.         final int date = (int) ((offset2000A - time) / 86400L);

  1262.         // extract calendar elements
  1263.         final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date);
  1264.         // extract time element, accounting for leap seconds
  1265.         final double leap = timeScale.insideLeap(this) ? timeScale.getLeap(this.toAbsoluteDate()) : 0;
  1266.         final int minuteDuration = timeScale.minuteDuration(this);
  1267.         final TimeComponents timeComponents = TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration);

  1268.         // build the components
  1269.         return new DateTimeComponents(dateComponents, timeComponents);

  1270.     }

  1271.     /** Split the instance into date/time components for a local time.
  1272.      *
  1273.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1274.      *
  1275.      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
  1276.      * negative Westward UTC)
  1277.      * @return date/time components
  1278.      * @see #getComponents(int, TimeScale)
  1279.      */
  1280.     @DefaultDataContext
  1281.     public DateTimeComponents getComponents(final int minutesFromUTC) {
  1282.         return getComponents(minutesFromUTC,
  1283.                              DataContext.getDefault().getTimeScales().getUTC());
  1284.     }

  1285.     /**
  1286.      * Split the instance into date/time components for a local time.
  1287.      *
  1288.      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
  1289.      *                       negative Westward UTC)
  1290.      * @param utc            time scale used to compute date and time components.
  1291.      * @return date/time components
  1292.      * @since 10.1
  1293.      */
  1294.     public DateTimeComponents getComponents(final int minutesFromUTC,
  1295.                                             final TimeScale utc) {

  1296.         final DateTimeComponents utcComponents = getComponents(utc);

  1297.         // shift the date according to UTC offset, but WITHOUT touching the seconds,
  1298.         // as they may exceed 60.0 during a leap seconds introduction,
  1299.         // and we want to preserve these special cases
  1300.         final double seconds = utcComponents.getTime().getSecond();
  1301.         int minute = utcComponents.getTime().getMinute() + minutesFromUTC;
  1302.         final int hourShift;
  1303.         if (minute < 0) {
  1304.             hourShift = (minute - 59) / 60;
  1305.         } else if (minute > 59) {
  1306.             hourShift = minute / 60;
  1307.         } else {
  1308.             hourShift = 0;
  1309.         }
  1310.         minute -= 60 * hourShift;
  1311.         int hour = utcComponents.getTime().getHour() + hourShift;
  1312.         final int dayShift;
  1313.         if (hour < 0) {
  1314.             dayShift = (hour - 23) / 24;
  1315.         } else if (hour > 23) {
  1316.             dayShift = hour / 24;
  1317.         } else {
  1318.             dayShift = 0;
  1319.         }
  1320.         hour -= 24 * dayShift;

  1321.         return new DateTimeComponents(new DateComponents(utcComponents.getDate(), dayShift),
  1322.                                       new TimeComponents(hour, minute, seconds, minutesFromUTC));

  1323.     }

  1324.     /** {@inheritDoc} */
  1325.     @Override
  1326.     public FieldAbsoluteDate<T> getDate() {
  1327.         return this;
  1328.     }

  1329.     /** Get the field.
  1330.      * @return field instance.
  1331.      */
  1332.     public Field<T> getField() {
  1333.         return field;
  1334.     }

  1335.     /** Split the instance into date/time components for a time zone.
  1336.      *
  1337.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1338.      *
  1339.      * @param timeZone time zone
  1340.      * @return date/time components
  1341.      * @see #getComponents(TimeZone, TimeScale)
  1342.      */
  1343.     @DefaultDataContext
  1344.     public DateTimeComponents getComponents(final TimeZone timeZone) {
  1345.         return getComponents(timeZone, DataContext.getDefault().getTimeScales().getUTC());
  1346.     }

  1347.     /** Split the instance into date/time components for a time zone.
  1348.      * @param timeZone time zone
  1349.      * @param utc            time scale used to compute date and time components.
  1350.      * @return date/time components
  1351.      * @since 10.1
  1352.      */
  1353.     public DateTimeComponents getComponents(final TimeZone timeZone,
  1354.                                             final TimeScale utc) {
  1355.         final FieldAbsoluteDate<T> javaEpoch =
  1356.                         new FieldAbsoluteDate<>(field, DateComponents.JAVA_EPOCH, utc);
  1357.         final long milliseconds = FastMath.round((offsetFrom(javaEpoch, utc).getReal()) * 1000);
  1358.         return getComponents(timeZone.getOffset(milliseconds) / 60000, utc);
  1359.     }

  1360.     /** Compare the instance with another date.
  1361.      * @param date other date to compare the instance to
  1362.      * @return a negative integer, zero, or a positive integer as this date
  1363.      * is before, simultaneous, or after the specified date.
  1364.      */
  1365.     public int compareTo(final FieldAbsoluteDate<T> date) {
  1366.         return Double.compare(durationFrom(date).getReal(), 0.0);
  1367.     }


  1368.     /** Check if the instance represents the same time as another instance.
  1369.      * @param date other date
  1370.      * @return true if the instance and the other date refer to the same instant
  1371.      */
  1372.     @SuppressWarnings("unchecked")
  1373.     public boolean equals(final Object date) {

  1374.         if (date == this) {
  1375.             // first fast check
  1376.             return true;
  1377.         }

  1378.         if (date instanceof FieldAbsoluteDate) {
  1379.             return durationFrom((FieldAbsoluteDate<T>) date).getReal() == 0.0;
  1380.         }

  1381.         return false;

  1382.     }

  1383.     /** Check if the instance represents the same time as another.
  1384.      * @param other the instant to compare this date to
  1385.      * @return true if the instance and the argument refer to the same instant
  1386.      * @see #isCloseTo(FieldTimeStamped, double)
  1387.      * @since 10.1
  1388.      */
  1389.     public boolean isEqualTo(final FieldTimeStamped<T> other) {
  1390.         return this.equals(other.getDate());
  1391.     }

  1392.     /** Check if the instance time is close to another.
  1393.      * @param other the instant to compare this date to
  1394.      * @param tolerance the separation, in seconds, under which the two instants will be considered close to each other
  1395.      * @return true if the duration between the instance and the argument is strictly below the tolerance
  1396.      * @see #isEqualTo(FieldTimeStamped)
  1397.      * @since 10.1
  1398.      */
  1399.     public boolean isCloseTo(final FieldTimeStamped<T> other, final double tolerance) {
  1400.         return FastMath.abs(this.durationFrom(other.getDate()).getReal()) < tolerance;
  1401.     }

  1402.     /** Check if the instance represents a time that is strictly before another.
  1403.      * @param other the instant to compare this date to
  1404.      * @return true if the instance is strictly before the argument when ordering chronologically
  1405.      * @see #isBeforeOrEqualTo(FieldTimeStamped)
  1406.      * @since 10.1
  1407.      */
  1408.     public boolean isBefore(final FieldTimeStamped<T> other) {
  1409.         return this.compareTo(other.getDate()) < 0;
  1410.     }

  1411.     /** Check if the instance represents a time that is strictly after another.
  1412.      * @param other the instant to compare this date to
  1413.      * @return true if the instance is strictly after the argument when ordering chronologically
  1414.      * @see #isAfterOrEqualTo(FieldTimeStamped)
  1415.      * @since 10.1
  1416.      */
  1417.     public boolean isAfter(final FieldTimeStamped<T> other) {
  1418.         return this.compareTo(other.getDate()) > 0;
  1419.     }

  1420.     /** Check if the instance represents a time that is before or equal to another.
  1421.      * @param other the instant to compare this date to
  1422.      * @return true if the instance is before (or equal to) the argument when ordering chronologically
  1423.      * @see #isBefore(FieldTimeStamped)
  1424.      * @since 10.1
  1425.      */
  1426.     public boolean isBeforeOrEqualTo(final FieldTimeStamped<T> other) {
  1427.         return this.isEqualTo(other) || this.isBefore(other);
  1428.     }

  1429.     /** Check if the instance represents a time that is after or equal to another.
  1430.      * @param other the instant to compare this date to
  1431.      * @return true if the instance is after (or equal to) the argument when ordering chronologically
  1432.      * @see #isAfterOrEqualTo(FieldTimeStamped)
  1433.      * @since 10.1
  1434.      */
  1435.     public boolean isAfterOrEqualTo(final FieldTimeStamped<T> other) {
  1436.         return this.isEqualTo(other) || this.isAfter(other);
  1437.     }

  1438.     /** Check if the instance represents a time that is strictly between two others representing
  1439.      * the boundaries of a time span. The two boundaries can be provided in any order: in other words,
  1440.      * whether <code>boundary</code> represents a time that is before or after <code>otherBoundary</code> will
  1441.      * not change the result of this method.
  1442.      * @param boundary one end of the time span
  1443.      * @param otherBoundary the other end of the time span
  1444.      * @return true if the instance is strictly between the two arguments when ordering chronologically
  1445.      * @see #isBetweenOrEqualTo(FieldTimeStamped, FieldTimeStamped)
  1446.      * @since 10.1
  1447.      */
  1448.     public boolean isBetween(final FieldTimeStamped<T> boundary, final FieldTimeStamped<T> otherBoundary) {
  1449.         final FieldTimeStamped<T> beginning;
  1450.         final FieldTimeStamped<T> end;
  1451.         if (boundary.getDate().isBefore(otherBoundary)) {
  1452.             beginning = boundary;
  1453.             end = otherBoundary;
  1454.         } else {
  1455.             beginning = otherBoundary;
  1456.             end = boundary;
  1457.         }
  1458.         return this.isAfter(beginning) && this.isBefore(end);
  1459.     }

  1460.     /** Check if the instance represents a time that is between two others representing
  1461.      * the boundaries of a time span, or equal to one of them. The two boundaries can be provided in any order:
  1462.      * in other words, whether <code>boundary</code> represents a time that is before or after
  1463.      * <code>otherBoundary</code> will not change the result of this method.
  1464.      * @param boundary one end of the time span
  1465.      * @param otherBoundary the other end of the time span
  1466.      * @return true if the instance is between the two arguments (or equal to at least one of them)
  1467.      * when ordering chronologically
  1468.      * @see #isBetween(FieldTimeStamped, FieldTimeStamped)
  1469.      * @since 10.1
  1470.      */
  1471.     public boolean isBetweenOrEqualTo(final FieldTimeStamped<T> boundary, final FieldTimeStamped<T> otherBoundary) {
  1472.         return this.isEqualTo(boundary) || this.isEqualTo(otherBoundary) || this.isBetween(boundary, otherBoundary);
  1473.     }

  1474.     /** Get a hashcode for this date.
  1475.      * @return hashcode
  1476.      */
  1477.     public int hashCode() {
  1478.         final long l = Double.doubleToLongBits(durationFrom(AbsoluteDate.ARBITRARY_EPOCH).getReal());
  1479.         return (int) (l ^ (l >>> 32));
  1480.     }

  1481.     /**
  1482.      * Get a String representation of the instant location with up to 16 digits of
  1483.      * precision for the seconds value.
  1484.      *
  1485.      * <p> Since this method is used in exception messages and error handling every
  1486.      * effort is made to return some representation of the instant. If UTC is available
  1487.      * from the default data context then it is used to format the string in UTC. If not
  1488.      * then TAI is used. Finally if the prior attempts fail this method falls back to
  1489.      * converting this class's internal representation to a string.
  1490.      *
  1491.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1492.      *
  1493.      * @return a string representation of the instance, in ISO-8601 format if UTC is
  1494.      * available from the default data context.
  1495.      * @see AbsoluteDate#toString()
  1496.      * @see #toString(TimeScale)
  1497.      * @see DateTimeComponents#toString(int, int)
  1498.      */
  1499.     @DefaultDataContext
  1500.     public String toString() {
  1501.         return toAbsoluteDate().toString();
  1502.     }

  1503.     /**
  1504.      * Get a String representation of the instant location in ISO-8601 format without the
  1505.      * UTC offset and with up to 16 digits of precision for the seconds value.
  1506.      *
  1507.      * @param timeScale time scale to use
  1508.      * @return a string representation of the instance.
  1509.      * @see DateTimeComponents#toString(int, int)
  1510.      */
  1511.     public String toString(final TimeScale timeScale) {
  1512.         return getComponents(timeScale).toStringWithoutUtcOffset();
  1513.     }

  1514.     /** Get a String representation of the instant location for a local time.
  1515.      *
  1516.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1517.      *
  1518.      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
  1519.      * negative Westward UTC).
  1520.      * @return string representation of the instance,
  1521.      * in ISO-8601 format with milliseconds accuracy
  1522.      * @see #toString(int, TimeScale)
  1523.      */
  1524.     @DefaultDataContext
  1525.     public String toString(final int minutesFromUTC) {
  1526.         return toString(minutesFromUTC,
  1527.                         DataContext.getDefault().getTimeScales().getUTC());
  1528.     }

  1529.     /**
  1530.      * Get a String representation of the instant location for a local time.
  1531.      *
  1532.      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
  1533.      *                       negative Westward UTC).
  1534.      * @param utc            time scale used to compute date and time components.
  1535.      * @return string representation of the instance, in ISO-8601 format with milliseconds
  1536.      * accuracy
  1537.      * @since 10.1
  1538.      */
  1539.     public String toString(final int minutesFromUTC, final TimeScale utc) {
  1540.         final int minuteDuration = utc.minuteDuration(this);
  1541.         return getComponents(minutesFromUTC, utc).toString(minuteDuration);
  1542.     }

  1543.     /** Get a String representation of the instant location for a time zone.
  1544.      *
  1545.      * <p>This method uses the {@link DataContext#getDefault() default data context}.
  1546.      *
  1547.      * @param timeZone time zone
  1548.      * @return string representation of the instance,
  1549.      * in ISO-8601 format with milliseconds accuracy
  1550.      * @see #toString(TimeZone, TimeScale)
  1551.      */
  1552.     @DefaultDataContext
  1553.     public String toString(final TimeZone timeZone) {
  1554.         return toString(timeZone, DataContext.getDefault().getTimeScales().getUTC());
  1555.     }

  1556.     /**
  1557.      * Get a String representation of the instant location for a time zone.
  1558.      *
  1559.      * @param timeZone time zone
  1560.      * @param utc      time scale used to compute date and time components.
  1561.      * @return string representation of the instance, in ISO-8601 format with milliseconds
  1562.      * accuracy
  1563.      * @since 10.1
  1564.      */
  1565.     public String toString(final TimeZone timeZone, final TimeScale utc) {
  1566.         final int minuteDuration = utc.minuteDuration(this);
  1567.         return getComponents(timeZone, utc).toString(minuteDuration);
  1568.     }

  1569.     /**
  1570.      * Return a string representation of this date-time, rounded to the given precision.
  1571.      *
  1572.      * <p>The format used is ISO8601 without the UTC offset.</p>
  1573.      *
  1574.      *
  1575.      * @param timeScale      to use to compute components.
  1576.      * @param fractionDigits the number of digits to include after the decimal point in
  1577.      *                       the string representation of the seconds. The date and time
  1578.      *                       is first rounded as necessary. {@code fractionDigits} must be
  1579.      *                       greater than or equal to {@code 0}.
  1580.      * @return string representation of this date, time, and UTC offset
  1581.      * @see #toString(TimeScale)
  1582.      * @see DateTimeComponents#toString(int, int)
  1583.      * @see DateTimeComponents#toStringWithoutUtcOffset(int, int)
  1584.      * @since 12.2
  1585.      */
  1586.     public String toStringWithoutUtcOffset(final TimeScale timeScale,
  1587.         final int fractionDigits) {
  1588.         return this.getComponents(timeScale)
  1589.             .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits);
  1590.     }

  1591.     /** Get a time-shifted date.
  1592.      * <p>
  1593.      * Calling this method is equivalent to call <code>new FieldAbsoluteDate(this, dt)</code>.
  1594.      * </p>
  1595.      * @param dt time shift in seconds
  1596.      * @return a new date, shifted with respect to instance (which is immutable)
  1597.      * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double)
  1598.      * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double)
  1599.      * @see org.orekit.orbits.FieldOrbit#shiftedBy(double)
  1600.      * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double)
  1601.      */
  1602.     @Override
  1603.     public FieldAbsoluteDate<T> shiftedBy(final double dt) {
  1604.         return new FieldAbsoluteDate<>(this, dt);
  1605.     }

  1606.     /** Get a time-shifted date.
  1607.      * <p>
  1608.      * Calling this method is equivalent to call <code>new FieldAbsoluteDate(this, dt, timeUnit)</code>.
  1609.      * </p>
  1610.      * @param dt time shift in time units
  1611.      * @param timeUnit {@link TimeUnit} for dt
  1612.      * @return a new date, shifted with respect to instance (which is immutable)
  1613.      * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double)
  1614.      * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double)
  1615.      * @see org.orekit.orbits.FieldOrbit#shiftedBy(double)
  1616.      * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double)
  1617.      * @since 12.1
  1618.      */
  1619.     public FieldAbsoluteDate<T> shiftedBy(final long dt, final TimeUnit timeUnit) {
  1620.         return new FieldAbsoluteDate<>(this, dt, timeUnit);
  1621.     }


  1622.     /** Transform the FieldAbsoluteDate in an AbsoluteDate.
  1623.      * @return AbsoluteDate of the FieldObject
  1624.      * */
  1625.     public AbsoluteDate toAbsoluteDate() {
  1626.         return new AbsoluteDate(epoch, offset.getReal());
  1627.     }

  1628.     /** Check if the Field is semantically equal to zero.
  1629.      *
  1630.      * <p> Using {@link FieldElement#isZero()}
  1631.      *
  1632.      * @return true the Field is semantically equal to zero
  1633.      * @since 12.0
  1634.      */
  1635.     public boolean hasZeroField() {
  1636.         return (offset instanceof Derivative<?> || offset instanceof Complex) && offset.subtract(offset.getReal()).isZero();
  1637.     }
  1638. }