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