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