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 
318         offset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
319         epoch  = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l +
320                         time.getMinute() - time.getMinutesFromUTC() - 720l) + dl;
321 
322     }
323 
324     /** Build an instance from a location in a {@link TimeScale time scale}.
325      * @param year year number (may be 0 or negative for BC years)
326      * @param month month number from 1 to 12
327      * @param day day number from 1 to 31
328      * @param hour hour number from 0 to 23
329      * @param minute minute number from 0 to 59
330      * @param second second number from 0.0 to 60.0 (excluded)
331      * @param timeScale time scale
332      * @exception IllegalArgumentException if inconsistent arguments
333      * are given (parameters out of range)
334      */
335     public AbsoluteDate(final int year, final int month, final int day,
336                         final int hour, final int minute, final double second,
337                         final TimeScale timeScale) throws IllegalArgumentException {
338         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
339     }
340 
341     /** Build an instance from a location in a {@link TimeScale time scale}.
342      * @param year year number (may be 0 or negative for BC years)
343      * @param month month enumerate
344      * @param day day number from 1 to 31
345      * @param hour hour number from 0 to 23
346      * @param minute minute number from 0 to 59
347      * @param second second number from 0.0 to 60.0 (excluded)
348      * @param timeScale time scale
349      * @exception IllegalArgumentException if inconsistent arguments
350      * are given (parameters out of range)
351      */
352     public AbsoluteDate(final int year, final Month month, final int day,
353                         final int hour, final int minute, final double second,
354                         final TimeScale timeScale) throws IllegalArgumentException {
355         this(new DateComponents(year, month, day), new TimeComponents(hour, minute, second), timeScale);
356     }
357 
358     /** Build an instance from a location in a {@link TimeScale time scale}.
359      * <p>The hour is set to 00:00:00.000.</p>
360      * @param date date location in the time scale
361      * @param timeScale time scale
362      * @exception IllegalArgumentException if inconsistent arguments
363      * are given (parameters out of range)
364      */
365     public AbsoluteDate(final DateComponents date, final TimeScale timeScale)
366         throws IllegalArgumentException {
367         this(date, TimeComponents.H00, timeScale);
368     }
369 
370     /** Build an instance from a location in a {@link TimeScale time scale}.
371      * <p>The hour is set to 00:00:00.000.</p>
372      * @param year year number (may be 0 or negative for BC years)
373      * @param month month number from 1 to 12
374      * @param day day number from 1 to 31
375      * @param timeScale time scale
376      * @exception IllegalArgumentException if inconsistent arguments
377      * are given (parameters out of range)
378      */
379     public AbsoluteDate(final int year, final int month, final int day,
380                         final TimeScale timeScale) throws IllegalArgumentException {
381         this(new DateComponents(year, month, day), 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 enumerate
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 Month 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      * @param location location in the time scale
400      * @param timeScale time scale
401      */
402     public AbsoluteDate(final Date location, final TimeScale timeScale) {
403         this(new DateComponents(DateComponents.JAVA_EPOCH,
404                                 (int) (location.getTime() / 86400000l)),
405                                  millisToTimeComponents((int) (location.getTime() % 86400000l)),
406              timeScale);
407     }
408 
409     /** Build an instance from an elapsed duration since to another instant.
410      * <p>It is important to note that the elapsed duration is <em>not</em>
411      * the difference between two readings on a time scale. As an example,
412      * the duration between the two instants leading to the readings
413      * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC}
414      * time scale is <em>not</em> 1 second, but a stop watch would have measured
415      * an elapsed duration of 2 seconds between these two instances because a leap
416      * second was introduced at the end of 2005 in this time scale.</p>
417      * <p>This constructor is the reverse of the {@link #durationFrom(AbsoluteDate)}
418      * method.</p>
419      * @param since start instant of the measured duration
420      * @param elapsedDuration physically elapsed duration from the <code>since</code>
421      * instant, as measured in a regular time scale
422      * @see #durationFrom(AbsoluteDate)
423      */
424     public AbsoluteDate(final AbsoluteDate since, final double elapsedDuration) {
425         // Use 2Sum for high precision.
426         final SumAndResidual sumAndResidual = MathUtils.twoSum(since.offset, elapsedDuration);
427         if (Double.isInfinite(sumAndResidual.getSum())) {
428             offset = sumAndResidual.getSum();
429             epoch  = (sumAndResidual.getSum() < 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
430         } else {
431             final long dl = (long) FastMath.floor(sumAndResidual.getSum());
432             offset = (sumAndResidual.getSum() - dl) + sumAndResidual.getResidual();
433             epoch  = since.epoch + dl;
434         }
435     }
436 
437     /** Build an instance from an apparent clock offset with respect to another
438      * instant <em>in the perspective of a specific {@link TimeScale time scale}</em>.
439      * <p>It is important to note that the apparent clock offset <em>is</em> the
440      * difference between two readings on a time scale and <em>not</em> an elapsed
441      * duration. As an example, the apparent clock offset between the two instants
442      * leading to the readings 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the
443      * {@link UTCScale UTC} time scale is 1 second, but the elapsed duration is 2
444      * seconds because a leap second has been introduced at the end of 2005 in this
445      * time scale.</p>
446      * <p>This constructor is the reverse of the {@link #offsetFrom(AbsoluteDate,
447      * TimeScale)} method.</p>
448      * @param reference reference instant
449      * @param apparentOffset apparent clock offset from the reference instant
450      * (difference between two readings in the specified time scale)
451      * @param timeScale time scale with respect to which the offset is defined
452      * @see #offsetFrom(AbsoluteDate, TimeScale)
453      */
454     public AbsoluteDate(final AbsoluteDate reference, final double apparentOffset,
455                         final TimeScale timeScale) {
456         this(new DateTimeComponents(reference.getComponents(timeScale), apparentOffset),
457              timeScale);
458     }
459 
460     /** Build a date from its internal components.
461      * <p>
462      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
463      * </p>
464      * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI.
465      * (beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT)
466      * @param offset offset from the reference epoch in seconds (must be
467      * between 0.0 included and 1.0 excluded)
468      * @since 9.0
469      */
470     AbsoluteDate(final long epoch, final double offset) {
471         this.epoch  = epoch;
472         this.offset = offset;
473     }
474 
475     /** Extract time components from a number of milliseconds within the day.
476      * @param millisInDay number of milliseconds within the day
477      * @return time components
478      */
479     private static TimeComponents millisToTimeComponents(final int millisInDay) {
480         return new TimeComponents(millisInDay / 1000, 0.001 * (millisInDay % 1000));
481     }
482 
483     /** Get the reference epoch in seconds from 2000-01-01T12:00:00 TAI.
484      * <p>
485      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
486      * </p>
487      * <p>
488      * Beware, it is not {@link #J2000_EPOCH} since it is in TAI and not in TT.
489      * </p>
490      * @return reference epoch in seconds from 2000-01-01T12:00:00 TAI
491      * @since 9.0
492      */
493     long getEpoch() {
494         return epoch;
495     }
496 
497     /** Get the offset from the reference epoch in seconds.
498      * <p>
499      * This method is reserved for internal used (for example by {@link FieldAbsoluteDate}).
500      * </p>
501      * @return offset from the reference epoch in seconds
502      * @since 9.0
503      */
504     double getOffset() {
505         return offset;
506     }
507 
508     /** Build an instance from a CCSDS Unsegmented Time Code (CUC).
509      * <p>
510      * CCSDS Unsegmented Time Code is defined in the blue book:
511      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
512      * </p>
513      * <p>
514      * If the date to be parsed is formatted using version 3 of the standard
515      * (CCSDS 301.0-B-3 published in 2002) or if the extension of the preamble
516      * field introduced in version 4 of the standard is not used, then the
517      * {@code preambleField2} parameter can be set to 0.
518      * </p>
519      *
520      * <p>This method uses the {@link DataContext#getDefault() default data context} if
521      * the CCSDS epoch is used.
522      *
523      * @param preambleField1 first byte of the field specifying the format, often
524      * not transmitted in data interfaces, as it is constant for a given data interface
525      * @param preambleField2 second byte of the field specifying the format
526      * (added in revision 4 of the CCSDS standard in 2010), often not transmitted in data
527      * interfaces, as it is constant for a given data interface (value ignored if presence
528      * not signaled in {@code preambleField1})
529      * @param timeField byte array containing the time code
530      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
531      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
532      * may be null in this case)
533      * @return an instance corresponding to the specified date
534      * @see #parseCCSDSUnsegmentedTimeCode(byte, byte, byte[], AbsoluteDate, AbsoluteDate)
535      */
536     @DefaultDataContext
537     public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(final byte preambleField1,
538                                                              final byte preambleField2,
539                                                              final byte[] timeField,
540                                                              final AbsoluteDate agencyDefinedEpoch) {
541         return parseCCSDSUnsegmentedTimeCode(preambleField1, preambleField2, timeField,
542                 agencyDefinedEpoch,
543                 DataContext.getDefault().getTimeScales().getCcsdsEpoch());
544     }
545 
546     /**
547      * Build an instance from a CCSDS Unsegmented Time Code (CUC).
548      * <p>
549      * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format
550      * (CCSDS 301.0-B-4) published in November 2010
551      * </p>
552      * <p>
553      * If the date to be parsed is formatted using version 3 of the standard (CCSDS
554      * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced
555      * in version 4 of the standard is not used, then the {@code preambleField2} parameter
556      * can be set to 0.
557      * </p>
558      *
559      * @param preambleField1     first byte of the field specifying the format, often not
560      *                           transmitted in data interfaces, as it is constant for a
561      *                           given data interface
562      * @param preambleField2     second byte of the field specifying the format (added in
563      *                           revision 4 of the CCSDS standard in 2010), often not
564      *                           transmitted in data interfaces, as it is constant for a
565      *                           given data interface (value ignored if presence not
566      *                           signaled in {@code preambleField1})
567      * @param timeField          byte array containing the time code
568      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies
569      *                           the {@link #CCSDS_EPOCH CCSDS reference epoch} is used
570      *                           (and hence may be null in this case)
571      * @param ccsdsEpoch         reference epoch, ignored if the preamble field specifies
572      *                           the agency epoch is used.
573      * @return an instance corresponding to the specified date
574      * @since 10.1
575      */
576     public static AbsoluteDate parseCCSDSUnsegmentedTimeCode(
577             final byte preambleField1,
578             final byte preambleField2,
579             final byte[] timeField,
580             final AbsoluteDate agencyDefinedEpoch,
581             final AbsoluteDate ccsdsEpoch) {
582 
583         // time code identification and reference epoch
584         final AbsoluteDate epoch;
585         switch (preambleField1 & 0x70) {
586             case 0x10:
587                 // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
588                 epoch = ccsdsEpoch;
589                 break;
590             case 0x20:
591                 // the reference epoch is agency defined
592                 if (agencyDefinedEpoch == null) {
593                     throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
594                 }
595                 epoch = agencyDefinedEpoch;
596                 break;
597             default :
598                 throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
599                                           formatByte(preambleField1));
600         }
601 
602         // time field lengths
603         int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2);
604         int fineTimeLength   = preambleField1 & 0x03;
605 
606         if ((preambleField1 & 0x80) != 0x0) {
607             // there is an additional octet in preamble field
608             coarseTimeLength += (preambleField2 & 0x60) >>> 5;
609             fineTimeLength   += (preambleField2 & 0x1C) >>> 2;
610         }
611 
612         if (timeField.length != coarseTimeLength + fineTimeLength) {
613             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
614                                       timeField.length, coarseTimeLength + fineTimeLength);
615         }
616 
617         double seconds = 0;
618         for (int i = 0; i < coarseTimeLength; ++i) {
619             seconds = seconds * 256 + toUnsigned(timeField[i]);
620         }
621         double subseconds = 0;
622         for (int i = timeField.length - 1; i >= coarseTimeLength; --i) {
623             subseconds = (subseconds + toUnsigned(timeField[i])) / 256;
624         }
625 
626         return new AbsoluteDate(epoch, seconds).shiftedBy(subseconds);
627 
628     }
629 
630     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
631      * <p>
632      * CCSDS Day Segmented Time Code is defined in the blue book:
633      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
634      * </p>
635      *
636      * <p>This method uses the {@link DataContext#getDefault() default data context}.
637      *
638      * @param preambleField field specifying the format, often not transmitted in
639      * data interfaces, as it is constant for a given data interface
640      * @param timeField byte array containing the time code
641      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
642      * specifies the {@link #CCSDS_EPOCH CCSDS reference epoch} is used (and hence
643      * may be null in this case)
644      * @return an instance corresponding to the specified date
645      * @see #parseCCSDSDaySegmentedTimeCode(byte, byte[], DateComponents, TimeScale)
646      */
647     @DefaultDataContext
648     public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, final byte[] timeField,
649                                                               final DateComponents agencyDefinedEpoch) {
650         return parseCCSDSDaySegmentedTimeCode(preambleField, timeField,
651                 agencyDefinedEpoch, DataContext.getDefault().getTimeScales().getUTC());
652     }
653 
654     /** Build an instance from a CCSDS Day Segmented Time Code (CDS).
655      * <p>
656      * CCSDS Day Segmented Time Code is defined in the blue book:
657      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
658      * </p>
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      * @param utc      time scale used to compute date and time components.
666      * @return an instance corresponding to the specified date
667      * @since 10.1
668      */
669     public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(
670             final byte preambleField,
671             final byte[] timeField,
672             final DateComponents agencyDefinedEpoch,
673             final TimeScale utc) {
674 
675         // time code identification
676         if ((preambleField & 0xF0) != 0x40) {
677             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
678                                       formatByte(preambleField));
679         }
680 
681         // reference epoch
682         final DateComponents epoch;
683         if ((preambleField & 0x08) == 0x00) {
684             // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
685             epoch = DateComponents.CCSDS_EPOCH;
686         } else {
687             // the reference epoch is agency defined
688             if (agencyDefinedEpoch == null) {
689                 throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
690             }
691             epoch = agencyDefinedEpoch;
692         }
693 
694         // time field lengths
695         final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3;
696         final int subMillisecondLength = (preambleField & 0x03) << 1;
697         if (subMillisecondLength == 6) {
698             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
699                                       formatByte(preambleField));
700         }
701         if (timeField.length != daySegmentLength + 4 + subMillisecondLength) {
702             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
703                                       timeField.length, daySegmentLength + 4 + subMillisecondLength);
704         }
705 
706 
707         int i   = 0;
708         int day = 0;
709         while (i < daySegmentLength) {
710             day = day * 256 + toUnsigned(timeField[i++]);
711         }
712 
713         long milliInDay = 0l;
714         while (i < daySegmentLength + 4) {
715             milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]);
716         }
717         final int milli   = (int) (milliInDay % 1000l);
718         final int seconds = (int) ((milliInDay - milli) / 1000l);
719 
720         double subMilli = 0;
721         double divisor  = 1;
722         while (i < timeField.length) {
723             subMilli = subMilli * 256 + toUnsigned(timeField[i++]);
724             divisor *= 1000;
725         }
726 
727         final DateComponents date = new DateComponents(epoch, day);
728         final TimeComponents time = new TimeComponents(seconds);
729         return new AbsoluteDate(date, time, utc).shiftedBy(milli * 1.0e-3 + subMilli / divisor);
730 
731     }
732 
733     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
734      * <p>
735      * CCSDS Calendar Segmented Time Code is defined in the blue book:
736      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
737      * </p>
738      *
739      * <p>This method uses the {@link DataContext#getDefault() default data context}.
740      *
741      * @param preambleField field specifying the format, often not transmitted in
742      * data interfaces, as it is constant for a given data interface
743      * @param timeField byte array containing the time code
744      * @return an instance corresponding to the specified date
745      * @see #parseCCSDSCalendarSegmentedTimeCode(byte, byte[], TimeScale)
746      */
747     @DefaultDataContext
748     public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField) {
749         return parseCCSDSCalendarSegmentedTimeCode(preambleField, timeField,
750                 DataContext.getDefault().getTimeScales().getUTC());
751     }
752 
753     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
754      * <p>
755      * CCSDS Calendar Segmented Time Code is defined in the blue book:
756      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
757      * </p>
758      * @param preambleField field specifying the format, often not transmitted in
759      * data interfaces, as it is constant for a given data interface
760      * @param timeField byte array containing the time code
761      * @param utc      time scale used to compute date and time components.
762      * @return an instance corresponding to the specified date
763      * @since 10.1
764      */
765     public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(
766             final byte preambleField,
767             final byte[] timeField,
768             final TimeScale utc) {
769 
770         // time code identification
771         if ((preambleField & 0xF0) != 0x50) {
772             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
773                                       formatByte(preambleField));
774         }
775 
776         // time field length
777         final int length = 7 + (preambleField & 0x07);
778         if (length == 14) {
779             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
780                                       formatByte(preambleField));
781         }
782         if (timeField.length != length) {
783             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
784                                       timeField.length, length);
785         }
786 
787         // date part in the first four bytes
788         final DateComponents date;
789         if ((preambleField & 0x08) == 0x00) {
790             // month of year and day of month variation
791             date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
792                                       toUnsigned(timeField[2]),
793                                       toUnsigned(timeField[3]));
794         } else {
795             // day of year variation
796             date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
797                                       toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3]));
798         }
799 
800         // time part from bytes 5 to last (between 7 and 13 depending on precision)
801         final TimeComponents time = new TimeComponents(toUnsigned(timeField[4]),
802                                                        toUnsigned(timeField[5]),
803                                                        toUnsigned(timeField[6]));
804         double subSecond = 0;
805         double divisor   = 1;
806         for (int i = 7; i < length; ++i) {
807             subSecond = subSecond * 100 + toUnsigned(timeField[i]);
808             divisor *= 100;
809         }
810 
811         return new AbsoluteDate(date, time, utc).shiftedBy(subSecond / divisor);
812 
813     }
814 
815     /** Decode a signed byte as an unsigned int value.
816      * @param b byte to decode
817      * @return an unsigned int value
818      */
819     private static int toUnsigned(final byte b) {
820         final int i = (int) b;
821         return (i < 0) ? 256 + i : i;
822     }
823 
824     /** Format a byte as an hex string for error messages.
825      * @param data byte to format
826      * @return a formatted string
827      */
828     private static String formatByte(final byte data) {
829         return "0x" + Integer.toHexString(data).toUpperCase();
830     }
831 
832     /** Build an instance corresponding to a Julian Day date.
833      * @param jd Julian day
834      * @param secondsSinceNoon seconds in the Julian day
835      * (BEWARE, Julian days start at noon, so 0.0 is noon)
836      * @param timeScale time scale in which the seconds in day are defined
837      * @return a new instant
838      */
839     public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon,
840                                              final TimeScale timeScale) {
841         return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jd),
842                                 TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon);
843     }
844 
845     /** Build an instance corresponding to a Modified Julian Day date.
846      * @param mjd modified Julian day
847      * @param secondsInDay seconds in the day
848      * @param timeScale time scale in which the seconds in day are defined
849      * @return a new instant
850      * @exception OrekitIllegalArgumentException if seconds number is out of range
851      */
852     public static AbsoluteDate createMJDDate(final int mjd, final double secondsInDay,
853                                              final TimeScale timeScale)
854         throws OrekitIllegalArgumentException {
855         final DateComponents dc = new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd);
856         final TimeComponents tc;
857         if (secondsInDay >= Constants.JULIAN_DAY) {
858             // check we are really allowed to use this number of seconds
859             final int    secondsA = 86399; // 23:59:59, i.e. 59s in the last minute of the day
860             final double secondsB = secondsInDay - secondsA;
861             final TimeComponents safeTC = new TimeComponents(secondsA, 0.0);
862             final AbsoluteDate safeDate = new AbsoluteDate(dc, safeTC, timeScale);
863             if (timeScale.minuteDuration(safeDate) > 59 + secondsB) {
864                 // we are within the last minute of the day, the number of seconds is OK
865                 return safeDate.shiftedBy(secondsB);
866             } else {
867                 // let TimeComponents trigger an OrekitIllegalArgumentException
868                 // for the wrong number of seconds
869                 tc = new TimeComponents(secondsA, secondsB);
870             }
871         } else {
872             tc = new TimeComponents(secondsInDay);
873         }
874 
875         // create the date
876         return new AbsoluteDate(dc, tc, timeScale);
877 
878     }
879 
880 
881     /** Build an instance corresponding to a Julian Epoch (JE).
882      * <p>According to Lieske paper: <a
883      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
884      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
885      * vol. 73, no. 3, Mar. 1979, p. 282-284, Julian Epoch is related to Julian Ephemeris Date as:</p>
886      * <pre>
887      * JE = 2000.0 + (JED - 2451545.0) / 365.25
888      * </pre>
889      * <p>
890      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Julian Epoch.
891      * </p>
892      *
893      * <p>This method uses the {@link DataContext#getDefault() default data context}.
894      *
895      * @param julianEpoch Julian epoch, like 2000.0 for defining the classical reference J2000.0
896      * @return a new instant
897      * @see #J2000_EPOCH
898      * @see #createBesselianEpoch(double)
899      * @see TimeScales#createJulianEpoch(double)
900      */
901     @DefaultDataContext
902     public static AbsoluteDate createJulianEpoch(final double julianEpoch) {
903         return DataContext.getDefault().getTimeScales().createJulianEpoch(julianEpoch);
904     }
905 
906     /** Build an instance corresponding to a Besselian Epoch (BE).
907      * <p>According to Lieske paper: <a
908      * href="http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&amp;defaultprint=YES&amp;filetype=.pdf.">
909      * Precession Matrix Based on IAU (1976) System of Astronomical Constants</a>, Astronomy and Astrophysics,
910      * vol. 73, no. 3, Mar. 1979, p. 282-284, Besselian Epoch is related to Julian Ephemeris Date as:</p>
911      * <pre>
912      * BE = 1900.0 + (JED - 2415020.31352) / 365.242198781
913      * </pre>
914      * <p>
915      * This method reverts the formula above and computes an {@code AbsoluteDate} from the Besselian Epoch.
916      * </p>
917      *
918      * <p>This method uses the {@link DataContext#getDefault() default data context}.
919      *
920      * @param besselianEpoch Besselian epoch, like 1950 for defining the classical reference B1950.0
921      * @return a new instant
922      * @see #createJulianEpoch(double)
923      * @see TimeScales#createBesselianEpoch(double)
924      */
925     @DefaultDataContext
926     public static AbsoluteDate createBesselianEpoch(final double besselianEpoch) {
927         return DataContext.getDefault().getTimeScales()
928                 .createBesselianEpoch(besselianEpoch);
929     }
930 
931     /** Get a time-shifted date.
932      * <p>
933      * Calling this method is equivalent to call <code>new AbsoluteDate(this, dt)</code>.
934      * </p>
935      * @param dt time shift in seconds
936      * @return a new date, shifted with respect to instance (which is immutable)
937      * @see org.orekit.utils.PVCoordinates#shiftedBy(double)
938      * @see org.orekit.attitudes.Attitude#shiftedBy(double)
939      * @see org.orekit.orbits.Orbit#shiftedBy(double)
940      * @see org.orekit.propagation.SpacecraftState#shiftedBy(double)
941      */
942     public AbsoluteDate shiftedBy(final double dt) {
943         return new AbsoluteDate(this, dt);
944     }
945 
946     /** Compute the physically elapsed duration between two instants.
947      * <p>The returned duration is the number of seconds physically
948      * elapsed between the two instants, measured in a regular time
949      * scale with respect to surface of the Earth (i.e either the {@link
950      * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link
951      * GPSScale GPS scale}). It is the only method that gives a
952      * duration with a physical meaning.</p>
953      * <p>This method gives the same result (with less computation)
954      * as calling {@link #offsetFrom(AbsoluteDate, TimeScale)}
955      * with a second argument set to one of the regular scales cited
956      * above.</p>
957      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
958      * double)} constructor.</p>
959      * @param instant instant to subtract from the instance
960      * @return offset in seconds between the two instants (positive
961      * if the instance is posterior to the argument)
962      * @see #offsetFrom(AbsoluteDate, TimeScale)
963      * @see #AbsoluteDate(AbsoluteDate, double)
964      */
965     public double durationFrom(final AbsoluteDate instant) {
966         return (epoch - instant.epoch) + (offset - instant.offset);
967     }
968 
969     /** Compute the apparent clock offset between two instant <em>in the
970      * perspective of a specific {@link TimeScale time scale}</em>.
971      * <p>The offset is the number of seconds counted in the given
972      * time scale between the locations of the two instants, with
973      * all time scale irregularities removed (i.e. considering all
974      * days are exactly 86400 seconds long). This method will give
975      * a result that may not have a physical meaning if the time scale
976      * is irregular. For example since a leap second was introduced at
977      * the end of 2005, the apparent offset between 2005-12-31T23:59:59
978      * and 2006-01-01T00:00:00 is 1 second, but the physical duration
979      * of the corresponding time interval as returned by the {@link
980      * #durationFrom(AbsoluteDate)} method is 2 seconds.</p>
981      * <p>This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate,
982      * double, TimeScale)} constructor.</p>
983      * @param instant instant to subtract from the instance
984      * @param timeScale time scale with respect to which the offset should
985      * be computed
986      * @return apparent clock offset in seconds between the two instants
987      * (positive if the instance is posterior to the argument)
988      * @see #durationFrom(AbsoluteDate)
989      * @see #AbsoluteDate(AbsoluteDate, double, TimeScale)
990      */
991     public double offsetFrom(final AbsoluteDate instant, final TimeScale timeScale) {
992         final long   elapsedDurationA = epoch - instant.epoch;
993         final double elapsedDurationB = (offset         + timeScale.offsetFromTAI(this)) -
994                                         (instant.offset + timeScale.offsetFromTAI(instant));
995         return  elapsedDurationA + elapsedDurationB;
996     }
997 
998     /** Compute the offset between two time scales at the current instant.
999      * <p>The offset is defined as <i>l₁-l₂</i>
1000      * where <i>l₁</i> is the location of the instant in
1001      * the <code>scale1</code> time scale and <i>l₂</i> is the
1002      * location of the instant in the <code>scale2</code> time scale.</p>
1003      * @param scale1 first time scale
1004      * @param scale2 second time scale
1005      * @return offset in seconds between the two time scales at the
1006      * current instant
1007      */
1008     public double timeScalesOffset(final TimeScale scale1, final TimeScale scale2) {
1009         return scale1.offsetFromTAI(this) - scale2.offsetFromTAI(this);
1010     }
1011 
1012     /** Convert the instance to a Java {@link java.util.Date Date}.
1013      * <p>Conversion to the Date class induces a loss of precision because
1014      * the Date class does not provide sub-millisecond information. Java Dates
1015      * are considered to be locations in some times scales.</p>
1016      * @param timeScale time scale to use
1017      * @return a {@link java.util.Date Date} instance representing the location
1018      * of the instant in the time scale
1019      */
1020     public Date toDate(final TimeScale timeScale) {
1021         final double time = epoch + (offset + timeScale.offsetFromTAI(this));
1022         return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000));
1023     }
1024 
1025     /** Split the instance into date/time components.
1026      * @param timeScale time scale to use
1027      * @return date/time components
1028      */
1029     public DateTimeComponents getComponents(final TimeScale timeScale) {
1030 
1031         if (Double.isInfinite(offset)) {
1032             // special handling for past and future infinity
1033             if (offset < 0) {
1034                 return new DateTimeComponents(DateComponents.MIN_EPOCH, TimeComponents.H00);
1035             } else {
1036                 return new DateTimeComponents(DateComponents.MAX_EPOCH,
1037                                               new TimeComponents(23, 59, 59.999));
1038             }
1039         }
1040 
1041         // Compute offset from 2000-01-01T00:00:00 in specified time scale.
1042         // Use 2Sum for high precision.
1043         final double taiOffset = timeScale.offsetFromTAI(this);
1044         final SumAndResidual sumAndResidual = MathUtils.twoSum(offset, taiOffset);
1045 
1046         // split date and time
1047         final long   carry = (long) FastMath.floor(sumAndResidual.getSum());
1048         double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual();
1049         long   offset2000A = epoch + carry + 43200l;
1050         if (offset2000B < 0) {
1051             offset2000A -= 1;
1052             offset2000B += 1;
1053         }
1054         long time = offset2000A % 86400l;
1055         if (time < 0l) {
1056             time += 86400l;
1057         }
1058         final int date = (int) ((offset2000A - time) / 86400l);
1059 
1060         // extract calendar elements
1061         final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date);
1062 
1063         // extract time element, accounting for leap seconds
1064         final double leap = timeScale.insideLeap(this) ? timeScale.getLeap(this) : 0;
1065         final int minuteDuration = timeScale.minuteDuration(this);
1066         final TimeComponents timeComponents =
1067                 TimeComponents.fromSeconds((int) time, offset2000B, leap, minuteDuration);
1068 
1069         // build the components
1070         return new DateTimeComponents(dateComponents, timeComponents);
1071 
1072     }
1073 
1074     /** Split the instance into date/time components for a local time.
1075      *
1076      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1077      *
1078      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1079      * negative Westward UTC)
1080      * @return date/time components
1081      * @since 7.2
1082      * @see #getComponents(int, TimeScale)
1083      */
1084     @DefaultDataContext
1085     public DateTimeComponents getComponents(final int minutesFromUTC) {
1086         return getComponents(minutesFromUTC,
1087                 DataContext.getDefault().getTimeScales().getUTC());
1088     }
1089 
1090     /**
1091      * Split the instance into date/time components for a local time.
1092      *
1093      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1094      *                       negative Westward UTC)
1095      * @param utc            time scale used to compute date and time components.
1096      * @return date/time components
1097      * @since 10.1
1098      */
1099     public DateTimeComponents getComponents(final int minutesFromUTC,
1100                                             final TimeScale utc) {
1101 
1102         final DateTimeComponents utcComponents = getComponents(utc);
1103 
1104         // shift the date according to UTC offset, but WITHOUT touching the seconds,
1105         // as they may exceed 60.0 during a leap seconds introduction,
1106         // and we want to preserve these special cases
1107         final double seconds = utcComponents.getTime().getSecond();
1108 
1109         int minute = utcComponents.getTime().getMinute() + minutesFromUTC;
1110         final int hourShift;
1111         if (minute < 0) {
1112             hourShift = (minute - 59) / 60;
1113         } else if (minute > 59) {
1114             hourShift = minute / 60;
1115         } else {
1116             hourShift = 0;
1117         }
1118         minute -= 60 * hourShift;
1119 
1120         int hour = utcComponents.getTime().getHour() + hourShift;
1121         final int dayShift;
1122         if (hour < 0) {
1123             dayShift = (hour - 23) / 24;
1124         } else if (hour > 23) {
1125             dayShift = hour / 24;
1126         } else {
1127             dayShift = 0;
1128         }
1129         hour -= 24 * dayShift;
1130 
1131         return new DateTimeComponents(new DateComponents(utcComponents.getDate(), dayShift),
1132                                       new TimeComponents(hour, minute, seconds, minutesFromUTC));
1133 
1134     }
1135 
1136     /** Split the instance into date/time components for a time zone.
1137      *
1138      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1139      *
1140      * @param timeZone time zone
1141      * @return date/time components
1142      * @since 7.2
1143      * @see #getComponents(TimeZone, TimeScale)
1144      */
1145     @DefaultDataContext
1146     public DateTimeComponents getComponents(final TimeZone timeZone) {
1147         return getComponents(timeZone, DataContext.getDefault().getTimeScales().getUTC());
1148     }
1149 
1150     /**
1151      * Split the instance into date/time components for a time zone.
1152      *
1153      * @param timeZone time zone
1154      * @param utc      time scale used to computed date and time components.
1155      * @return date/time components
1156      * @since 10.1
1157      */
1158     public DateTimeComponents getComponents(final TimeZone timeZone,
1159                                             final TimeScale utc) {
1160         final AbsoluteDate javaEpoch = new AbsoluteDate(DateComponents.JAVA_EPOCH, utc);
1161         final long milliseconds = FastMath.round(1000 * offsetFrom(javaEpoch, utc));
1162         return getComponents(timeZone.getOffset(milliseconds) / 60000, utc);
1163     }
1164 
1165     /** Compare the instance with another date.
1166      * @param date other date to compare the instance to
1167      * @return a negative integer, zero, or a positive integer as this date
1168      * is before, simultaneous, or after the specified date.
1169      */
1170     public int compareTo(final AbsoluteDate date) {
1171         final double duration = durationFrom(date);
1172         if (!Double.isNaN(duration)) {
1173             return Double.compare(duration, 0.0);
1174         }
1175         // both dates are infinity or one is NaN or both are NaN
1176         return Double.compare(offset, date.offset);
1177     }
1178 
1179     /** {@inheritDoc} */
1180     public AbsoluteDate getDate() {
1181         return this;
1182     }
1183 
1184     /** Check if the instance represents the same time as another instance.
1185      * @param date other date
1186      * @return true if the instance and the other date refer to the same instant
1187      */
1188     public boolean equals(final Object date) {
1189 
1190         if (date == this) {
1191             // first fast check
1192             return true;
1193         }
1194 
1195         if (date instanceof AbsoluteDate) {
1196             return durationFrom((AbsoluteDate) date) == 0;
1197         }
1198 
1199         return false;
1200 
1201     }
1202 
1203     /** Check if the instance represents the same time as another.
1204      * @param other the instant to compare this date to
1205      * @return true if the instance and the argument refer to the same instant
1206      * @see #isCloseTo(TimeStamped, double)
1207      * @since 10.1
1208      */
1209     public boolean isEqualTo(final TimeStamped other) {
1210         return this.equals(other.getDate());
1211     }
1212 
1213     /** Check if the instance time is close to another.
1214      * @param other the instant to compare this date to
1215      * @param tolerance the separation, in seconds, under which the two instants will be considered close to each other
1216      * @return true if the duration between the instance and the argument is strictly below the tolerance
1217      * @see #isEqualTo(TimeStamped)
1218      * @since 10.1
1219      */
1220     public boolean isCloseTo(final TimeStamped other, final double tolerance) {
1221         return FastMath.abs(this.durationFrom(other.getDate())) < tolerance;
1222     }
1223 
1224     /** Check if the instance represents a time that is strictly before another.
1225      * @param other the instant to compare this date to
1226      * @return true if the instance is strictly before the argument when ordering chronologically
1227      * @see #isBeforeOrEqualTo(TimeStamped)
1228      * @since 10.1
1229      */
1230     public boolean isBefore(final TimeStamped other) {
1231         return this.compareTo(other.getDate()) < 0;
1232     }
1233 
1234     /** Check if the instance represents a time that is strictly after another.
1235      * @param other the instant to compare this date to
1236      * @return true if the instance is strictly after the argument when ordering chronologically
1237      * @see #isAfterOrEqualTo(TimeStamped)
1238      * @since 10.1
1239      */
1240     public boolean isAfter(final TimeStamped other) {
1241         return this.compareTo(other.getDate()) > 0;
1242     }
1243 
1244     /** Check if the instance represents a time that is before or equal to another.
1245      * @param other the instant to compare this date to
1246      * @return true if the instance is before (or equal to) the argument when ordering chronologically
1247      * @see #isBefore(TimeStamped)
1248      * @since 10.1
1249      */
1250     public boolean isBeforeOrEqualTo(final TimeStamped other) {
1251         return this.isEqualTo(other) || this.isBefore(other);
1252     }
1253 
1254     /** Check if the instance represents a time that is after or equal to another.
1255      * @param other the instant to compare this date to
1256      * @return true if the instance is after (or equal to) the argument when ordering chronologically
1257      * @see #isAfterOrEqualTo(TimeStamped)
1258      * @since 10.1
1259      */
1260     public boolean isAfterOrEqualTo(final TimeStamped other) {
1261         return this.isEqualTo(other) || this.isAfter(other);
1262     }
1263 
1264     /** Check if the instance represents a time that is strictly between two others representing
1265      * the boundaries of a time span. The two boundaries can be provided in any order: in other words,
1266      * whether <code>boundary</code> represents a time that is before or after <code>otherBoundary</code> will
1267      * not change the result of this method.
1268      * @param boundary one end of the time span
1269      * @param otherBoundary the other end of the time span
1270      * @return true if the instance is strictly between the two arguments when ordering chronologically
1271      * @see #isBetweenOrEqualTo(TimeStamped, TimeStamped)
1272      * @since 10.1
1273      */
1274     public boolean isBetween(final TimeStamped boundary, final TimeStamped otherBoundary) {
1275         final TimeStamped beginning;
1276         final TimeStamped end;
1277         if (boundary.getDate().isBefore(otherBoundary)) {
1278             beginning = boundary;
1279             end = otherBoundary;
1280         } else {
1281             beginning = otherBoundary;
1282             end = boundary;
1283         }
1284         return this.isAfter(beginning) && this.isBefore(end);
1285     }
1286 
1287     /** Check if the instance represents a time that is between two others representing
1288      * the boundaries of a time span, or equal to one of them. The two boundaries can be provided in any order:
1289      * in other words, whether <code>boundary</code> represents a time that is before or after
1290      * <code>otherBoundary</code> will not change the result of this method.
1291      * @param boundary one end of the time span
1292      * @param otherBoundary the other end of the time span
1293      * @return true if the instance is between the two arguments (or equal to at least one of them)
1294      * when ordering chronologically
1295      * @see #isBetween(TimeStamped, TimeStamped)
1296      * @since 10.1
1297      */
1298     public boolean isBetweenOrEqualTo(final TimeStamped boundary, final TimeStamped otherBoundary) {
1299         return this.isEqualTo(boundary) || this.isEqualTo(otherBoundary) || this.isBetween(boundary, otherBoundary);
1300     }
1301 
1302     /** Get a hashcode for this date.
1303      * @return hashcode
1304      */
1305     public int hashCode() {
1306         final long l = Double.doubleToLongBits(durationFrom(ARBITRARY_EPOCH));
1307         return (int) (l ^ (l >>> 32));
1308     }
1309 
1310     /**
1311      * Get a String representation of the instant location with up to 16 digits of
1312      * precision for the seconds value.
1313      *
1314      * <p> Since this method is used in exception messages and error handling every
1315      * effort is made to return some representation of the instant. If UTC is available
1316      * from the default data context then it is used to format the string in UTC. If not
1317      * then TAI is used. Finally if the prior attempts fail this method falls back to
1318      * converting this class's internal representation to a string.
1319      *
1320      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1321      *
1322      * @return a string representation of the instance, in ISO-8601 format if UTC is
1323      * available from the default data context.
1324      * @see #toString(TimeScale)
1325      * @see #toStringRfc3339(TimeScale)
1326      * @see DateTimeComponents#toString(int, int)
1327      */
1328     @DefaultDataContext
1329     public String toString() {
1330         // CHECKSTYLE: stop IllegalCatch check
1331         try {
1332             // try to use UTC first at that is likely most familiar to the user.
1333             return toString(DataContext.getDefault().getTimeScales().getUTC()) + "Z";
1334         } catch (RuntimeException e1) {
1335             // catch OrekitException, OrekitIllegalStateException, etc.
1336             try {
1337                 // UTC failed, try to use TAI
1338                 return toString(new TAIScale()) + " TAI";
1339             } catch (RuntimeException e2) {
1340                 // catch OrekitException, OrekitIllegalStateException, etc.
1341                 // Likely failed to convert to ymdhms.
1342                 // Give user some indication of what time it is.
1343                 try {
1344                     return "(" + this.epoch + " + " + this.offset + ") seconds past epoch";
1345                 } catch (RuntimeException e3) {
1346                     // give up and throw an exception
1347                     e2.addSuppressed(e3);
1348                     e1.addSuppressed(e2);
1349                     throw e1;
1350                 }
1351             }
1352         }
1353         // CHECKSTYLE: resume IllegalCatch check
1354     }
1355 
1356     /**
1357      * Get a String representation of the instant location in ISO-8601 format without the
1358      * UTC offset and with up to 16 digits of precision for the seconds value.
1359      *
1360      * @param timeScale time scale to use
1361      * @return a string representation of the instance.
1362      * @see #toStringRfc3339(TimeScale)
1363      * @see DateTimeComponents#toString(int, int)
1364      */
1365     public String toString(final TimeScale timeScale) {
1366         return getComponents(timeScale).toStringWithoutUtcOffset();
1367     }
1368 
1369     /** Get a String representation of the instant location for a local time.
1370      *
1371      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1372      *
1373      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1374      * negative Westward UTC).
1375      * @return string representation of the instance,
1376      * in ISO-8601 format with milliseconds accuracy
1377      * @since 7.2
1378      * @see #toString(int, TimeScale)
1379      */
1380     @DefaultDataContext
1381     public String toString(final int minutesFromUTC) {
1382         return toString(minutesFromUTC,
1383                 DataContext.getDefault().getTimeScales().getUTC());
1384     }
1385 
1386     /**
1387      * Get a String representation of the instant location for a local time.
1388      *
1389      * @param minutesFromUTC offset in <em>minutes</em> from UTC (positive Eastwards UTC,
1390      *                       negative Westward UTC).
1391      * @param utc            time scale used to compute date and time components.
1392      * @return string representation of the instance, in ISO-8601 format with milliseconds
1393      * accuracy
1394      * @since 10.1
1395      * @see #getComponents(int, TimeScale)
1396      * @see DateTimeComponents#toString(int, int)
1397      */
1398     public String toString(final int minutesFromUTC, final TimeScale utc) {
1399         final int minuteDuration = utc.minuteDuration(this);
1400         return getComponents(minutesFromUTC, utc).toString(minuteDuration);
1401     }
1402 
1403     /** Get a String representation of the instant location for a time zone.
1404      *
1405      * <p>This method uses the {@link DataContext#getDefault() default data context}.
1406      *
1407      * @param timeZone time zone
1408      * @return string representation of the instance,
1409      * in ISO-8601 format with milliseconds accuracy
1410      * @since 7.2
1411      * @see #toString(TimeZone, TimeScale)
1412      */
1413     @DefaultDataContext
1414     public String toString(final TimeZone timeZone) {
1415         return toString(timeZone, DataContext.getDefault().getTimeScales().getUTC());
1416     }
1417 
1418     /**
1419      * Get a String representation of the instant location for a time zone.
1420      *
1421      * @param timeZone time zone
1422      * @param utc      time scale used to compute date and time components.
1423      * @return string representation of the instance, in ISO-8601 format with milliseconds
1424      * accuracy
1425      * @since 10.1
1426      * @see #getComponents(TimeZone, TimeScale)
1427      * @see DateTimeComponents#toString(int, int)
1428      */
1429     public String toString(final TimeZone timeZone, final TimeScale utc) {
1430         final int minuteDuration = utc.minuteDuration(this);
1431         return getComponents(timeZone, utc).toString(minuteDuration);
1432     }
1433 
1434     /**
1435      * Represent the given date as a string according to the format in RFC 3339. RFC3339
1436      * is a restricted subset of ISO 8601 with a well defined grammar. Enough digits are
1437      * included in the seconds value to avoid rounding up to the next minute.
1438      *
1439      * <p>This method is different than {@link AbsoluteDate#toString(TimeScale)} in that
1440      * it includes a {@code "Z"} at the end to indicate the time zone and enough precision
1441      * to represent the point in time without rounding up to the next minute.
1442      *
1443      * <p>RFC3339 is unable to represent BC years, years of 10000 or more, time zone
1444      * offsets of 100 hours or more, or NaN. In these cases the value returned from this
1445      * method will not be valid RFC3339 format.
1446      *
1447      * @param utc time scale.
1448      * @return RFC 3339 format string.
1449      * @see <a href="https://tools.ietf.org/html/rfc3339#page-8">RFC 3339</a>
1450      * @see DateTimeComponents#toStringRfc3339()
1451      * @see #toString(TimeScale)
1452      * @see #getComponents(TimeScale)
1453      */
1454     public String toStringRfc3339(final TimeScale utc) {
1455         return this.getComponents(utc).toStringRfc3339();
1456     }
1457 
1458     /**
1459      * Return a string representation of this date-time, rounded to the given precision.
1460      *
1461      * <p>The format used is ISO8601 without the UTC offset.</p>
1462      *
1463      * <p>Calling {@code toStringWithoutUtcOffset(DataContext.getDefault().getTimeScales().getUTC(),
1464      * 3)} will emulate the behavior of {@link #toString()} in Orekit 10 and earlier. Note
1465      * this method is more accurate as it correctly handles rounding during leap seconds.
1466      *
1467      * @param timeScale      to use to compute components.
1468      * @param fractionDigits the number of digits to include after the decimal point in
1469      *                       the string representation of the seconds. The date and time
1470      *                       is first rounded as necessary. {@code fractionDigits} must be
1471      *                       greater than or equal to {@code 0}.
1472      * @return string representation of this date, time, and UTC offset
1473      * @see #toString(TimeScale)
1474      * @see #toStringRfc3339(TimeScale)
1475      * @see DateTimeComponents#toString(int, int)
1476      * @see DateTimeComponents#toStringWithoutUtcOffset(int, int)
1477      * @since 11.1
1478      */
1479     public String toStringWithoutUtcOffset(final TimeScale timeScale,
1480                                            final int fractionDigits) {
1481         return this.getComponents(timeScale)
1482                 .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits);
1483     }
1484 
1485 }