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