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