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