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  
19  import java.io.Serializable;
20  import java.time.Instant;
21  import java.time.LocalDateTime;
22  import java.time.ZoneOffset;
23  import java.time.format.DateTimeFormatter;
24  import java.util.Date;
25  import java.util.TimeZone;
26  
27  import java.util.concurrent.TimeUnit;
28  import org.hipparchus.util.FastMath;
29  import org.hipparchus.util.MathUtils;
30  import org.hipparchus.util.MathUtils.SumAndResidual;
31  import org.orekit.annotation.DefaultDataContext;
32  import org.orekit.data.DataContext;
33  import org.orekit.errors.OrekitIllegalArgumentException;
34  import org.orekit.utils.Constants;
35  
36  
37  /** This class represents a specific instant in time.
38  
39   * <p>Instances of this class are considered to be absolute in the sense
40   * that each one represent the occurrence of some event and can be compared
41   * to other instances or located in <em>any</em> {@link TimeScale time scale}. In
42   * other words the different locations of an event with respect to two different
43   * time scales (say {@link TAIScale TAI} and {@link UTCScale UTC} for example) are
44   * simply different perspective related to a single object. Only one
45   * <code>AbsoluteDate</code> instance is needed, both representations being available
46   * from this single instance by specifying the time scales as parameter when calling
47   * the ad-hoc methods.</p>
48   *
49   * <p>Since an instance is not bound to a specific time-scale, all methods related
50   * to the location of the date within some time scale require to provide the time
51   * scale as an argument. It is therefore possible to define a date in one time scale
52   * and to use it in another one. An example of such use is to read a date from a file
53   * in UTC and write it in another file in TAI. This can be done as follows:</p>
54   * <pre>
55   *   DateTimeComponents utcComponents = readNextDate();
56   *   AbsoluteDate date = new AbsoluteDate(utcComponents, TimeScalesFactory.getUTC());
57   *   writeNextDate(date.getComponents(TimeScalesFactory.getTAI()));
58   * </pre>
59   *
60   * <p>Two complementary views are available:</p>
61   * <ul>
62   *   <li><p>location view (mainly for input/output or conversions)</p>
63   *   <p>locations represent the coordinate of one event with respect to a
64   *   {@link TimeScale time scale}. The related methods are {@link
65   *   #AbsoluteDate(DateComponents, TimeComponents, TimeScale)}, {@link
66   *   #AbsoluteDate(int, int, int, int, int, double, TimeScale)}, {@link
67   *   #AbsoluteDate(int, int, int, TimeScale)}, {@link #AbsoluteDate(Date,
68   *   TimeScale)}, {@link #parseCCSDSCalendarSegmentedTimeCode(byte, byte[])},
69   *   {@link #toDate(TimeScale)}, {@link #toString(TimeScale) toString(timeScale)},
70   *   {@link #toString()}, 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 #AbsoluteDate(AbsoluteDate, double)},
77   *   {@link #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate)},
78   *   {@link #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents)},
79   *   {@link #durationFrom(AbsoluteDate)}, {@link #compareTo(AbsoluteDate)}, {@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 #JULIAN_EPOCH}, {@link #MODIFIED_JULIAN_EPOCH}, {@link #FIFTIES_EPOCH},
87   * {@link #CCSDS_EPOCH}, {@link #GALILEO_EPOCH}, {@link #GPS_EPOCH}, {@link #QZSS_EPOCH}
88   * {@link #J2000_EPOCH}, {@link #JAVA_EPOCH}.
89   * There are also two factory methods {@link #createJulianEpoch(double)}
90   * and {@link #createBesselianEpoch(double)} that can be used to compute other reference
91   * epochs like J1900.0 or B1950.0.
92   * In addition to these reference epochs, two other constants are defined for convenience:
93   * {@link #PAST_INFINITY} and {@link #FUTURE_INFINITY}, which can be used either as dummy
94   * dates when a date is not yet initialized, or for initialization of loops searching for
95   * a min or max date.
96   * </p>
97   * <p>
98   * Instances of the <code>AbsoluteDate</code> class are guaranteed to be immutable.
99   * </p>
100  * @author Luc Maisonobe
101  * @author Evan Ward
102  * @see TimeScale
103  * @see TimeStamped
104  * @see ChronologicalComparator
105  */
106 public class AbsoluteDate
107     implements TimeStamped, TimeShiftable<AbsoluteDate>, Comparable<AbsoluteDate>, Serializable {
108 
109     /** Reference epoch for julian dates: -4712-01-01T12:00:00 Terrestrial Time.
110      * <p>Both <code>java.util.Date</code> and {@link DateComponents} classes
111      * follow the astronomical conventions and consider a year 0 between
112      * years -1 and +1, hence this reference date lies in year -4712 and not
113      * in year -4713 as can be seen in other documents or programs that obey
114      * a different convention (for example the <code>convcal</code> utility).</p>
115      *
116      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
117      *
118      * @see TimeScales#getJulianEpoch()
119      */
120     @DefaultDataContext
121     public static final AbsoluteDate JULIAN_EPOCH =
122             DataContext.getDefault().getTimeScales().getJulianEpoch();
123 
124     /** Reference epoch for modified julian dates: 1858-11-17T00:00:00 Terrestrial Time.
125      *
126      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
127      *
128      * @see TimeScales#getModifiedJulianEpoch()
129      */
130     @DefaultDataContext
131     public static final AbsoluteDate MODIFIED_JULIAN_EPOCH =
132             DataContext.getDefault().getTimeScales().getModifiedJulianEpoch();
133 
134     /** Reference epoch for 1950 dates: 1950-01-01T00:00:00 Terrestrial Time.
135      *
136      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
137      *
138      * @see TimeScales#getFiftiesEpoch()
139      */
140     @DefaultDataContext
141     public static final AbsoluteDate FIFTIES_EPOCH =
142             DataContext.getDefault().getTimeScales().getFiftiesEpoch();
143 
144     /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4):
145      * 1958-01-01T00:00:00 International Atomic Time (<em>not</em> UTC).
146      *
147      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
148      *
149      * @see TimeScales#getCcsdsEpoch()
150      */
151     @DefaultDataContext
152     public static final AbsoluteDate CCSDS_EPOCH =
153             DataContext.getDefault().getTimeScales().getCcsdsEpoch();
154 
155     /** Reference epoch for Galileo System Time: 1999-08-22T00:00:00 GST.
156      *
157      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
158      *
159      * @see TimeScales#getGalileoEpoch()
160      */
161     @DefaultDataContext
162     public static final AbsoluteDate GALILEO_EPOCH =
163             DataContext.getDefault().getTimeScales().getGalileoEpoch();
164 
165     /** Reference epoch for GPS weeks: 1980-01-06T00:00:00 GPS time.
166      *
167      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
168      *
169      * @see TimeScales#getGpsEpoch()
170      */
171     @DefaultDataContext
172     public static final AbsoluteDate GPS_EPOCH =
173             DataContext.getDefault().getTimeScales().getGpsEpoch();
174 
175     /** Reference epoch for QZSS weeks: 1980-01-06T00:00:00 QZSS time.
176      *
177      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
178      *
179      * @see TimeScales#getQzssEpoch()
180      */
181     @DefaultDataContext
182     public static final AbsoluteDate QZSS_EPOCH =
183             DataContext.getDefault().getTimeScales().getQzssEpoch();
184 
185     /** Reference epoch for IRNSS weeks: 1999-08-22T00:00:00 IRNSS time.
186      *
187      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
188      *
189      * @see TimeScales#getIrnssEpoch()
190      */
191     @DefaultDataContext
192     public static final AbsoluteDate IRNSS_EPOCH =
193             DataContext.getDefault().getTimeScales().getIrnssEpoch();
194 
195     /** Reference epoch for BeiDou weeks: 2006-01-01T00:00:00 UTC.
196      *
197      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
198      *
199      * @see TimeScales#getBeidouEpoch()
200      */
201     @DefaultDataContext
202     public static final AbsoluteDate BEIDOU_EPOCH =
203             DataContext.getDefault().getTimeScales().getBeidouEpoch();
204 
205     /** Reference epoch for GLONASS four-year interval number: 1996-01-01T00:00:00 GLONASS time.
206      * <p>By convention, TGLONASS = UTC + 3 hours.</p>
207      *
208      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
209      *
210      * @see TimeScales#getGlonassEpoch()
211      */
212     @DefaultDataContext
213     public static final AbsoluteDate GLONASS_EPOCH =
214             DataContext.getDefault().getTimeScales().getGlonassEpoch();
215 
216     /** J2000.0 Reference epoch: 2000-01-01T12:00:00 Terrestrial Time (<em>not</em> UTC).
217      *
218      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
219      *
220      * @see #createJulianEpoch(double)
221      * @see #createBesselianEpoch(double)
222      * @see TimeScales#getJ2000Epoch()
223      */
224     @DefaultDataContext
225     public static final AbsoluteDate J2000_EPOCH = // TODO
226             DataContext.getDefault().getTimeScales().getJ2000Epoch();
227 
228     /** Java Reference epoch: 1970-01-01T00:00:00 Universal Time Coordinate.
229      * <p>
230      * Between 1968-02-01 and 1972-01-01, UTC-TAI = 4.213 170 0s + (MJD - 39 126) x 0.002 592s.
231      * As on 1970-01-01 MJD = 40587, UTC-TAI = 8.000082s
232      * </p>
233      *
234      * <p>This constant uses the {@link DataContext#getDefault() default data context}.
235      *
236      * @see TimeScales#getJavaEpoch()
237      */
238     @DefaultDataContext
239     public static final AbsoluteDate JAVA_EPOCH =
240             DataContext.getDefault().getTimeScales().getJavaEpoch();
241 
242     /**
243      * An arbitrary finite date. Uses when a non-null date is needed but its value doesn't
244      * matter.
245      */
246     public static final AbsoluteDate ARBITRARY_EPOCH = new AbsoluteDate(0, 0);
247 
248     /** Dummy date at infinity in the past direction.
249      * @see TimeScales#getPastInfinity()
250      */
251     public static final AbsoluteDate PAST_INFINITY = ARBITRARY_EPOCH.shiftedBy(Double.NEGATIVE_INFINITY);
252 
253     /** Dummy date at infinity in the future direction.
254      * @see TimeScales#getFutureInfinity()
255      */
256     public static final AbsoluteDate FUTURE_INFINITY = ARBITRARY_EPOCH.shiftedBy(Double.POSITIVE_INFINITY);
257 
258     /** Serializable UID. */
259     private static final long serialVersionUID = 617061803741806846L;
260 
261     /** Reference epoch in seconds from 2000-01-01T12:00:00 TAI.
262      * <p>Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT.</p> */
263     private final long epoch;
264 
265     /** Offset from the reference epoch in seconds. */
266     private final double offset;
267 
268     /** Create an instance with a default value ({@link #J2000_EPOCH}).
269      *
270      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
271      *
272      * @see #AbsoluteDate(DateTimeComponents, TimeScale)
273      */
274     @DefaultDataContext
275     public AbsoluteDate() {
276         epoch  = J2000_EPOCH.epoch;
277         offset = J2000_EPOCH.offset;
278     }
279 
280     /** Build an instance from a location (parsed from a string) in a {@link TimeScale time scale}.
281      * <p>
282      * The supported formats for location are mainly the ones defined in ISO-8601 standard,
283      * the exact subset is explained in {@link DateTimeComponents#parseDateTime(String)},
284      * {@link DateComponents#parseDate(String)} and {@link TimeComponents#parseTime(String)}.
285      * </p>
286      * <p>
287      * As CCSDS ASCII calendar segmented time code is a trimmed down version of ISO-8601,
288      * it is also supported by this constructor.
289      * </p>
290      * @param location location in the time scale, must be in a supported format
291      * @param timeScale time scale
292      * @exception IllegalArgumentException if location string is not in a supported format
293      */
294     public AbsoluteDate(final String location, final TimeScale timeScale) {
295         this(DateTimeComponents.parseDateTime(location), timeScale);
296     }
297 
298     /** Build an instance from a location in a {@link TimeScale time scale}.
299      * @param location location in the time scale
300      * @param timeScale time scale
301      */
302     public AbsoluteDate(final DateTimeComponents location, final TimeScale timeScale) {
303         this(location.getDate(), location.getTime(), timeScale);
304     }
305 
306     /** Build an instance from a location in a {@link TimeScale time scale}.
307      * @param date date location in the time scale
308      * @param time time location in the time scale
309      * @param timeScale time scale
310      */
311     public AbsoluteDate(final DateComponents date, final TimeComponents time,
312                         final TimeScale timeScale) {
313 
314         final double seconds  = time.getSecond();
315         final double tsOffset = timeScale.offsetToTAI(date, time);
316 
317         // Use 2Sum for high precision.
318         final SumAndResidual sumAndResidual = MathUtils.twoSum(seconds, tsOffset);
319         final long dl = (long) FastMath.floor(sumAndResidual.getSum());
320         final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
321 
322         if (regularOffset >= 0) {
323             // regular case, the offset is between 0.0 and 1.0
324             offset = regularOffset;
325             epoch  = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L +
326                             time.getMinute() - time.getMinutesFromUTC() - 720L) + dl;
327         } else {
328             // very rare case, the offset is just before a whole second
329             // we will loose some bits of accuracy when adding 1 second
330             // but this will ensure the offset remains in the [0.0; 1.0] interval
331             offset = 1.0 + regularOffset;
332             epoch  = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L +
333                             time.getMinute() - time.getMinutesFromUTC() - 720L) + dl - 1;
334         }
335 
336     }
337 
338     /** Build an instance from a location in a {@link TimeScale time scale}.
339      * @param year year number (may be 0 or negative for BC years)
340      * @param month month number from 1 to 12
341      * @param day day number from 1 to 31
342      * @param hour hour number from 0 to 23
343      * @param minute minute number from 0 to 59
344      * @param second second number from 0.0 to 60.0 (excluded)
345      * @param timeScale time scale
346      * @exception IllegalArgumentException if inconsistent arguments
347      * are given (parameters out of range)
348      */
349     public AbsoluteDate(final int year, final int month, final int day,
350                         final int hour, final int minute, final double second,
351                         final TimeScale timeScale) throws IllegalArgumentException {
352         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
353     }
354 
355     /** Build an instance from a location in a {@link TimeScale time scale}.
356      * @param year year number (may be 0 or negative for BC years)
357      * @param month month enumerate
358      * @param day day number from 1 to 31
359      * @param hour hour number from 0 to 23
360      * @param minute minute number from 0 to 59
361      * @param second second number from 0.0 to 60.0 (excluded)
362      * @param timeScale time scale
363      * @exception IllegalArgumentException if inconsistent arguments
364      * are given (parameters out of range)
365      */
366     public AbsoluteDate(final int year, final Month month, final int day,
367                         final int hour, final int minute, final double second,
368                         final TimeScale timeScale) throws IllegalArgumentException {
369         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
370     }
371 
372     /** Build an instance from a location in a {@link TimeScale time scale}.
373      * <p>The hour is set to 00:00:00.000.</p>
374      * @param date date location in the time scale
375      * @param timeScale time scale
376      * @exception IllegalArgumentException if inconsistent arguments
377      * are given (parameters out of range)
378      */
379     public AbsoluteDate(final DateComponents date, final TimeScale timeScale)
380         throws IllegalArgumentException {
381         this(date, TimeComponents.H00, timeScale);
382     }
383 
384     /** Build an instance from a location in a {@link TimeScale time scale}.
385      * <p>The hour is set to 00:00:00.000.</p>
386      * @param year year number (may be 0 or negative for BC years)
387      * @param month month number from 1 to 12
388      * @param day day number from 1 to 31
389      * @param timeScale time scale
390      * @exception IllegalArgumentException if inconsistent arguments
391      * are given (parameters out of range)
392      */
393     public AbsoluteDate(final int year, final int month, final int day,
394                         final TimeScale timeScale) throws IllegalArgumentException {
395         this(new DateComponents(year, month, day), TimeComponents.H00, timeScale);
396     }
397 
398     /** Build an instance from a location in a {@link TimeScale time scale}.
399      * <p>The hour is set to 00:00:00.000.</p>
400      * @param year year number (may be 0 or negative for BC years)
401      * @param month month enumerate
402      * @param day day number from 1 to 31
403      * @param timeScale time scale
404      * @exception IllegalArgumentException if inconsistent arguments
405      * are given (parameters out of range)
406      */
407     public AbsoluteDate(final int year, final Month month, final int day,
408                         final TimeScale timeScale) throws IllegalArgumentException {
409         this(new DateComponents(year, month, day), TimeComponents.H00, timeScale);
410     }
411 
412     /** Build an instance from a location in a {@link TimeScale time scale}.
413      * @param location location in the time scale
414      * @param timeScale time scale
415      */
416     public AbsoluteDate(final Date location, final TimeScale timeScale) {
417         this(new DateComponents(DateComponents.JAVA_EPOCH,
418                                 (int) (location.getTime() / 86400000L)),
419                                  millisToTimeComponents((int) (location.getTime() % 86400000L)),
420              timeScale);
421     }
422 
423     /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}.
424      *
425      * @deprecated Use {@link AbsoluteDate#AbsoluteDate(Instant, UTCScale)} or {@link AbsoluteDate#AbsoluteDate(Instant)} instead
426      * @param instant instant in the time scale
427      * @param timeScale time scale
428      * @since 12.0
429      */
430     @Deprecated
431     public AbsoluteDate(final Instant instant, final TimeScale timeScale) {
432         this(new DateComponents(DateComponents.JAVA_EPOCH,
433                                 (int) (instant.getEpochSecond() / 86400L)),
434              instantToTimeComponents(instant),
435              timeScale);
436     }
437 
438     /** Build an instance from an {@link Instant instant} in utc time scale.
439      * @param instant instant in the time scale
440      * @since 12.1
441      */
442     @DefaultDataContext
443     public AbsoluteDate(final Instant instant) {
444         this(instant, TimeScalesFactory.getUTC());
445     }
446 
447     /** Build an instance from an {@link Instant instant} in the {@link UTCScale time scale}.
448      * @param instant instant in the time scale
449      * @param utcScale utc time scale
450      * @since 12.1
451      */
452     public AbsoluteDate(final Instant instant, final UTCScale utcScale) {
453         this(new DateComponents(DateComponents.JAVA_EPOCH,
454                 (int) (instant.getEpochSecond() / 86400l)),
455             instantToTimeComponents(instant),
456             utcScale);
457     }
458 
459     /** Build an instance from an elapsed duration since to another instant.
460      * <p>It is important to note that the elapsed duration is <em>not</em>
461      * the difference between two readings on a time scale. As an example,
462      * the duration between the two instants leading to the readings
463      * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC}
464      * time scale is <em>not</em> 1 second, but a stop watch would have measured
465      * an elapsed duration of 2 seconds between these two instances because a leap
466      * second was introduced at the end of 2005 in this time scale.</p>
467      * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate)}
468      * method.</p>
469      * @param since start instant of the measured duration
470      * @param elapsedDuration physically elapsed duration from the <code>since</code>
471      * instant, as measured in a regular time scale
472      * @see #durationFrom(AbsoluteDate)
473      */
474     public AbsoluteDate(final AbsoluteDate since, final double elapsedDuration) {
475         // Use 2Sum for high precision.
476         final SumAndResidual sumAndResidual = MathUtils.twoSum(since.offset, elapsedDuration);
477         if (Double.isInfinite(sumAndResidual.getSum())) {
478             offset = sumAndResidual.getSum();
479             epoch  = (sumAndResidual.getSum() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
480         } else {
481             final long dl = (long) FastMath.floor(sumAndResidual.getSum());
482             final double regularOffset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
483             if (regularOffset >= 0) {
484                 // regular case, the offset is between 0.0 and 1.0
485                 offset = regularOffset;
486                 epoch  = since.epoch + dl;
487             } else {
488                 // very rare case, the offset is just before a whole second
489                 // we will loose some bits of accuracy when adding 1 second
490                 // but this will ensure the offset remains in the [0.0; 1.0] interval
491                 offset = 1.0 + regularOffset;
492                 epoch  = since.epoch + dl - 1;
493             }
494         }
495     }
496 
497     /** Build an instance from an elapsed duration since to another instant.
498      * <p>It is important to note that the elapsed duration is <em>not</em>
499      * the difference between two readings on a time scale. As an example,
500      * the duration between the two instants leading to the readings
501      * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC}
502      * time scale is <em>not</em> 1 second, but a stop watch would have measured
503      * an elapsed duration of 2 seconds between these two instances because a leap
504      * second was introduced at the end of 2005 in this time scale.</p>
505      * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate, TimeUnit)}
506      * method.</p>
507      * @param since start instant of the measured duration
508      * @param elapsedDuration physically elapsed duration from the <code>since</code>
509      * instant, as measured in a regular time scale
510      * @param timeUnit {@link TimeUnit} of the elapsedDuration
511      * @see #durationFrom(AbsoluteDate, TimeUnit)
512      * @since 12.1
513      */
514     public AbsoluteDate(final AbsoluteDate since, final long elapsedDuration, final TimeUnit timeUnit) {
515         final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(elapsedDuration, timeUnit);
516         final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1);
517         final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1);
518         final double newOffset = since.offset + deltaOffset;
519         if (newOffset >= 1.0) {
520             // newOffset is in [1.0, 2.0]
521             epoch = since.epoch + deltaEpoch + 1L;
522             offset = newOffset - 1.0;
523         } else if (newOffset < 0) {
524             epoch = since.epoch + deltaEpoch - 1L;
525             offset = 1.0 + newOffset;
526         } else {
527             epoch = since.epoch + deltaEpoch;
528             offset = newOffset;
529         }
530     }
531 
532     /** Build an instance from an apparent clock offset with respect to another
533      * instant <em>in the perspective of a specific {@link TimeScale time scale}</em>.
534      * <p>It is important to note that the apparent clock offset <em>is</em> the
535      * difference between two readings on a time scale and <em>not</em> an elapsed
536      * duration. As an example, the apparent clock offset between the two instants
537      * leading to the readings 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the
538      * {@link UTCScale UTC} time scale is 1 second, but the elapsed duration is 2
539      * seconds because a leap second has been introduced at the end of 2005 in this
540      * time scale.</p>
541      * <p>This constructor is the reverse of the {@link #offsetFrom(AbsoluteDate,
542      * TimeScale)} method.</p>
543      * @param reference reference instant
544      * @param apparentOffset apparent clock offset from the reference instant
545      * (difference between two readings in the specified time scale)
546      * @param timeScale time scale with respect to which the offset is defined
547      * @see #offsetFrom(AbsoluteDate, TimeScale)
548      */
549     public AbsoluteDate(final AbsoluteDate reference, final double apparentOffset,
550                         final TimeScale timeScale) {
551         this(new DateTimeComponents(reference.getComponents(timeScale), apparentOffset),
552              timeScale);
553     }
554 
555     /** Build a date from its internal components.
556      * <p>
557      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
558      * </p>
559      * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI.
560      * (beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT)
561      * @param offset offset from the reference epoch in seconds (must be
562      * between 0.0 included and 1.0 excluded)
563      * @since 9.0
564      */
565     AbsoluteDate(final long epoch, final double offset) {
566         this.epoch  = epoch;
567         this.offset = offset;
568     }
569 
570     /** Extract time components from a number of milliseconds within the day.
571      * @param millisInDay number of milliseconds within the day
572      * @return time components
573      */
574     private static TimeComponents millisToTimeComponents(final int millisInDay) {
575         return new TimeComponents(millisInDay / 1000, 0.001 * (millisInDay % 1000));
576     }
577 
578     /** Extract time components from an instant within the day.
579      * @param instant instant to extract the number of seconds within the day
580      * @return time components
581      */
582     private static TimeComponents instantToTimeComponents(final Instant instant) {
583         final int secInDay = (int) (instant.getEpochSecond() % 86400L);
584         return new TimeComponents(secInDay, 1.0e-9 * instant.getNano());
585     }
586 
587     /** Get the reference epoch in seconds from 2000-01-01T12:00:00 TAI.
588      * <p>
589      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
590      * </p>
591      * <p>
592      * Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT.
593      * </p>
594      * @return reference epoch in seconds from 2000-01-01T12:00:00 TAI
595      * @since 9.0
596      */
597     long getEpoch() {
598         return epoch;
599     }
600 
601     /** Get the offset from the reference epoch in seconds.
602      * <p>
603      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
604      * </p>
605      * @return offset from the reference epoch in seconds
606      * @since 9.0
607      */
608     double getOffset() {
609         return offset;
610     }
611 
612     /** Build an instance from a CCSDS Unsegmented Time Code (CUC).
613      * <p>
614      * CCSDS Unsegmented Time Code is defined in the blue book:
615      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
616      * </p>
617      * <p>
618      * If the date to be parsed is formatted using version 3 of the standard
619      * (CCSDS 301.0-B-3 published in 2002) or if the extension of the preamble
620      * field introduced in version 4 of the standard is not used, then the
621      * {@code preambleField2} parameter can be set to 0.
622      * </p>
623      *
624      * <p>This method uses the {@link DataContext#getDefault() default data context} if
625      * the CCSDS epoch is used.
626      *
627      * @param preambleField1 first byte of the field specifying the format, often
628      * not transmitted in data interfaces, as it is constant for a given data interface
629      * @param preambleField2 second byte of the field specifying the format
630      * (added in revision 4 of the CCSDS standard in 2010), often not transmitted in data
631      * interfaces, as it is constant for a given data interface (value ignored if presence
632      * not signaled in {@code preambleField1})
633      * @param timeField byte array containing the time code
634      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
635      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
636      * may be null in this case)
637      * @return an instance corresponding to the specified date
638      * @see #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate, AbsoluteDate)
639      */
640     @DefaultDataContext
641     public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(final byte preambleField1,
642                                                              final byte preambleField2,
643                                                              final byte[] timeField,
644                                                              final AbsoluteDate agencyDefinedEpoch) {
645         return parseCCSDSUnsegmentedTimeCode(preambleField1, preambleField2, timeField,
646                 agencyDefinedEpoch,
647                 DataContext.getDefault().getTimeScales().getCcsdsEpoch());
648     }
649 
650     /**
651      * Build an instance from a CCSDS Unsegmented Time Code (CUC).
652      * <p>
653      * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format
654      * (CCSDS 301.0-B-4) published in November 2010
655      * </p>
656      * <p>
657      * If the date to be parsed is formatted using version 3 of the standard (CCSDS
658      * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced
659      * in version 4 of the standard is not used, then the {@code preambleField2} parameter
660      * can be set to 0.
661      * </p>
662      *
663      * @param preambleField1     first byte of the field specifying the format, often not
664      *                           transmitted in data interfaces, as it is constant for a
665      *                           given data interface
666      * @param preambleField2     second byte of the field specifying the format (added in
667      *                           revision 4 of the CCSDS standard in 2010), often not
668      *                           transmitted in data interfaces, as it is constant for a
669      *                           given data interface (value ignored if presence not
670      *                           signaled in {@code preambleField1})
671      * @param timeField          byte array containing the time code
672      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies
673      *                           the {@link #CCSDS_EPOCH CCSDS reference epoch} is used
674      *                           (and hence may be null in this case)
675      * @param ccsdsEpoch         reference epoch, ignored if the preamble field specifies
676      *                           the agency epoch is used.
677      * @return an instance corresponding to the specified date
678      * @since 10.1
679      */
680     public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(
681             final byte preambleField1,
682             final byte preambleField2,
683             final byte[] timeField,
684             final AbsoluteDate agencyDefinedEpoch,
685             final AbsoluteDate ccsdsEpoch) {
686         final CcsdsUnsegmentedTimeCode<AbsoluteDate> timeCode =
687             new CcsdsUnsegmentedTimeCode<>(preambleField1, preambleField2, timeField,
688                                            agencyDefinedEpoch, ccsdsEpoch);
689         return new AbsoluteDate(timeCode.getEpoch(), timeCode.getSeconds()).
690                shiftedBy(timeCode.getSubSecond());
691 
692     }
693 
694     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
695      * <p>
696      * CCSDS Day Segmented Time Code is defined in the blue book:
697      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
698      * </p>
699      *
700      * <p>This method uses the {@link DataContext#getDefault() default data context}.
701      *
702      * @param preambleField field specifying the format, often not transmitted in
703      * data interfaces, as it is constant for a given data interface
704      * @param timeField byte array containing the time code
705      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
706      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
707      * may be null in this case)
708      * @return an instance corresponding to the specified date
709      * @see #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents, TimeScale)
710      */
711     @DefaultDataContext
712     public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, final byte[] timeField,
713                                                               final DateComponents agencyDefinedEpoch) {
714         return parseCCSDSDaySegmentedTimeCode(preambleField, timeField,
715                 agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC());
716     }
717 
718     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
719      * <p>
720      * CCSDS Day Segmented Time Code is defined in the blue book:
721      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
722      * </p>
723      * @param preambleField field specifying the format, often not transmitted in
724      * data interfaces, as it is constant for a given data interface
725      * @param timeField byte array containing the time code
726      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
727      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
728      * may be null in this case)
729      * @param utc      time scale used to compute date and time components.
730      * @return an instance corresponding to the specified date
731      * @since 10.1
732      */
733     public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField,
734                                                               final byte[] timeField,
735                                                               final DateComponents agencyDefinedEpoch,
736                                                               final TimeScale utc) {
737 
738         final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField,
739                                                                            agencyDefinedEpoch);
740         return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc).
741                shiftedBy(timeCode.getSubSecond());
742     }
743 
744     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
745      * <p>
746      * CCSDS Calendar Segmented Time Code is defined in the blue book:
747      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
748      * </p>
749      *
750      * <p>This method uses the {@link DataContext#getDefault() default data context}.
751      *
752      * @param preambleField field specifying the format, often not transmitted in
753      * data interfaces, as it is constant for a given data interface
754      * @param timeField byte array containing the time code
755      * @return an instance corresponding to the specified date
756      * @see #parseCCSDSCalendarSegmentedTimeCode(byte, byte[], TimeScale)
757      */
758     @DefaultDataContext
759     public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField) {
760         return parseCCSDSCalendarSegmentedTimeCode(preambleField, timeField,
761                 DataContext.getDefault().getTimeScales().getUTC());
762     }
763 
764     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
765      * <p>
766      * CCSDS Calendar Segmented Time Code is defined in the blue book:
767      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
768      * </p>
769      * @param preambleField field specifying the format, often not transmitted in
770      * data interfaces, as it is constant for a given data interface
771      * @param timeField byte array containing the time code
772      * @param utc      time scale used to compute date and time components.
773      * @return an instance corresponding to the specified date
774      * @since 10.1
775      */
776     public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField,
777                                                                    final byte[] timeField,
778                                                                    final TimeScale utc) {
779         final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField);
780         return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc).
781                shiftedBy(timeCode.getSubSecond());
782     }
783 
784     /** Build an instance corresponding to a Julian Day date.
785      * @param jd Julian day
786      * @param secondsSinceNoon seconds in the Julian day
787      * (BEWARE, Julian days start at noon, so 0.0 is noon)
788      * @param timeScale time scale in which the seconds in day are defined
789      * @return a new instant
790      */
791     public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon,
792                                             final TimeScale timeScale) {
793         return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jd),
794                 TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon);
795     }
796 
797     /** Build an instance corresponding to a Julian Day date.
798      * <p>
799      * This function should be preferred to {@link #createMJDDate(int, double, TimeScale)} when the target time scale
800      * has a non-constant offset with respect to TAI.
801      * <p>
802      * The idea is to introduce a pivot time scale that is close to the target time scale but has a constant bias with TAI.
803      * <p>
804      * For example, to get a date from an MJD in TDB time scale, it's advised to use the TT time scale
805      * as a pivot scale. TT is very close to TDB and has constant offset to TAI.
806      * @param jd Julian day
807      * @param secondsSinceNoon seconds in the Julian day
808      * (BEWARE, Julian days start at noon, so 0.0 is noon)
809      * @param timeScale timescale in which the seconds in day are defined
810      * @param pivotTimeScale pivot timescale used as intermediate timescale
811      * @return a new instant
812      */
813     public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon,
814                                             final TimeScale timeScale,
815                                             final TimeScale pivotTimeScale) {
816         // Get the date in pivot timescale
817         final AbsoluteDate dateInPivotTimeScale = createJDDate(jd, secondsSinceNoon, pivotTimeScale);
818 
819         // Compare offsets to TAI of the two time scales
820         final double offsetFromTAI = timeScale.offsetFromTAI(dateInPivotTimeScale) -  pivotTimeScale.offsetFromTAI(dateInPivotTimeScale);
821 
822         // Return date in desired timescale
823         return dateInPivotTimeScale.shiftedBy(-offsetFromTAI);
824     }
825 
826     /** Build an instance corresponding to a Modified Julian Day date.
827      * @param mjd modified Julian day
828      * @param secondsInDay seconds in the day
829      * @param timeScale time scale in which the seconds in day are defined
830      * @return a new instant
831      * @exception OrekitIllegalArgumentException if seconds number is out of range
832      */
833     public static AbsoluteDate createMJDDate(final int mjd, final double secondsInDay,
834                                              final TimeScale timeScale)
835         throws OrekitIllegalArgumentException {
836         final DateComponents dc = new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd);
837         final TimeComponents tc;
838         if (secondsInDay >= Constants.JULIAN_DAY) {
839             // check we are really allowed to use this number of seconds
840             final int    secondsA = 86399; // 23:59:59, i.e. 59s in the last minute of the day
841             final double secondsB = secondsInDay - secondsA;
842             final TimeComponents safeTC = new TimeComponents(secondsA, 0.0);
843             final AbsoluteDate safeDate = new AbsoluteDate(dc, safeTC, timeScale);
844             if (timeScale.minuteDuration(safeDate) > 59 + secondsB) {
845                 // we are within the last minute of the day, the number of seconds is OK
846                 return safeDate.shiftedBy(secondsB);
847             } else {
848                 // let TimeComponents trigger an OrekitIllegalArgumentException
849                 // for the wrong number of seconds
850                 tc = new TimeComponents(secondsA, secondsB);
851             }
852         } else {
853             tc = new TimeComponents(secondsInDay);
854         }
855 
856         // create the date
857         return new AbsoluteDate(dc, tc, timeScale);
858 
859     }
860 
861     /** Build an instance corresponding to a Julian Epoch (JE).
862      * <p>According to Lieske paper: <a
863      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
864      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
865      * vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is related to Julian Ephemeris Date as:</p>
866      * <pre>
867      * JE = 2000.0 + (JED - 2451545.0) / 365.25
868      * </pre>
869      * <p>
870      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Julian Epoch.
871      * </p>
872      *
873      * <p>This method uses the {@link DataContext#getDefault() default data context}.
874      *
875      * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference J2000.0
876      * @return a new instant
877      * @see #J2000_EPOCH
878      * @see #createBesselianEpoch(double)
879      * @see TimeScales#createJulianEpoch(double)
880      */
881     @DefaultDataContext
882     public static AbsoluteDate createJulianEpoch(final double julianEpoch) {
883         return DataContext.getDefault().getTimeScales().createJulianEpoch(julianEpoch);
884     }
885 
886     /** Build an instance corresponding to a Besselian Epoch (BE).
887      * <p>According to Lieske paper: <a
888      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
889      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
890      * vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch is related to Julian Ephemeris Date as:</p>
891      * <pre>
892      * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781
893      * </pre>
894      * <p>
895      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Besselian Epoch.
896      * </p>
897      *
898      * <p>This method uses the {@link DataContext#getDefault() default data context}.
899      *
900      * @param besselianEpoch Besselian epoch, like 1950 for defining the classical reference B1950.0
901      * @return a new instant
902      * @see #createJulianEpoch(double)
903      * @see TimeScales#createBesselianEpoch(double)
904      */
905     @DefaultDataContext
906     public static AbsoluteDate createBesselianEpoch(final double besselianEpoch) {
907         return DataContext.getDefault().getTimeScales()
908                 .createBesselianEpoch(besselianEpoch);
909     }
910 
911     /** Get a time-shifted date.
912      * <p>
913      * Calling this method is equivalent to call <code>new AbsoluteDate(this, dt)</code>.
914      * </p>
915      * @param dt time shift in seconds
916      * @return a new date, shifted with respect to instance (which is immutable)
917      * @see org.orekit.utils.PVCoordinates#shiftedBy(double)
918      * @see org.orekit.attitudes.Attitude#shiftedBy(double)
919      * @see org.orekit.orbits.Orbit#shiftedBy(double)
920      * @see org.orekit.propagation.SpacecraftState#shiftedBy(double)
921      */
922     public AbsoluteDate shiftedBy(final double dt) {
923         return new AbsoluteDate(this, dt);
924     }
925 
926     /** Get a time-shifted date.
927      * <p>
928      * Calling this method is equivalent to call <code>new AbsoluteDate(this, shift, timeUnit)</code>.
929      * </p>
930      * @param dt time shift in time units
931      * @param timeUnit {@link TimeUnit} of the shift
932      * @return a new date, shifted with respect to instance (which is immutable)
933      * @since 12.1
934      */
935     public AbsoluteDate shiftedBy(final long dt, final TimeUnit timeUnit) {
936         return new AbsoluteDate(this, dt, timeUnit);
937     }
938 
939     /** Compute the physically elapsed duration between two instants.
940      * <p>The returned duration is the number of seconds physically
941      * elapsed between the two instants, measured in a regular time
942      * scale with respect to surface of the Earth (i.e either the {@link
943      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
944      * GPSScale GPS scale}). It is the only method that gives a
945      * duration with a physical meaning.</p>
946      * <p>This method gives the same result (with less computation)
947      * as calling {@link #offsetFrom(AbsoluteDate, TimeScale)}
948      * with a second argument set to one of the regular scales cited
949      * above.</p>
950      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
951      * double)} constructor.</p>
952      * @param instant instant to subtract from the instance
953      * @return offset in seconds between the two instants (positive
954      * if the instance is posterior to the argument)
955      * @see #offsetFrom(AbsoluteDate, TimeScale)
956      * @see #AbsoluteDate(AbsoluteDate, double)
957      */
958     public double durationFrom(final AbsoluteDate instant) {
959         return (epoch - instant.epoch) + (offset - instant.offset);
960     }
961 
962     /** Compute the physically elapsed duration between two instants.
963      * <p>The returned duration is the duration physically
964      * elapsed between the two instants, using the given time unit and rounded to the nearest integer, measured in a regular time
965      * scale with respect to surface of the Earth (i.e either the {@link
966      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
967      * GPSScale GPS scale}). It is the only method that gives a
968      * duration with a physical meaning.</p>
969      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
970      * long, TimeUnit)} constructor.</p>
971      * @param instant instant to subtract from the instance
972      * @param timeUnit {@link TimeUnit} precision for the offset
973      * @return offset in the given timeunit between the two instants (positive
974      * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit}
975      * @see #AbsoluteDate(AbsoluteDate, long, TimeUnit)
976      * @since 12.1
977      */
978     public long durationFrom(final AbsoluteDate instant, final TimeUnit timeUnit) {
979         final long deltaEpoch = timeUnit.convert(epoch - instant.epoch, TimeUnit.SECONDS);
980 
981         final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS);
982         final long deltaOffset = FastMath.round((offset - instant.offset) * multiplier);
983 
984         return deltaEpoch + deltaOffset;
985     }
986 
987     /** Compute the apparent clock offset between two instant <em>in the
988      * perspective of a specific {@link TimeScale time scale}</em>.
989      * <p>The offset is the number of seconds counted in the given
990      * time scale between the locations of the two instants, with
991      * all time scale irregularities removed (i.e. considering all
992      * days are exactly 86400 seconds long). This method will give
993      * a result that may not have a physical meaning if the time scale
994      * is irregular. For example since a leap second was introduced at
995      * the end of 2005, the apparent offset between 2005-12-31T23:59:59
996      * and 2006-01-01T00:00:00 is 1 second, but the physical duration
997      * of the corresponding time interval as returned by the {@link
998      * #durationFrom(AbsoluteDate)} method is 2 seconds.</p>
999      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
1000      * double, TimeScale)} constructor.</p>
1001      * @param instant instant to subtract from the instance
1002      * @param timeScale time scale with respect to which the offset should
1003      * be computed
1004      * @return apparent clock offset in seconds between the two instants
1005      * (positive if the instance is posterior to the argument)
1006      * @see #durationFrom(AbsoluteDate)
1007      * @see #AbsoluteDate(AbsoluteDate, double, TimeScale)
1008      */
1009     public double offsetFrom(final AbsoluteDate instant, final TimeScale timeScale) {
1010         final long   elapsedDurationA = epoch - instant.epoch;
1011         final double elapsedDurationB = (offset         + timeScale.offsetFromTAI(this)) -
1012                                         (instant.offset + timeScale.offsetFromTAI(instant));
1013         return  elapsedDurationA + elapsedDurationB;
1014     }
1015 
1016     /** Compute the offset between two time scales at the current instant.
1017      * <p>The offset is defined as <i>l₁-l₂</i>
1018      * where <i>l₁</i> is the location of the instant in
1019      * the <code>scale1</code> time scale and <i>l₂</i> is the
1020      * location of the instant in the <code>scale2</code> time scale.</p>
1021      * @param scale1 first time scale
1022      * @param scale2 second time scale
1023      * @return offset in seconds between the two time scales at the
1024      * current instant
1025      */
1026     public double timeScalesOffset(final TimeScale scale1, final TimeScale scale2) {
1027         return scale1.offsetFromTAI(this) - scale2.offsetFromTAI(this);
1028     }
1029 
1030     /** Convert the instance to a Java {@link java.util.Date Date}.
1031      * <p>Conversion to the Date class induces a loss of precision because
1032      * the Date class does not provide sub-millisecond information. Java Dates
1033      * are considered to be locations in some times scales.</p>
1034      * @param timeScale time scale to use
1035      * @return a {@link java.util.Date Date} instance representing the location
1036      * of the instant in the time scale
1037      */
1038     public Date toDate(final TimeScale timeScale) {
1039         final double time = epoch + (offset + timeScale.offsetFromTAI(this));
1040         return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000));
1041     }
1042 
1043     /**
1044      * Convert the instance to a Java {@link java.time.Instant Instant}.
1045      * Nanosecond precision is preserved during this conversion
1046      *
1047      * @return a {@link java.time.Instant Instant} instance representing the location
1048      * of the instant in the utc time scale
1049      * @since 12.1
1050      */
1051     @DefaultDataContext
1052     public Instant toInstant() {
1053         return toInstant(TimeScalesFactory.getTimeScales());
1054     }
1055 
1056     /**
1057      * Convert the instance to a Java {@link java.time.Instant Instant}.
1058      * Nanosecond precision is preserved during this conversion
1059      *
1060      * @param timeScales the timescales to use
1061      * @return a {@link java.time.Instant Instant} instance representing the location
1062      * of the instant in the utc time scale
1063      * @since 12.1
1064      */
1065     public Instant toInstant(final TimeScales timeScales) {
1066         final UTCScale utc = timeScales.getUTC();
1067         final String stringWithoutUtcOffset = toStringWithoutUtcOffset(utc, 9);
1068 
1069         final LocalDateTime localDateTime = LocalDateTime.parse(stringWithoutUtcOffset, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
1070         return localDateTime.toInstant(ZoneOffset.UTC);
1071     }
1072 
1073     /** Split the instance into date/time components.
1074      * @param timeScale time scale to use
1075      * @return date/time components
1076      */
1077     public DateTimeComponents getComponents(final TimeScale timeScale) {
1078 
1079         if (Double.isInfinite(offset)) {
1080             // special handling for past and future infinity
1081             if (offset < 0) {
1082                 return new DateTimeComponents(DateComponents.MIN_EPOCH, TimeComponents.H00);
1083             } else {
1084                 return new DateTimeComponents(DateComponents.MAX_EPOCH,
1085                                               new TimeComponents(23, 59, 59.999));
1086             }
1087         }
1088 
1089         // Compute offset from 2000-01-01T00:00:00 in specified time scale.
1090         // Use 2Sum for high precision.
1091         final double taiOffset = timeScale.offsetFromTAI(this);
1092         final SumAndResidual sumAndResidual = MathUtils.twoSum(offset, taiOffset);
1093 
1094         // split date and time
1095         final long   carry = (long) FastMath.floor(sumAndResidual.getSum());
1096         double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual();
1097         long   offset2000A = epoch + carry + 43200L;
1098         if (offset2000B < 0) {
1099             offset2000A -= 1;
1100             offset2000B += 1;
1101         }
1102         long time = offset2000A % 86400L;
1103         if (time < 0L) {
1104             time += 86400L;
1105         }
1106         final int date = (int) ((offset2000A - time) / 86400L);
1107 
1108         // extract calendar elements
1109         final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date);
1110 
1111         // extract time element, accounting for leap seconds
1112         final double leap = timeScale.insideLeap(this) ? timeScale.getLeap(this) : 0;
1113         final int minuteDuration = timeScale.minuteDuration(this);
1114         final TimeComponents timeComponents =
1115                 TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration);
1116 
1117         // build the components
1118         return new DateTimeComponents(dateComponents, timeComponents);
1119 
1120     }
1121 
1122     /** Split the instance into date/time components for a local time.
1123      *
1124      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1125      *
1126      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1127      * negative Westward UTC)
1128      * @return date/time components
1129      * @since 7.2
1130      * @see #getComponents(int, TimeScale)
1131      */
1132     @DefaultDataContext
1133     public DateTimeComponents getComponents(final int minutesFromUTC) {
1134         return getComponents(minutesFromUTC,
1135                 DataContext.getDefault().getTimeScales().getUTC());
1136     }
1137 
1138     /**
1139      * Split the instance into date/time components for a local time.
1140      *
1141      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1142      *                       negative Westward UTC)
1143      * @param utc            time scale used to compute date and time components.
1144      * @return date/time components
1145      * @since 10.1
1146      */
1147     public DateTimeComponents getComponents(final int minutesFromUTC,
1148                                             final TimeScale utc) {
1149 
1150         final DateTimeComponents utcComponents = getComponents(utc);
1151 
1152         // shift the date according to UTC offset, but WITHOUT touching the seconds,
1153         // as they may exceed 60.0 during a leap seconds introduction,
1154         // and we want to preserve these special cases
1155         final double seconds = utcComponents.getTime().getSecond();
1156 
1157         int minute = utcComponents.getTime().getMinute() + minutesFromUTC;
1158         final int hourShift;
1159         if (minute < 0) {
1160             hourShift = (minute - 59) / 60;
1161         } else if (minute > 59) {
1162             hourShift = minute / 60;
1163         } else {
1164             hourShift = 0;
1165         }
1166         minute -= 60 * hourShift;
1167 
1168         int hour = utcComponents.getTime().getHour() + hourShift;
1169         final int dayShift;
1170         if (hour < 0) {
1171             dayShift = (hour - 23) / 24;
1172         } else if (hour > 23) {
1173             dayShift = hour / 24;
1174         } else {
1175             dayShift = 0;
1176         }
1177         hour -= 24 * dayShift;
1178 
1179         return new DateTimeComponents(new DateComponents(utcComponents.getDate(), dayShift),
1180                                       new TimeComponents(hour, minute, seconds, minutesFromUTC));
1181 
1182     }
1183 
1184     /** Split the instance into date/time components for a time zone.
1185      *
1186      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1187      *
1188      * @param timeZone time zone
1189      * @return date/time components
1190      * @since 7.2
1191      * @see #getComponents(TimeZone, TimeScale)
1192      */
1193     @DefaultDataContext
1194     public DateTimeComponents getComponents(final TimeZone timeZone) {
1195         return getComponents(timeZone, DataContext.getDefault().getTimeScales().getUTC());
1196     }
1197 
1198     /**
1199      * Split the instance into date/time components for a time zone.
1200      *
1201      * @param timeZone time zone
1202      * @param utc      time scale used to computed date and time components.
1203      * @return date/time components
1204      * @since 10.1
1205      */
1206     public DateTimeComponents getComponents(final TimeZone timeZone,
1207                                             final TimeScale utc) {
1208         final AbsoluteDate javaEpoch = new AbsoluteDate(DateComponents.JAVA_EPOCH, utc);
1209         final long milliseconds = FastMath.round(1000 * offsetFrom(javaEpoch, utc));
1210         return getComponents(timeZone.getOffset(milliseconds) / 60000, utc);
1211     }
1212 
1213     /** Compare the instance with another date.
1214      * @param date other date to compare the instance to
1215      * @return a negative integer, zero, or a positive integer as this date
1216      * is before, simultaneous, or after the specified date.
1217      */
1218     public int compareTo(final AbsoluteDate date) {
1219         final double duration = durationFrom(date);
1220         if (!Double.isNaN(duration)) {
1221             return Double.compare(duration, 0.0);
1222         }
1223         // both dates are infinity or one is NaN or both are NaN
1224         return Double.compare(offset, date.offset);
1225     }
1226 
1227     /** {@inheritDoc} */
1228     public AbsoluteDate getDate() {
1229         return this;
1230     }
1231 
1232     /** Check if the instance represents the same time as another instance.
1233      * @param date other date
1234      * @return true if the instance and the other date refer to the same instant
1235      */
1236     public boolean equals(final Object date) {
1237 
1238         if (date == this) {
1239             // first fast check
1240             return true;
1241         }
1242 
1243         if (date instanceof AbsoluteDate) {
1244 
1245             // Improve robustness against positive/negative infinity dates
1246             if ( this.offset == Double.NEGATIVE_INFINITY && ((AbsoluteDate) date).offset == Double.NEGATIVE_INFINITY ||
1247                     this.offset == Double.POSITIVE_INFINITY && ((AbsoluteDate) date).offset == Double.POSITIVE_INFINITY ) {
1248                 return true;
1249             } else {
1250                 return durationFrom((AbsoluteDate) date) == 0;
1251             }
1252         }
1253 
1254         return false;
1255     }
1256 
1257     /** Check if the instance represents the same time as another.
1258      * @param other the instant to compare this date to
1259      * @return true if the instance and the argument refer to the same instant
1260      * @see #isCloseTo(TimeStamped, double)
1261      * @since 10.1
1262      */
1263     public boolean isEqualTo(final TimeStamped other) {
1264         return this.equals(other.getDate());
1265     }
1266 
1267     /** Check if the instance time is close to another.
1268      * @param other the instant to compare this date to
1269      * @param tolerance the separation, in seconds, under which the two instants will be considered close to each other
1270      * @return true if the duration between the instance and the argument is strictly below the tolerance
1271      * @see #isEqualTo(TimeStamped)
1272      * @since 10.1
1273      */
1274     public boolean isCloseTo(final TimeStamped other, final double tolerance) {
1275         return FastMath.abs(this.durationFrom(other.getDate())) < tolerance;
1276     }
1277 
1278     /** Check if the instance represents a time that is strictly before another.
1279      * @param other the instant to compare this date to
1280      * @return true if the instance is strictly before the argument when ordering chronologically
1281      * @see #isBeforeOrEqualTo(TimeStamped)
1282      * @since 10.1
1283      */
1284     public boolean isBefore(final TimeStamped other) {
1285         return this.compareTo(other.getDate()) < 0;
1286     }
1287 
1288     /** Check if the instance represents a time that is strictly after another.
1289      * @param other the instant to compare this date to
1290      * @return true if the instance is strictly after the argument when ordering chronologically
1291      * @see #isAfterOrEqualTo(TimeStamped)
1292      * @since 10.1
1293      */
1294     public boolean isAfter(final TimeStamped other) {
1295         return this.compareTo(other.getDate()) > 0;
1296     }
1297 
1298     /** Check if the instance represents a time that is before or equal to another.
1299      * @param other the instant to compare this date to
1300      * @return true if the instance is before (or equal to) the argument when ordering chronologically
1301      * @see #isBefore(TimeStamped)
1302      * @since 10.1
1303      */
1304     public boolean isBeforeOrEqualTo(final TimeStamped other) {
1305         return this.isEqualTo(other) || this.isBefore(other);
1306     }
1307 
1308     /** Check if the instance represents a time that is after or equal to another.
1309      * @param other the instant to compare this date to
1310      * @return true if the instance is after (or equal to) the argument when ordering chronologically
1311      * @see #isAfterOrEqualTo(TimeStamped)
1312      * @since 10.1
1313      */
1314     public boolean isAfterOrEqualTo(final TimeStamped other) {
1315         return this.isEqualTo(other) || this.isAfter(other);
1316     }
1317 
1318     /** Check if the instance represents a time that is strictly between two others representing
1319      * the boundaries of a time span. The two boundaries can be provided in any order: in other words,
1320      * whether <code>boundary</code> represents a time that is before or after <code>otherBoundary</code> will
1321      * not change the result of this method.
1322      * @param boundary one end of the time span
1323      * @param otherBoundary the other end of the time span
1324      * @return true if the instance is strictly between the two arguments when ordering chronologically
1325      * @see #isBetweenOrEqualTo(TimeStamped, TimeStamped)
1326      * @since 10.1
1327      */
1328     public boolean isBetween(final TimeStamped boundary, final TimeStamped otherBoundary) {
1329         final TimeStamped beginning;
1330         final TimeStamped end;
1331         if (boundary.getDate().isBefore(otherBoundary)) {
1332             beginning = boundary;
1333             end = otherBoundary;
1334         } else {
1335             beginning = otherBoundary;
1336             end = boundary;
1337         }
1338         return this.isAfter(beginning) && this.isBefore(end);
1339     }
1340 
1341     /** Check if the instance represents a time that is between two others representing
1342      * the boundaries of a time span, or equal to one of them. The two boundaries can be provided in any order:
1343      * in other words, whether <code>boundary</code> represents a time that is before or after
1344      * <code>otherBoundary</code> will not change the result of this method.
1345      * @param boundary one end of the time span
1346      * @param otherBoundary the other end of the time span
1347      * @return true if the instance is between the two arguments (or equal to at least one of them)
1348      * when ordering chronologically
1349      * @see #isBetween(TimeStamped, TimeStamped)
1350      * @since 10.1
1351      */
1352     public boolean isBetweenOrEqualTo(final TimeStamped boundary, final TimeStamped otherBoundary) {
1353         return this.isEqualTo(boundary) || this.isEqualTo(otherBoundary) || this.isBetween(boundary, otherBoundary);
1354     }
1355 
1356     /** Get a hashcode for this date.
1357      * @return hashcode
1358      */
1359     public int hashCode() {
1360         final long l = Double.doubleToLongBits(durationFrom(ARBITRARY_EPOCH));
1361         return (int) (l ^ (l >>> 32));
1362     }
1363 
1364     /**
1365      * Get a String representation of the instant location with up to 16 digits of
1366      * precision for the seconds value.
1367      *
1368      * <p> Since this method is used in exception messages and error handling every
1369      * effort is made to return some representation of the instant. If UTC is available
1370      * from the default data context then it is used to format the string in UTC. If not
1371      * then TAI is used. Finally if the prior attempts fail this method falls back to
1372      * converting this class's internal representation to a string.
1373      *
1374      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1375      *
1376      * @return a string representation of the instance, in ISO-8601 format if UTC is
1377      * available from the default data context.
1378      * @see #toString(TimeScale)
1379      * @see #toStringRfc3339(TimeScale)
1380      * @see DateTimeComponents#toString(int, int)
1381      */
1382     @DefaultDataContext
1383     public String toString() {
1384         // CHECKSTYLE: stop IllegalCatch check
1385         try {
1386             // try to use UTC first at that is likely most familiar to the user.
1387             return toString(DataContext.getDefault().getTimeScales().getUTC()) + "Z";
1388         } catch (RuntimeException e1) {
1389             // catch OrekitException, OrekitIllegalStateException, etc.
1390             try {
1391                 // UTC failed, try to use TAI
1392                 return toString(new TAIScale()) + " TAI";
1393             } catch (RuntimeException e2) {
1394                 // catch OrekitException, OrekitIllegalStateException, etc.
1395                 // Likely failed to convert to ymdhms.
1396                 // Give user some indication of what time it is.
1397                 try {
1398                     return "(" + this.epoch + " + " + this.offset + ") seconds past epoch";
1399                 } catch (RuntimeException e3) {
1400                     // give up and throw an exception
1401                     e2.addSuppressed(e3);
1402                     e1.addSuppressed(e2);
1403                     throw e1;
1404                 }
1405             }
1406         }
1407         // CHECKSTYLE: resume IllegalCatch check
1408     }
1409 
1410     /**
1411      * Get a String representation of the instant location in ISO-8601 format without the
1412      * UTC offset and with up to 16 digits of precision for the seconds value.
1413      *
1414      * @param timeScale time scale to use
1415      * @return a string representation of the instance.
1416      * @see #toStringRfc3339(TimeScale)
1417      * @see DateTimeComponents#toString(int, int)
1418      */
1419     public String toString(final TimeScale timeScale) {
1420         return getComponents(timeScale).toStringWithoutUtcOffset();
1421     }
1422 
1423     /** Get a String representation of the instant location for a local time.
1424      *
1425      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1426      *
1427      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1428      * negative Westward UTC).
1429      * @return string representation of the instance,
1430      * in ISO-8601 format with milliseconds accuracy
1431      * @since 7.2
1432      * @see #toString(int, TimeScale)
1433      */
1434     @DefaultDataContext
1435     public String toString(final int minutesFromUTC) {
1436         return toString(minutesFromUTC,
1437                 DataContext.getDefault().getTimeScales().getUTC());
1438     }
1439 
1440     /**
1441      * Get a String representation of the instant location for a local time.
1442      *
1443      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1444      *                       negative Westward UTC).
1445      * @param utc            time scale used to compute date and time components.
1446      * @return string representation of the instance, in ISO-8601 format with milliseconds
1447      * accuracy
1448      * @since 10.1
1449      * @see #getComponents(int, TimeScale)
1450      * @see DateTimeComponents#toString(int, int)
1451      */
1452     public String toString(final int minutesFromUTC, final TimeScale utc) {
1453         final int minuteDuration = utc.minuteDuration(this);
1454         return getComponents(minutesFromUTC, utc).toString(minuteDuration);
1455     }
1456 
1457     /** Get a String representation of the instant location for a time zone.
1458      *
1459      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1460      *
1461      * @param timeZone time zone
1462      * @return string representation of the instance,
1463      * in ISO-8601 format with milliseconds accuracy
1464      * @since 7.2
1465      * @see #toString(TimeZone, TimeScale)
1466      */
1467     @DefaultDataContext
1468     public String toString(final TimeZone timeZone) {
1469         return toString(timeZone, DataContext.getDefault().getTimeScales().getUTC());
1470     }
1471 
1472     /**
1473      * Get a String representation of the instant location for a time zone.
1474      *
1475      * @param timeZone time zone
1476      * @param utc      time scale used to compute date and time components.
1477      * @return string representation of the instance, in ISO-8601 format with milliseconds
1478      * accuracy
1479      * @since 10.1
1480      * @see #getComponents(TimeZone, TimeScale)
1481      * @see DateTimeComponents#toString(int, int)
1482      */
1483     public String toString(final TimeZone timeZone, final TimeScale utc) {
1484         final int minuteDuration = utc.minuteDuration(this);
1485         return getComponents(timeZone, utc).toString(minuteDuration);
1486     }
1487 
1488     /**
1489      * Represent the given date as a string according to the format in RFC 3339. RFC3339
1490      * is a restricted subset of ISO 8601 with a well defined grammar. Enough digits are
1491      * included in the seconds value to avoid rounding up to the next minute.
1492      *
1493      * <p>This method is different than {@link AbsoluteDate#toString(TimeScale)} in that
1494      * it includes a {@code "Z"} at the end to indicate the time zone and enough precision
1495      * to represent the point in time without rounding up to the next minute.
1496      *
1497      * <p>RFC3339 is unable to represent BC years, years of 10000 or more, time zone
1498      * offsets of 100 hours or more, or NaN. In these cases the value returned from this
1499      * method will not be valid RFC3339 format.
1500      *
1501      * @param utc time scale.
1502      * @return RFC 3339 format string.
1503      * @see <a href="https://tools.ietf.org/html/rfc3339#page-8">RFC 3339</a>
1504      * @see DateTimeComponents#toStringRfc3339()
1505      * @see #toString(TimeScale)
1506      * @see #getComponents(TimeScale)
1507      */
1508     public String toStringRfc3339(final TimeScale utc) {
1509         return this.getComponents(utc).toStringRfc3339();
1510     }
1511 
1512     /**
1513      * Return a string representation of this date-time, rounded to the given precision.
1514      *
1515      * <p>The format used is ISO8601 without the UTC offset.</p>
1516      *
1517      * <p>Calling {@code toStringWithoutUtcOffset(DataContext.getDefault().getTimeScales().getUTC(),
1518      * 3)} will emulate the behavior of {@link #toString()} in Orekit 10 and earlier. Note
1519      * this method is more accurate as it correctly handles rounding during leap seconds.
1520      *
1521      * @param timeScale      to use to compute components.
1522      * @param fractionDigits the number of digits to include after the decimal point in
1523      *                       the string representation of the seconds. The date and time
1524      *                       is first rounded as necessary. {@code fractionDigits} must be
1525      *                       greater than or equal to {@code 0}.
1526      * @return string representation of this date, time, and UTC offset
1527      * @see #toString(TimeScale)
1528      * @see #toStringRfc3339(TimeScale)
1529      * @see DateTimeComponents#toString(int, int)
1530      * @see DateTimeComponents#toStringWithoutUtcOffset(int, int)
1531      * @since 11.1
1532      */
1533     public String toStringWithoutUtcOffset(final TimeScale timeScale,
1534                                            final int fractionDigits) {
1535         return this.getComponents(timeScale)
1536                 .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits);
1537     }
1538 
1539 }