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