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