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&defaultprint=YES&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&defaultprint=YES&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 }