1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.time;
18  
19  import java.sql.Timestamp;
20  import java.time.Instant;
21  import java.time.LocalDateTime;
22  import java.time.OffsetDateTime;
23  import java.time.ZoneOffset;
24  import java.time.ZonedDateTime;
25  import java.time.format.DateTimeFormatter;
26  import java.util.Date;
27  import java.util.TimeZone;
28  
29  import java.util.concurrent.TimeUnit;
30  import org.hamcrest.CoreMatchers;
31  import org.hamcrest.MatcherAssert;
32  import org.hipparchus.util.FastMath;
33  import org.hipparchus.util.Precision;
34  import org.junit.jupiter.api.Assertions;
35  import org.junit.jupiter.api.BeforeEach;
36  import org.junit.jupiter.api.Test;
37  import org.orekit.OrekitMatchers;
38  import org.orekit.Utils;
39  import org.orekit.annotation.DefaultDataContext;
40  import org.orekit.data.DataContext;
41  import org.orekit.errors.OrekitException;
42  import org.orekit.errors.OrekitIllegalArgumentException;
43  import org.orekit.errors.OrekitMessages;
44  import org.orekit.utils.Constants;
45  
46  public class AbsoluteDateTest {
47  
48      @Test
49      @DefaultDataContext
50      public void testStandardEpoch() {
51          TimeScale tai = TimeScalesFactory.getTAI();
52          TimeScale tt  = TimeScalesFactory.getTT();
53          Assertions.assertEquals(-210866760000000L, AbsoluteDate.JULIAN_EPOCH.toDate(tt).getTime());
54          Assertions.assertEquals(-3506716800000L,   AbsoluteDate.MODIFIED_JULIAN_EPOCH.toDate(tt).getTime());
55          Assertions.assertEquals(-631152000000L,    AbsoluteDate.FIFTIES_EPOCH.toDate(tt).getTime());
56          Assertions.assertEquals(-378691200000L,    AbsoluteDate.CCSDS_EPOCH.toDate(tai).getTime());
57          Assertions.assertEquals(935280019000L,     AbsoluteDate.GALILEO_EPOCH.toDate(tai).getTime());
58          Assertions.assertEquals(315964819000L,     AbsoluteDate.GPS_EPOCH.toDate(tai).getTime());
59          Assertions.assertEquals(315964819000L,     AbsoluteDate.QZSS_EPOCH.toDate(tai).getTime());
60          Assertions.assertEquals(1136073633000L,    AbsoluteDate.BEIDOU_EPOCH.toDate(tai).getTime());
61          Assertions.assertEquals(820443629000L,     AbsoluteDate.GLONASS_EPOCH.toDate(tai).getTime());
62          Assertions.assertEquals(935280019000L,     AbsoluteDate.NAVIC_EPOCH.toDate(tai).getTime());
63          Assertions.assertEquals(946728000000L,     AbsoluteDate.J2000_EPOCH.toDate(tt).getTime());
64      }
65  
66      @Test
67      @DefaultDataContext
68      public void testStandardEpochStrings() {
69          Assertions.assertEquals("-4712-01-01T12:00:00.000",
70                       AbsoluteDate.JULIAN_EPOCH.toString(TimeScalesFactory.getTT()));
71          Assertions.assertEquals("1858-11-17T00:00:00.000",
72                       AbsoluteDate.MODIFIED_JULIAN_EPOCH.toString(TimeScalesFactory.getTT()));
73          Assertions.assertEquals("1950-01-01T00:00:00.000",
74                              AbsoluteDate.FIFTIES_EPOCH.toString(TimeScalesFactory.getTT()));
75          Assertions.assertEquals("1958-01-01T00:00:00.000",
76                              AbsoluteDate.CCSDS_EPOCH.toString(TimeScalesFactory.getTAI()));
77          Assertions.assertEquals("1999-08-21T23:59:47.000",
78                              AbsoluteDate.GALILEO_EPOCH.toString(TimeScalesFactory.getUTC()));
79          Assertions.assertEquals("1980-01-06T00:00:00.000",
80                              AbsoluteDate.GPS_EPOCH.toString(TimeScalesFactory.getUTC()));
81          Assertions.assertEquals("1980-01-06T00:00:00.000",
82                              AbsoluteDate.QZSS_EPOCH.toString(TimeScalesFactory.getUTC()));
83          Assertions.assertEquals("2006-01-01T00:00:00.000",
84                              AbsoluteDate.BEIDOU_EPOCH.toString(TimeScalesFactory.getUTC()));
85          Assertions.assertEquals("1995-12-31T21:00:00.000",
86                              AbsoluteDate.GLONASS_EPOCH.toString(TimeScalesFactory.getUTC()));
87          Assertions.assertEquals("1999-08-21T23:59:47.000",
88                  AbsoluteDate.NAVIC_EPOCH.toString(TimeScalesFactory.getUTC()));
89          Assertions.assertEquals("2000-01-01T12:00:00.000",
90                       AbsoluteDate.J2000_EPOCH.toString(TimeScalesFactory.getTT()));
91          Assertions.assertEquals("1970-01-01T00:00:00.000",
92                       AbsoluteDate.JAVA_EPOCH.toString(TimeScalesFactory.getUTC()));
93      }
94  
95      @Test
96      public void testJulianEpochRate() {
97  
98          for (int i = 0; i < 10; ++i) {
99              AbsoluteDate j200i = AbsoluteDate.createJulianEpoch(2000.0 + i);
100             AbsoluteDate j2000 = AbsoluteDate.J2000_EPOCH;
101             double expected    = i * Constants.JULIAN_YEAR;
102             Assertions.assertEquals(expected, j200i.durationFrom(j2000), 4.0e-15 * expected);
103         }
104 
105     }
106 
107     @Test
108     public void testBesselianEpochRate() {
109 
110         for (int i = 0; i < 10; ++i) {
111             AbsoluteDate b195i = AbsoluteDate.createBesselianEpoch(1950.0 + i);
112             AbsoluteDate b1950 = AbsoluteDate.createBesselianEpoch(1950.0);
113             double expected    = i * Constants.BESSELIAN_YEAR;
114             Assertions.assertEquals(expected, b195i.durationFrom(b1950), 4.0e-15 * expected);
115         }
116 
117     }
118 
119     @Test
120     public void testLieske() {
121 
122         // the following test values correspond to table 1 in the paper:
123         // Precession Matrix Based on IAU (1976) System of Astronomical Constants,
124         // Jay H. Lieske, Astronomy and Astrophysics, vol. 73, no. 3, Mar. 1979, p. 282-284
125         // http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf
126 
127         // published table, with limited accuracy
128         final double publishedEpsilon = 1.0e-6 * Constants.JULIAN_YEAR;
129         checkEpochs(1899.999142, 1900.000000, publishedEpsilon);
130         checkEpochs(1900.000000, 1900.000858, publishedEpsilon);
131         checkEpochs(1950.000000, 1949.999790, publishedEpsilon);
132         checkEpochs(1950.000210, 1950.000000, publishedEpsilon);
133         checkEpochs(2000.000000, 1999.998722, publishedEpsilon);
134         checkEpochs(2000.001278, 2000.000000, publishedEpsilon);
135 
136         // recomputed table, using directly Lieske formulas (i.e. *not* Orekit implementation) with high accuracy
137         final double accurateEpsilon = 1.2e-13 * Constants.JULIAN_YEAR;
138         checkEpochs(1899.99914161068724704, 1900.00000000000000000, accurateEpsilon);
139         checkEpochs(1900.00000000000000000, 1900.00085837097878165, accurateEpsilon);
140         checkEpochs(1950.00000000000000000, 1949.99979044229979466, accurateEpsilon);
141         checkEpochs(1950.00020956217615449, 1950.00000000000000000, accurateEpsilon);
142         checkEpochs(2000.00000000000000000, 1999.99872251362080766, accurateEpsilon);
143         checkEpochs(2000.00127751366506194, 2000.00000000000000000, accurateEpsilon);
144 
145     }
146 
147     private void checkEpochs(final double besselianEpoch, final double julianEpoch, final double epsilon) {
148         final AbsoluteDate b = AbsoluteDate.createBesselianEpoch(besselianEpoch);
149         final AbsoluteDate j = AbsoluteDate.createJulianEpoch(julianEpoch);
150         Assertions.assertEquals(0.0, b.durationFrom(j), epsilon);
151     }
152 
153     @Test
154     public void testParse() {
155         Assertions.assertEquals(AbsoluteDate.MODIFIED_JULIAN_EPOCH,
156                             new AbsoluteDate("1858-W46-3", TimeScalesFactory.getTT()));
157         Assertions.assertEquals(AbsoluteDate.JULIAN_EPOCH,
158                             new AbsoluteDate("-4712-01-01T12:00:00.000", TimeScalesFactory.getTT()));
159         Assertions.assertEquals(AbsoluteDate.FIFTIES_EPOCH,
160                             new AbsoluteDate("1950-01-01", TimeScalesFactory.getTT()));
161         Assertions.assertEquals(AbsoluteDate.CCSDS_EPOCH,
162                             new AbsoluteDate("1958-001", TimeScalesFactory.getTAI()));
163     }
164 
165     @Test
166     public void testLocalTimeParsing() {
167         TimeScale utc = TimeScalesFactory.getUTC();
168         Assertions.assertEquals(new AbsoluteDate("2011-12-31T23:00:00",       utc),
169                             new AbsoluteDate("2012-01-01T03:30:00+04:30", utc));
170         Assertions.assertEquals(new AbsoluteDate("2011-12-31T23:00:00",       utc),
171                             new AbsoluteDate("2012-01-01T03:30:00+0430",  utc));
172         Assertions.assertEquals(new AbsoluteDate("2011-12-31T23:30:00",       utc),
173                             new AbsoluteDate("2012-01-01T03:30:00+04",    utc));
174         Assertions.assertEquals(new AbsoluteDate("2012-01-01T05:17:00",       utc),
175                             new AbsoluteDate("2011-12-31T22:17:00-07:00", utc));
176         Assertions.assertEquals(new AbsoluteDate("2012-01-01T05:17:00",       utc),
177                             new AbsoluteDate("2011-12-31T22:17:00-0700",  utc));
178         Assertions.assertEquals(new AbsoluteDate("2012-01-01T05:17:00",       utc),
179                             new AbsoluteDate("2011-12-31T22:17:00-07",    utc));
180     }
181 
182     @Test
183     public void testTimeZoneDisplay() {
184         final TimeScale utc = TimeScalesFactory.getUTC();
185         final AbsoluteDate date = new AbsoluteDate("2000-01-01T01:01:01.000", utc);
186         Assertions.assertEquals("2000-01-01T01:01:01.000Z",      date.toString());
187         Assertions.assertEquals("2000-01-01T11:01:01.000+10:00", date.toString( 600));
188         Assertions.assertEquals("1999-12-31T23:01:01.000-02:00", date.toString(-120));
189         Assertions.assertEquals("2000-01-01T01:01:01.000+00:00", date.toString(0));
190 
191         // winter time, Europe is one hour ahead of UTC
192         TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
193         Assertions.assertEquals("2001-01-22T11:30:00.000+01:00",
194                             new AbsoluteDate("2001-01-22T10:30:00", utc).toString(tz));
195 
196         // summer time, Europe is two hours ahead of UTC
197         Assertions.assertEquals("2001-06-23T11:30:00.000+02:00",
198                             new AbsoluteDate("2001-06-23T09:30:00", utc).toString(tz));
199 
200         // check with UTC
201         tz = TimeZone.getTimeZone("UTC");
202         Assertions.assertEquals("2001-06-23T09:30:00.000+00:00",
203                 new AbsoluteDate("2001-06-23T09:30:00", utc).toString(tz));
204 
205     }
206 
207     @Test
208     public void testLocalTimeLeapSecond() {
209 
210         TimeScale utc = TimeScalesFactory.getUTC();
211         AbsoluteDate beforeLeap = new AbsoluteDate("2012-06-30T23:59:59.8", utc);
212         AbsoluteDate inLeap     = new AbsoluteDate("2012-06-30T23:59:60.5", utc);
213         Assertions.assertEquals(0.7, inLeap.durationFrom(beforeLeap), 1.0e-12);
214         for (int minutesFromUTC = -1500; minutesFromUTC < 1500; ++minutesFromUTC) {
215             DateTimeComponents dtcBeforeLeap = beforeLeap.getComponents(minutesFromUTC);
216             DateTimeComponents dtcInsideLeap = inLeap.getComponents(minutesFromUTC);
217             Assertions.assertEquals(dtcBeforeLeap.getDate(), dtcInsideLeap.getDate());
218             Assertions.assertEquals(dtcBeforeLeap.getTime().getHour(), dtcInsideLeap.getTime().getHour());
219             Assertions.assertEquals(dtcBeforeLeap.getTime().getMinute(), dtcInsideLeap.getTime().getMinute());
220             Assertions.assertEquals(minutesFromUTC, dtcBeforeLeap.getTime().getMinutesFromUTC());
221             Assertions.assertEquals(minutesFromUTC, dtcInsideLeap.getTime().getMinutesFromUTC());
222             Assertions.assertEquals(59.8, dtcBeforeLeap.getTime().getSecond(), 1.0e-10);
223             Assertions.assertEquals(60.5, dtcInsideLeap.getTime().getSecond(), 1.0e-10);
224         }
225 
226     }
227 
228     @Test
229     public void testTimeZoneLeapSecond() {
230 
231         TimeScale utc = TimeScalesFactory.getUTC();
232         final TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
233         AbsoluteDate localBeforeMidnight = new AbsoluteDate("2012-06-30T21:59:59.800", utc);
234         Assertions.assertEquals("2012-06-30T23:59:59.800+02:00",
235                             localBeforeMidnight.toString(tz));
236         Assertions.assertEquals("2012-07-01T00:00:00.800+02:00",
237                             localBeforeMidnight.shiftedBy(1.0).toString(tz));
238 
239         AbsoluteDate beforeLeap = new AbsoluteDate("2012-06-30T23:59:59.8", utc);
240         AbsoluteDate inLeap     = new AbsoluteDate("2012-06-30T23:59:60.5", utc);
241         Assertions.assertEquals(0.7, inLeap.durationFrom(beforeLeap), 1.0e-12);
242         Assertions.assertEquals("2012-07-01T01:59:59.800+02:00", beforeLeap.toString(tz));
243         Assertions.assertEquals("2012-07-01T01:59:60.500+02:00", inLeap.toString(tz));
244 
245     }
246 
247     @Test
248     public void testParseLeap() {
249         TimeScale utc = TimeScalesFactory.getUTC();
250         AbsoluteDate beforeLeap = new AbsoluteDate("2012-06-30T23:59:59.8", utc);
251         AbsoluteDate inLeap     = new AbsoluteDate("2012-06-30T23:59:60.5", utc);
252         Assertions.assertEquals(0.7, inLeap.durationFrom(beforeLeap), 1.0e-12);
253         Assertions.assertEquals("2012-06-30T23:59:60.500", inLeap.toString(utc));
254     }
255 
256     @Test
257     public void testOutput() {
258         TimeScale tt = TimeScalesFactory.getTT();
259         Assertions.assertEquals("1950-01-01T01:01:01.000",
260                             AbsoluteDate.FIFTIES_EPOCH.shiftedBy(3661.0).toString(tt));
261         Assertions.assertEquals("2000-01-01T13:01:01.000",
262                             AbsoluteDate.J2000_EPOCH.shiftedBy(3661.0).toString(tt));
263     }
264 
265     @Test
266     public void testJ2000() {
267         Assertions.assertEquals("2000-01-01T12:00:00.000",
268                      AbsoluteDate.J2000_EPOCH.toString(TimeScalesFactory.getTT()));
269         Assertions.assertEquals("2000-01-01T11:59:27.816",
270                      AbsoluteDate.J2000_EPOCH.toString(TimeScalesFactory.getTAI()));
271         Assertions.assertEquals("2000-01-01T11:58:55.816",
272                      AbsoluteDate.J2000_EPOCH.toString(utc));
273     }
274 
275     @Test
276     public void testFraction() {
277         AbsoluteDate d =
278             new AbsoluteDate(new DateComponents(2000, 1, 1), new TimeComponents(11, 59, 27.816),
279                              TimeScalesFactory.getTAI());
280         Assertions.assertEquals(0, d.durationFrom(AbsoluteDate.J2000_EPOCH), 1.0e-10);
281     }
282 
283     @Test
284     public void testScalesOffset() {
285         AbsoluteDate date = new AbsoluteDate(new DateComponents(2006, 2, 24),
286                                              new TimeComponents(15, 38, 0),
287                                              utc);
288         Assertions.assertEquals(33,
289                      date.timeScalesOffset(TimeScalesFactory.getTAI(), utc),
290                      1.0e-10);
291     }
292 
293     @Test
294     public void testUTC() {
295         AbsoluteDate date = new AbsoluteDate(new DateComponents(2002, 1, 1),
296                                              new TimeComponents(0, 0, 1),
297                                              utc);
298         Assertions.assertEquals("2002-01-01T00:00:01.000Z", date.toString());
299     }
300 
301     @Test
302     public void test1970() {
303         AbsoluteDate date = new AbsoluteDate(new Date(0L), utc);
304         Assertions.assertEquals("1970-01-01T00:00:00.000Z", date.toString());
305     }
306 
307     @Test
308     public void test1970Instant() {
309         Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.EPOCH, utc).toString());
310         Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.ofEpochMilli(0L), utc).toString());
311         Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.EPOCH, (UTCScale) utc).toString());
312         Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.ofEpochMilli(0L), (UTCScale) utc).toString());
313     }
314 
315     @Test
316     public void testInstantAccuracy() {
317         Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(87400, 123456789), utc).toString());
318         Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(519000, 123456789), utc).toString());
319         Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(87400, 123456789), (UTCScale) utc).toString());
320         Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(519000, 123456789), (UTCScale) utc).toString());
321     }
322 
323     @Test
324     public void testToInstant() {
325         Assertions.assertEquals(Instant.ofEpochSecond(0), new AbsoluteDate("1970-01-01T00:00:00.000Z", utc).toInstant());
326         Assertions.assertEquals(Instant.ofEpochSecond(0), new AbsoluteDate("1970-01-01T00:00:00.000Z", utc).toInstant(TimeScalesFactory.getTimeScales()));
327 
328         Instant expectedInstant = Instant.ofEpochSecond(519000, 123456789);
329         Assertions.assertEquals(expectedInstant, new AbsoluteDate("1970-01-07T00:10:00.123456789Z", utc).toInstant());
330         Assertions.assertEquals(expectedInstant, new AbsoluteDate("1970-01-07T00:10:00.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales()));
331 
332         Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(),
333             new AbsoluteDate("2024-05-15T09:32:36.123456789Z", utc).toInstant());
334         Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(),
335             new AbsoluteDate("2024-05-15T09:32:36.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales()));
336     }
337 
338     @Test
339     public void testUtcGpsOffset() {
340         AbsoluteDate date1   = new AbsoluteDate(new DateComponents(2005, 8, 9),
341                                                 new TimeComponents(16, 31, 17),
342                                                 utc);
343         AbsoluteDate date2   = new AbsoluteDate(new DateComponents(2006, 8, 9),
344                                                 new TimeComponents(16, 31, 17),
345                                                 utc);
346         AbsoluteDate dateRef = new AbsoluteDate(new DateComponents(1980, 1, 6),
347                                                 TimeComponents.H00,
348                                                 utc);
349 
350         // 13 seconds offset between GPS time and UTC in 2005
351         long noLeapGap = ((9347 * 24 + 16) * 60 + 31) * 60 + 17;
352         long realGap   = (long) date1.durationFrom(dateRef);
353         Assertions.assertEquals(13L, realGap - noLeapGap);
354 
355         // 14 seconds offset between GPS time and UTC in 2006
356         noLeapGap = ((9712 * 24 + 16) * 60 + 31) * 60 + 17;
357         realGap   = (long) date2.durationFrom(dateRef);
358         Assertions.assertEquals(14L, realGap - noLeapGap);
359 
360     }
361 
362     @Test
363     public void testMJDDate() {
364         AbsoluteDate dateA = AbsoluteDate.createMJDDate(51544, 0.5 * Constants.JULIAN_DAY,
365                                                              TimeScalesFactory.getTT());
366         Assertions.assertEquals(0.0, AbsoluteDate.J2000_EPOCH.durationFrom(dateA), 1.0e-15);
367         AbsoluteDate dateB = AbsoluteDate.createMJDDate(53774, 0.0, TimeScalesFactory.getUTC());
368         AbsoluteDate dateC = new AbsoluteDate("2006-02-08T00:00:00", TimeScalesFactory.getUTC());
369         Assertions.assertEquals(0.0, dateC.durationFrom(dateB), 1.0e-15);
370     }
371 
372     @Test
373     public void testJDDate() {
374         final AbsoluteDate date = AbsoluteDate.createJDDate(2400000, 0.5 * Constants.JULIAN_DAY,
375                                                             TimeScalesFactory.getTT());
376         Assertions.assertEquals(0.0, AbsoluteDate.MODIFIED_JULIAN_EPOCH.durationFrom(date), 1.0e-15);
377     }
378 
379     /** Test issue 1310: get a date from a JD using a pivot timescale. */
380     @Test
381     public void testIssue1310JDDateInTDB() {
382         // Given
383         // -----
384         final TDBScale TDBscale = TimeScalesFactory.getTDB();
385         final AbsoluteDate refDate = new AbsoluteDate("2023-08-01T00:00:00.000", TDBscale);
386 
387         // When
388         // ----
389         final AbsoluteDate wrongDate  = AbsoluteDate.createJDDate(2460157,
390                 Constants.JULIAN_DAY / 2.0d, TDBscale);
391         final AbsoluteDate properDate = AbsoluteDate.createJDDate(2460157,
392                 Constants.JULIAN_DAY/2.0d, TDBscale, TimeScalesFactory.getTT());
393 
394         // Then
395         // ----
396 
397         // Wrong date is too far from reference date
398         Assertions.assertEquals(0.0, wrongDate.durationFrom(refDate), 1.270e-05);
399 
400         // Proper date is close enough from reference date
401         Assertions.assertEquals(0.0, properDate.durationFrom(refDate), 2.132e-13);
402     }
403 
404     @Test
405     public void testMedian() {
406         final AbsoluteDate date1 = new AbsoluteDate(2003, 6, 13, 14, 15,
407                                                     new TimeOffset(53, TimeOffset.SECOND, 12, TimeOffset.ATTOSECOND),
408                                                     TimeScalesFactory.getTT());
409         final AbsoluteDate date2 = new AbsoluteDate(2003, 6, 13, 14, 17,
410                                                     new TimeOffset(25, TimeOffset.SECOND, 120, TimeOffset.ATTOSECOND),
411                                                     TimeScalesFactory.getTT());
412         final AbsoluteDate dateM = new AbsoluteDate(2003, 6, 13, 14, 16,
413                                                  new TimeOffset(39, TimeOffset.SECOND, 66, TimeOffset.ATTOSECOND),
414                                                  TimeScalesFactory.getTT());
415         Assertions.assertEquals(dateM, AbsoluteDate.createMedian(date1, date2));
416         Assertions.assertEquals(dateM, AbsoluteDate.createMedian(date2, date1));
417     }
418 
419     @Test
420     public void testMedianInfinite() {
421         Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY,
422                                 AbsoluteDate.createMedian(AbsoluteDate.FUTURE_INFINITY,
423                                                           AbsoluteDate.ARBITRARY_EPOCH));
424         Assertions.assertEquals(AbsoluteDate.PAST_INFINITY,
425                                 AbsoluteDate.createMedian(AbsoluteDate.PAST_INFINITY,
426                                                           AbsoluteDate.ARBITRARY_EPOCH));
427     }
428 
429     @Test
430     public void testOffsets() {
431         final TimeScale tai = TimeScalesFactory.getTAI();
432         AbsoluteDate leapStartUTC = new AbsoluteDate(1976, 12, 31, 23, 59, 59, utc);
433         AbsoluteDate leapEndUTC   = new AbsoluteDate(1977,  1,  1,  0,  0,  0, utc);
434         AbsoluteDate leapStartTAI = new AbsoluteDate(1977,  1,  1,  0,  0, 14, tai);
435         AbsoluteDate leapEndTAI   = new AbsoluteDate(1977,  1,  1,  0,  0, 16, tai);
436         Assertions.assertEquals(leapStartUTC, leapStartTAI);
437         Assertions.assertEquals(leapEndUTC, leapEndTAI);
438         Assertions.assertEquals(1, leapEndUTC.offsetFrom(leapStartUTC, utc), 1.0e-10);
439         Assertions.assertEquals(1, leapEndTAI.offsetFrom(leapStartTAI, utc), 1.0e-10);
440         Assertions.assertEquals(2, leapEndUTC.offsetFrom(leapStartUTC, tai), 1.0e-10);
441         Assertions.assertEquals(2, leapEndTAI.offsetFrom(leapStartTAI, tai), 1.0e-10);
442         Assertions.assertEquals(2, leapEndUTC.durationFrom(leapStartUTC),    1.0e-10);
443         Assertions.assertEquals(2, leapEndTAI.durationFrom(leapStartTAI),    1.0e-10);
444     }
445 
446     @Test
447     public void testBeforeAndAfterLeap() {
448         final TimeScale tai = TimeScalesFactory.getTAI();
449         AbsoluteDate leapStart = new AbsoluteDate(1977,  1,  1,  0,  0, 14, tai);
450         AbsoluteDate leapEnd   = new AbsoluteDate(1977,  1,  1,  0,  0, 16, tai);
451         for (int i = -10; i < 10; ++i) {
452             final double dt = 1.1 * (2 * i - 1);
453             AbsoluteDate d1 = leapStart.shiftedBy(dt);
454             AbsoluteDate d2 = new AbsoluteDate(leapStart, dt, tai);
455             AbsoluteDate d3 = new AbsoluteDate(leapStart, dt, utc);
456             AbsoluteDate d4 = new AbsoluteDate(leapEnd,   dt, tai);
457             AbsoluteDate d5 = new AbsoluteDate(leapEnd,   dt, utc);
458             Assertions.assertTrue(FastMath.abs(d1.durationFrom(d2)) < 1.0e-10);
459             if (dt < 0) {
460                 Assertions.assertTrue(FastMath.abs(d2.durationFrom(d3)) < 1.0e-10);
461                 Assertions.assertTrue(d4.durationFrom(d5) > (1.0 - 1.0e-10));
462             } else {
463                 Assertions.assertTrue(d2.durationFrom(d3) < (-1.0 + 1.0e-10));
464                 Assertions.assertTrue(FastMath.abs(d4.durationFrom(d5)) < 1.0e-10);
465             }
466         }
467     }
468 
469     @Test
470     public void testSymmetry() {
471         final TimeScale tai = TimeScalesFactory.getTAI();
472         AbsoluteDate leapStart = new AbsoluteDate(1977,  1,  1,  0,  0, 14, tai);
473         for (int i = -10; i < 10; ++i) {
474             final double dt = 1.1 * (2 * i - 1);
475             Assertions.assertEquals(dt, new AbsoluteDate(leapStart, dt, utc).offsetFrom(leapStart, utc), 1.0e-10);
476             Assertions.assertEquals(dt, new AbsoluteDate(leapStart, dt, tai).offsetFrom(leapStart, tai), 1.0e-10);
477             Assertions.assertEquals(dt, leapStart.shiftedBy(dt).durationFrom(leapStart), 1.0e-10);
478         }
479     }
480 
481     @SuppressWarnings("unlikely-arg-type")
482     @Test
483     public void testEquals() {
484         AbsoluteDate d1 =
485             new AbsoluteDate(new DateComponents(2006, 2, 25),
486                              new TimeComponents(17, 10, 34),
487                              utc);
488         AbsoluteDate d2 = new AbsoluteDate(new DateComponents(2006, 2, 25),
489                                            new TimeComponents(17, 10, 0),
490                                            utc).shiftedBy(34);
491         Assertions.assertEquals(d1, d2);
492         Assertions.assertNotEquals(d1, this);
493     }
494 
495     @Test
496     public void testComponents() {
497         // this is NOT J2000.0,
498         // it is either a few seconds before or after depending on time scale
499         DateComponents date = new DateComponents(2000, 1, 1);
500         TimeComponents time = new TimeComponents(11, 59, 10);
501         TimeScale[] scales = {
502             TimeScalesFactory.getTAI(), TimeScalesFactory.getUTC(),
503             TimeScalesFactory.getTT(), TimeScalesFactory.getTCG()
504         };
505         for (int i = 0; i < scales.length; ++i) {
506             AbsoluteDate in = new AbsoluteDate(date, time, scales[i]);
507             for (int j = 0; j < scales.length; ++j) {
508                 DateTimeComponents pair = in.getComponents(scales[j]);
509                 if (i == j) {
510                     Assertions.assertEquals(date, pair.getDate());
511                     Assertions.assertEquals(time, pair.getTime());
512                 } else {
513                     Assertions.assertNotSame(date, pair.getDate());
514                     Assertions.assertNotSame(time, pair.getTime());
515                 }
516             }
517         }
518     }
519 
520     @Test
521     public void testMonth() {
522         TimeScale utc = TimeScalesFactory.getUTC();
523         Assertions.assertEquals(new AbsoluteDate(2011, 2, 23, utc),
524                             new AbsoluteDate(2011, Month.FEBRUARY, 23, utc));
525         Assertions.assertEquals(new AbsoluteDate(2011, 2, 23, 1, 2, 3.4, utc),
526                             new AbsoluteDate(2011, Month.FEBRUARY, 23, 1, 2, 3.4, utc));
527     }
528 
529     @Test
530     public void testCCSDSUnsegmentedNoExtension() {
531 
532         AbsoluteDate reference = new AbsoluteDate("2002-05-23T12:34:56.789", utc);
533         double lsb = FastMath.pow(2.0, -24);
534 
535         byte[] timeCCSDSEpoch = new byte[] { 0x53, 0x7F, 0x40, -0x70, -0x37, -0x05, -0x19 };
536         for (int preamble = 0x00; preamble < 0x80; ++preamble) {
537             if (preamble == 0x1F) {
538                 // using CCSDS reference epoch
539                 AbsoluteDate ccsds1 =
540                     AbsoluteDate.parseCCSDSUnsegmentedTimeCode((byte) preamble, (byte) 0x0, timeCCSDSEpoch, null);
541                 Assertions.assertEquals(0, ccsds1.durationFrom(reference), lsb / 2);
542             } else {
543                 try {
544                     AbsoluteDate.parseCCSDSUnsegmentedTimeCode((byte) preamble, (byte) 0x0, timeCCSDSEpoch, null);
545                     Assertions.fail("an exception should have been thrown");
546                 } catch (OrekitException iae) {
547                     // expected
548                 }
549 
550             }
551         }
552 
553         // missing epoch
554         byte[] timeJ2000Epoch = new byte[] { 0x04, 0x7E, -0x0B, -0x10, -0x07, 0x16, -0x79 };
555         try {
556             AbsoluteDate.parseCCSDSUnsegmentedTimeCode((byte) 0x2F, (byte) 0x0, timeJ2000Epoch, null);
557             Assertions.fail("an exception should have been thrown");
558         } catch (OrekitException iae) {
559             // expected
560         }
561 
562         // using J2000.0 epoch
563         AbsoluteDate ccsds3 =
564             AbsoluteDate.parseCCSDSUnsegmentedTimeCode((byte) 0x2F, (byte) 0x0, timeJ2000Epoch, AbsoluteDate.J2000_EPOCH);
565         Assertions.assertEquals(0, ccsds3.durationFrom(reference), lsb / 2);
566 
567     }
568 
569     @Test
570     public void testCCSDSUnsegmentedWithExtendedPreamble() {
571 
572         AbsoluteDate reference = new AbsoluteDate("2095-03-03T22:02:45.789012345678901", utc);
573         int leap = (int) FastMath.rint(utc.offsetFromTAI(reference).toDouble());
574 
575         byte extendedPreamble = (byte) -0x80;
576         byte identification   = (byte)  0x10;
577         byte coarseLength1    = (byte)  0x0C; // four (3 + 1) bytes
578         byte fineLength1      = (byte)  0x03; // 3 bytes
579         byte coarseLength2    = (byte)  0x20; // 1 additional byte for coarse time
580         byte fineLength2      = (byte)  0x10; // 4 additional bytes for fine time
581         byte[] timeCCSDSEpoch = new byte[] {
582              0x01,  0x02,  0x03,  0x04,  (byte)(0x05 - leap), // 5 bytes for coarse time (seconds)
583             -0x37, -0x04, -0x4A, -0x74, -0x2C, -0x3C, -0x48   // 7 bytes for fine time (sub-seconds)
584         };
585         byte preamble1 = (byte) (extendedPreamble | identification | coarseLength1 | fineLength1);
586         byte preamble2 = (byte) (coarseLength2 | fineLength2);
587         AbsoluteDate ccsds1 =
588                 AbsoluteDate.parseCCSDSUnsegmentedTimeCode(preamble1, preamble2, timeCCSDSEpoch, null);
589 
590         // The 8 attoseconds difference comes from the fact unsegmented time is
591         // in powers of 1/256 s, so it is not a whole number of attoseconds
592         Assertions.assertEquals(-8.0e-18, ccsds1.durationFrom(reference), 1.0e-18);
593 
594     }
595 
596     @Test
597     public void testCCSDSDaySegmented() {
598 
599         AbsoluteDate reference = new AbsoluteDate("2002-05-23T12:34:56.789012345678", TimeScalesFactory.getUTC());
600         double lsb = 1.0e-13;
601         byte[] timeCCSDSEpoch = new byte[] { 0x3F, 0x55, 0x02, -0x4D, 0x2C, -0x6B, 0x00, -0x44, 0x61, 0x4E };
602 
603         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
604             if (preamble == 0x42) {
605                 // using CCSDS reference epoch
606                 AbsoluteDate ccsds1 =
607                     AbsoluteDate.parseCCSDSDaySegmentedTimeCode((byte) preamble, timeCCSDSEpoch, null);
608                 Assertions.assertEquals(0, ccsds1.durationFrom(reference), lsb / 2);
609             } else {
610                 try {
611                     AbsoluteDate.parseCCSDSDaySegmentedTimeCode((byte) preamble, timeCCSDSEpoch, null);
612                     Assertions.fail("an exception should have been thrown");
613                 } catch (OrekitException iae) {
614                     // expected
615                 }
616 
617             }
618         }
619 
620         // missing epoch
621         byte[] timeJ2000Epoch = new byte[] { 0x03, 0x69, 0x02, -0x4D, 0x2C, -0x6B, 0x00, -0x44, 0x61, 0x4E };
622         try {
623             AbsoluteDate.parseCCSDSDaySegmentedTimeCode((byte) 0x4A, timeJ2000Epoch, null);
624             Assertions.fail("an exception should have been thrown");
625         } catch (OrekitException iae) {
626             // expected
627         }
628 
629         // using J2000.0 epoch
630         AbsoluteDate ccsds3 =
631             AbsoluteDate.parseCCSDSDaySegmentedTimeCode((byte) 0x4A, timeJ2000Epoch, DateComponents.J2000_EPOCH);
632         Assertions.assertEquals(0, ccsds3.durationFrom(reference), lsb / 2);
633 
634         // limit to microsecond
635         byte[] timeMicrosecond = new byte[] { 0x03, 0x69, 0x02, -0x4D, 0x2C, -0x6B, 0x00, 0x0C };
636         AbsoluteDate ccsds4 =
637             AbsoluteDate.parseCCSDSDaySegmentedTimeCode((byte) 0x49, timeMicrosecond, DateComponents.J2000_EPOCH);
638         Assertions.assertEquals(-0.345678e-6, ccsds4.durationFrom(reference), lsb / 2);
639 
640     }
641 
642     @Test
643     public void testCCSDSCalendarSegmented() {
644 
645         AbsoluteDate reference = new AbsoluteDate("2002-05-23T12:34:56.789012345678", TimeScalesFactory.getUTC());
646         double lsb = 1.0e-13;
647 
648         // month of year / day of month variation
649         byte[] timeMonthDay = new byte[] { 0x07, -0x2E, 0x05, 0x17, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C, 0x22, 0x38, 0x4E };
650         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
651             if (preamble == 0x56) {
652                 AbsoluteDate ccsds1 =
653                     AbsoluteDate.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeMonthDay);
654                 Assertions.assertEquals(0, ccsds1.durationFrom(reference), lsb / 2);
655             } else {
656                 try {
657                     AbsoluteDate.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeMonthDay);
658                     Assertions.fail("an exception should have been thrown");
659                 } catch (OrekitException iae) {
660                     // expected
661                 } catch (IllegalArgumentException iae) {
662                     // should happen when preamble specifies day of year variation
663                     // since there is no day 1303 (= 5 * 256 + 23) in any year ...
664                     Assertions.assertEquals(preamble & 0x08, 0x08);
665                 }
666 
667             }
668         }
669 
670         // day of year variation
671         byte[] timeDay = new byte[] { 0x07, -0x2E, 0x00, -0x71, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C, 0x22, 0x38, 0x4E };
672         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
673             if (preamble == 0x5E) {
674                 AbsoluteDate ccsds1 =
675                     AbsoluteDate.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeDay);
676                 Assertions.assertEquals(0, ccsds1.durationFrom(reference), lsb / 2);
677             } else {
678                 try {
679                     AbsoluteDate.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeDay);
680                     Assertions.fail("an exception should have been thrown");
681                 } catch (OrekitException iae) {
682                     // expected
683                 } catch (IllegalArgumentException iae) {
684                     // should happen when preamble specifies month of year / day of month variation
685                     // since there is no month 0 in any year ...
686                     Assertions.assertEquals(preamble & 0x08, 0x00);
687                 }
688 
689             }
690         }
691 
692         // limit to microsecond
693         byte[] timeMicrosecond = new byte[] { 0x07, -0x2E, 0x00, -0x71, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C };
694         AbsoluteDate ccsds4 =
695             AbsoluteDate.parseCCSDSCalendarSegmentedTimeCode((byte) 0x5B, timeMicrosecond);
696         Assertions.assertEquals(-0.345678e-6, ccsds4.durationFrom(reference), lsb / 2);
697 
698     }
699 
700     @Test
701     public void testExpandedConstructors() {
702         Assertions.assertThrows(IllegalArgumentException.class, () -> {
703             Assertions.assertEquals(new AbsoluteDate(new DateComponents(2002, 5, 28),
704                             new TimeComponents(15, 30, 0),
705                             TimeScalesFactory.getUTC()),
706                     new AbsoluteDate(2002, 5, 28, 15, 30, 0, TimeScalesFactory.getUTC()));
707             Assertions.assertEquals(new AbsoluteDate(new DateComponents(2002, 5, 28), TimeComponents.H00,
708                             TimeScalesFactory.getUTC()),
709                     new AbsoluteDate(2002, 5, 28, TimeScalesFactory.getUTC()));
710             new AbsoluteDate(2002, 5, 28, 25, 30, 0, TimeScalesFactory.getUTC());
711         });
712     }
713 
714     @Test
715     public void testHashcode() {
716         AbsoluteDate d1 =
717             new AbsoluteDate(new DateComponents(2006, 2, 25),
718                              new TimeComponents(17, 10, 34),
719                              utc);
720         AbsoluteDate d2 = new AbsoluteDate(new DateComponents(2006, 2, 25),
721                                            new TimeComponents(17, 10, 0),
722                                            utc).shiftedBy(34);
723         Assertions.assertEquals(d1.hashCode(), d2.hashCode());
724         Assertions.assertTrue(d1.hashCode() != d1.shiftedBy(1.0e-3).hashCode());
725     }
726 
727     @Test
728     public void testInfinity() {
729         Assertions.assertTrue(AbsoluteDate.JULIAN_EPOCH.compareTo(AbsoluteDate.PAST_INFINITY) > 0);
730         Assertions.assertTrue(AbsoluteDate.JULIAN_EPOCH.compareTo(AbsoluteDate.FUTURE_INFINITY) < 0);
731         Assertions.assertTrue(AbsoluteDate.J2000_EPOCH.compareTo(AbsoluteDate.PAST_INFINITY) > 0);
732         Assertions.assertTrue(AbsoluteDate.J2000_EPOCH.compareTo(AbsoluteDate.FUTURE_INFINITY) < 0);
733         Assertions.assertTrue(AbsoluteDate.PAST_INFINITY.compareTo(AbsoluteDate.PAST_INFINITY) == 0);
734         Assertions.assertTrue(AbsoluteDate.PAST_INFINITY.compareTo(AbsoluteDate.JULIAN_EPOCH) < 0);
735         Assertions.assertTrue(AbsoluteDate.PAST_INFINITY.compareTo(AbsoluteDate.J2000_EPOCH) < 0);
736         Assertions.assertTrue(AbsoluteDate.PAST_INFINITY.compareTo(AbsoluteDate.FUTURE_INFINITY) < 0);
737         Assertions.assertTrue(AbsoluteDate.FUTURE_INFINITY.compareTo(AbsoluteDate.JULIAN_EPOCH) > 0);
738         Assertions.assertTrue(AbsoluteDate.FUTURE_INFINITY.compareTo(AbsoluteDate.J2000_EPOCH) > 0);
739         Assertions.assertTrue(AbsoluteDate.FUTURE_INFINITY.compareTo(AbsoluteDate.PAST_INFINITY) > 0);
740         Assertions.assertTrue(AbsoluteDate.FUTURE_INFINITY.compareTo(AbsoluteDate.FUTURE_INFINITY) == 0);
741         Assertions.assertTrue(Double.isInfinite(AbsoluteDate.FUTURE_INFINITY.durationFrom(AbsoluteDate.J2000_EPOCH)));
742         Assertions.assertTrue(Double.isInfinite(AbsoluteDate.FUTURE_INFINITY.durationFrom(AbsoluteDate.PAST_INFINITY)));
743         Assertions.assertTrue(Double.isInfinite(AbsoluteDate.PAST_INFINITY.durationFrom(AbsoluteDate.J2000_EPOCH)));
744         Assertions.assertTrue(Double.isNaN(AbsoluteDate.FUTURE_INFINITY.durationFrom(AbsoluteDate.FUTURE_INFINITY)));
745         Assertions.assertTrue(Double.isNaN(AbsoluteDate.PAST_INFINITY.durationFrom(AbsoluteDate.PAST_INFINITY)));
746         Assertions.assertEquals("5881610-07-11T23:59:59.999Z",  AbsoluteDate.FUTURE_INFINITY.toString());
747         Assertions.assertEquals("-5877490-03-03T00:00:00.000Z", AbsoluteDate.PAST_INFINITY.toString());
748         Assertions.assertEquals(true, AbsoluteDate.FUTURE_INFINITY.equals(AbsoluteDate.FUTURE_INFINITY));
749         Assertions.assertEquals(true, AbsoluteDate.PAST_INFINITY.equals(AbsoluteDate.PAST_INFINITY));
750         Assertions.assertEquals(false, AbsoluteDate.PAST_INFINITY.equals(AbsoluteDate.FUTURE_INFINITY));
751         Assertions.assertEquals(false, AbsoluteDate.FUTURE_INFINITY.equals(AbsoluteDate.PAST_INFINITY));
752 
753         Assertions.assertTrue(AbsoluteDate.J2000_EPOCH.durationFrom(AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(Double.NEGATIVE_INFINITY))
754                           == Double.POSITIVE_INFINITY);
755         Assertions.assertTrue(AbsoluteDate.J2000_EPOCH.durationFrom(AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(Double.POSITIVE_INFINITY))
756                           == Double.NEGATIVE_INFINITY);
757 
758     }
759 
760     @Test
761     public void testCompareTo() {
762         // check long time spans
763         AbsoluteDate epoch =
764                 new AbsoluteDate(2000, 1, 1, 12, 0, 0, TimeScalesFactory.getTAI());
765         Assertions.assertTrue(AbsoluteDate.JULIAN_EPOCH.compareTo(epoch) < 0);
766         Assertions.assertTrue(epoch.compareTo(AbsoluteDate.JULIAN_EPOCH) > 0);
767         // check short time spans
768         AbsoluteDate d = epoch;
769         double epsilon = 1.0 - FastMath.nextDown(1.0);
770         Assertions.assertTrue(d.compareTo(d.shiftedBy(epsilon)) < 0);
771         Assertions.assertTrue(d.compareTo(d.shiftedBy(0)) == 0);
772         Assertions.assertTrue(d.compareTo(d.shiftedBy(-epsilon)) > 0);
773         // check date with negative offset
774         d = epoch.shiftedBy(496891466)
775                 .shiftedBy(0.7320114066633323)
776                 .shiftedBy(-19730.732011406664);
777         // offset is 0 in d1
778         AbsoluteDate d1 = epoch.shiftedBy(496891466 - 19730);
779         Assertions.assertTrue(d.compareTo(d1) < 0);
780         // decrement epoch, now offset is 0.999... in d1
781         d1 = d1.shiftedBy(-1e-16);
782         Assertions.assertTrue(d.compareTo(d1) < 0,"" + d.durationFrom(d1));
783         // check large dates
784         // these tests fail due to long overflow in durationFrom() Bug #584
785         // d = new AbsoluteDate(epoch, Long.MAX_VALUE);
786         // Assertions.assertEquals(-1, epoch.compareTo(d));
787         // Assertions.assertTrue(d.compareTo(AbsoluteDate.FUTURE_INFINITY) < 0);
788         // d = new AbsoluteDate(epoch, Long.MIN_VALUE);
789         // Assertions.assertTrue(epoch.compareTo(d) > 0);
790         // Assertions.assertTrue(d.compareTo(AbsoluteDate.PAST_INFINITY) > 0);
791     }
792 
793     @Test
794     public void testIsEqualTo() {
795         Assertions.assertTrue(present.isEqualTo(present));
796         Assertions.assertTrue(present.isEqualTo(presentToo));
797         Assertions.assertFalse(present.isEqualTo(past));
798         Assertions.assertFalse(present.isEqualTo(future));
799     }
800 
801     @Test
802     public void testIsCloseTo() {
803         double tolerance = 10;
804         TimeStamped closeToPresent = new AnyTimeStamped(present.shiftedBy(5));
805         Assertions.assertTrue(present.isCloseTo(present, tolerance));
806         Assertions.assertTrue(present.isCloseTo(presentToo, tolerance));
807         Assertions.assertTrue(present.isCloseTo(closeToPresent, tolerance));
808         Assertions.assertFalse(present.isCloseTo(past, tolerance));
809         Assertions.assertFalse(present.isCloseTo(future, tolerance));
810     }
811 
812     @Test
813     public void testIsBefore() {
814         Assertions.assertFalse(present.isBefore(past));
815         Assertions.assertFalse(present.isBefore(present));
816         Assertions.assertFalse(present.isBefore(presentToo));
817         Assertions.assertTrue(present.isBefore(future));
818     }
819 
820     @Test
821     public void testIsAfter() {
822         Assertions.assertTrue(present.isAfter(past));
823         Assertions.assertFalse(present.isAfter(present));
824         Assertions.assertFalse(present.isAfter(presentToo));
825         Assertions.assertFalse(present.isAfter(future));
826     }
827 
828     @Test
829     public void testIsBeforeOrEqualTo() {
830         Assertions.assertFalse(present.isBeforeOrEqualTo(past));
831         Assertions.assertTrue(present.isBeforeOrEqualTo(present));
832         Assertions.assertTrue(present.isBeforeOrEqualTo(presentToo));
833         Assertions.assertTrue(present.isBeforeOrEqualTo(future));
834     }
835 
836     @Test
837     public void testIsAfterOrEqualTo() {
838         Assertions.assertTrue(present.isAfterOrEqualTo(past));
839         Assertions.assertTrue(present.isAfterOrEqualTo(present));
840         Assertions.assertTrue(present.isAfterOrEqualTo(presentToo));
841         Assertions.assertFalse(present.isAfterOrEqualTo(future));
842     }
843 
844     @Test
845     public void testIsBetween() {
846         Assertions.assertTrue(present.isBetween(past, future));
847         Assertions.assertTrue(present.isBetween(future, past));
848         Assertions.assertFalse(past.getDate().isBetween(present, future));
849         Assertions.assertFalse(past.getDate().isBetween(future, present));
850         Assertions.assertFalse(future.getDate().isBetween(past, present));
851         Assertions.assertFalse(future.getDate().isBetween(present, past));
852         Assertions.assertFalse(present.isBetween(present, future));
853         Assertions.assertFalse(present.isBetween(past, present));
854         Assertions.assertFalse(present.isBetween(past, past));
855         Assertions.assertFalse(present.isBetween(present, present));
856         Assertions.assertFalse(present.isBetween(present, presentToo));
857     }
858 
859     @Test
860     public void testIsBetweenOrEqualTo() {
861         Assertions.assertTrue(present.isBetweenOrEqualTo(past, future));
862         Assertions.assertTrue(present.isBetweenOrEqualTo(future, past));
863         Assertions.assertFalse(past.getDate().isBetweenOrEqualTo(present, future));
864         Assertions.assertFalse(past.getDate().isBetweenOrEqualTo(future, present));
865         Assertions.assertFalse(future.getDate().isBetweenOrEqualTo(past, present));
866         Assertions.assertFalse(future.getDate().isBetweenOrEqualTo(present, past));
867         Assertions.assertTrue(present.isBetweenOrEqualTo(present, future));
868         Assertions.assertTrue(present.isBetweenOrEqualTo(past, present));
869         Assertions.assertFalse(present.isBetweenOrEqualTo(past, past));
870         Assertions.assertTrue(present.isBetweenOrEqualTo(present, present));
871         Assertions.assertTrue(present.isBetweenOrEqualTo(present, presentToo));
872     }
873 
874     @Test
875     public void testAccuracy() {
876         TimeScale tai = TimeScalesFactory.getTAI();
877         double sec = 0.281;
878         AbsoluteDate t = new AbsoluteDate(2010, 6, 21, 18, 42, sec, tai);
879         double recomputedSec = t.getComponents(tai).getTime().getSecond();
880         Assertions.assertEquals(sec, recomputedSec, FastMath.ulp(sec));
881     }
882 
883     @Test
884     public void testShiftPastInfinity() {
885         AbsoluteDate shifted = AbsoluteDate.PAST_INFINITY.shiftedBy(Constants.JULIAN_DAY);
886         Assertions.assertEquals(AbsoluteDate.PAST_INFINITY.getSeconds(), shifted.getSeconds());
887         Assertions.assertEquals(AbsoluteDate.PAST_INFINITY.getAttoSeconds(), shifted.getAttoSeconds());
888     }
889 
890     @Test
891     public void testShiftFutureInfinity() {
892         AbsoluteDate shifted = AbsoluteDate.FUTURE_INFINITY.shiftedBy(Constants.JULIAN_DAY);
893         Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY.getSeconds(), shifted.getSeconds());
894         Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY.getAttoSeconds(), shifted.getAttoSeconds());
895     }
896 
897     @Test
898     public void testSubFemtoSecondPositiveShift() {
899         TimeScale tai = TimeScalesFactory.getTAI();
900         AbsoluteDate since = new AbsoluteDate(2008, 4, 7, 0, 53, 0.0078125, tai);
901         double deltaT = 1.0e-17;
902         AbsoluteDate shifted = since.shiftedBy(deltaT);
903         Assertions.assertEquals(deltaT, shifted.durationFrom(since), 1.0e-25);
904     }
905 
906     @Test
907     public void testSubFemtoSecondNegativeShift() {
908         TimeScale tai = TimeScalesFactory.getTAI();
909         AbsoluteDate since = new AbsoluteDate(2008, 4, 7, 0, 53, 0.0078125, tai);
910         double deltaT = -1.0e-17;
911         AbsoluteDate shifted = since.shiftedBy(deltaT);
912         Assertions.assertEquals(deltaT, shifted.durationFrom(since), 1.0e-25);
913     }
914 
915     @Test
916     public void testIterationAccuracy() {
917 
918         final TimeScale tai = TimeScalesFactory.getTAI();
919         final AbsoluteDate t0 = new AbsoluteDate(2010, 6, 21, 18, 42, 0.281, tai);
920 
921         // 0.1 is not representable exactly in double precision
922         // we will accumulate error, between -0.5ULP and -3ULP at each iteration
923         checkIteration(0.1, t0, 10000, 3.0, -0.3874, 1.0e-4);
924 
925         // 0.125 is representable exactly in double precision
926         // error will be null
927         checkIteration(0.125, t0, 10000, 1.0e-15, 0.0, 1.0e-15);
928 
929     }
930 
931     private void checkIteration(final double step, final AbsoluteDate t0, final int nMax,
932                                 final double maxErrorFactor,
933                                 final double expectedMean, final double meanTolerance) {
934         final double epsilon = FastMath.ulp(step);
935         AbsoluteDate iteratedDate = t0;
936         double mean = 0;
937         for (int i = 1; i < nMax; ++i) {
938             iteratedDate = iteratedDate.shiftedBy(step);
939             AbsoluteDate directDate = t0.shiftedBy(i * step);
940             final double error = iteratedDate.durationFrom(directDate);
941             mean += error / (i * epsilon);
942             Assertions.assertEquals(0.0, iteratedDate.durationFrom(directDate), maxErrorFactor * i * epsilon);
943         }
944         mean /= nMax;
945         Assertions.assertEquals(expectedMean, mean, meanTolerance);
946     }
947 
948     @Test
949     public void testIssue142() {
950 
951         final AbsoluteDate epoch = AbsoluteDate.JAVA_EPOCH;
952         final TimeScale utc = TimeScalesFactory.getUTC();
953 
954         Assertions.assertEquals("1970-01-01T00:00:00.000", epoch.toString(utc));
955         Assertions.assertEquals(0.0, epoch.durationFrom(new AbsoluteDate(1970, 1, 1, utc)), 1.0e-15);
956         Assertions.assertEquals(8.000082,
957                             epoch.durationFrom(new AbsoluteDate(DateComponents.JAVA_EPOCH, TimeScalesFactory.getTAI())),
958                             1.0e-15);
959 
960         // April 1, 2006, in UTC
961         final TimeOffset offset = new TimeOffset(1143849600L, 0L);
962         final AbsoluteDate ad = new AbsoluteDate(epoch, offset, TimeScalesFactory.getUTC());
963         Assertions.assertEquals("2006-04-01T00:00:00.000", ad.toString(utc));
964 
965     }
966 
967     @Test
968     public void testIssue148() {
969         final TimeScale utc = TimeScalesFactory.getUTC();
970         AbsoluteDate t0 = new AbsoluteDate(2012, 6, 30, 23, 59, 50.0, utc);
971         DateTimeComponents components = t0.shiftedBy(11.0 - 200 * Precision.EPSILON).getComponents(utc);
972         Assertions.assertEquals(2012, components.getDate().getYear());
973         Assertions.assertEquals(   6, components.getDate().getMonth());
974         Assertions.assertEquals(  30, components.getDate().getDay());
975         Assertions.assertEquals(  23, components.getTime().getHour());
976         Assertions.assertEquals(  59, components.getTime().getMinute());
977         Assertions.assertEquals(  61 - 200 * Precision.EPSILON,
978                             components.getTime().getSecond(), 1.0e-15);
979     }
980 
981     @Test
982     public void testIssue149() {
983         final TimeScale utc = TimeScalesFactory.getUTC();
984         AbsoluteDate t0 = new AbsoluteDate(2012, 6, 30, 23, 59, 59, utc);
985         DateTimeComponents components = t0.shiftedBy(1.0 - Precision.EPSILON).getComponents(utc);
986         Assertions.assertEquals(2012, components.getDate().getYear());
987         Assertions.assertEquals(   6, components.getDate().getMonth());
988         Assertions.assertEquals(  30, components.getDate().getDay());
989         Assertions.assertEquals(  23, components.getTime().getHour());
990         Assertions.assertEquals(  59, components.getTime().getMinute());
991         Assertions.assertEquals(  60 - Precision.EPSILON,  // misleading as 60.0 - eps = 60.0
992                             components.getTime().getSecond(), 1.0e-15);
993     }
994 
995     @Test
996     public void testWrapAtMinuteEnd() {
997         TimeScale tai = TimeScalesFactory.getTAI();
998         TimeScale utc = TimeScalesFactory.getUTC();
999         AbsoluteDate date0 = new AbsoluteDate(DateComponents.J2000_EPOCH, TimeComponents.H12, tai);
1000         AbsoluteDate ref = date0.shiftedBy(new TimeOffset(496891466L, 732011406663332300L));
1001         AbsoluteDate date = ref.shiftedBy(new TimeOffset(597L, 900970042626200000L).negate().multiply(33));
1002         DateTimeComponents dtc = date.getComponents(utc);
1003         Assertions.assertEquals(                2015,  dtc.getDate().getYear());
1004         Assertions.assertEquals(                   9,  dtc.getDate().getMonth());
1005         Assertions.assertEquals(                  30,  dtc.getDate().getDay());
1006         Assertions.assertEquals(                   7,  dtc.getTime().getHour());
1007         Assertions.assertEquals(                  54,  dtc.getTime().getMinute());
1008         Assertions.assertEquals(                  59L, dtc.getTime().getSplitSecond().getSeconds());
1009         Assertions.assertEquals(  999999999998732300L, dtc.getTime().getSplitSecond().getAttoSeconds());
1010         Assertions.assertEquals("2015-09-30T07:54:59.9999999999987323", date.toString(utc));
1011         AbsoluteDate beforeMidnight = new AbsoluteDate(2008, 2, 29, 23, 59, new TimeOffset(59L, 999400000000000000L), utc);
1012         AbsoluteDate stillBeforeMidnight = beforeMidnight.shiftedBy(new TimeOffset(0L, 200000000000000L));
1013         Assertions.assertEquals(59.9994, beforeMidnight.getComponents(utc).getTime().getSecond(), 1.0e-15);
1014         Assertions.assertEquals(59.9996, stillBeforeMidnight.getComponents(utc).getTime().getSecond(), 1.0e-15);
1015         Assertions.assertEquals("2008-02-29T23:59:59.9994", beforeMidnight.toString(utc));
1016         Assertions.assertEquals("2008-02-29T23:59:59.9996", stillBeforeMidnight.toString(utc));
1017     }
1018 
1019 
1020     @Test
1021     public void testLastLeapOutput() {
1022         UTCScale utc = TimeScalesFactory.getUTC();
1023         AbsoluteDate t = utc.getLastKnownLeapSecond();
1024         Assertions.assertEquals("23:59:59.500", t.shiftedBy(-0.5).toString(utc).substring(11));
1025         Assertions.assertEquals("23:59:60.000", t.shiftedBy( 0.0).toString(utc).substring(11));
1026         Assertions.assertEquals("23:59:60.500", t.shiftedBy(+0.5).toString(utc).substring(11));
1027     }
1028 
1029     @Test
1030     public void testWrapBeforeLeap() {
1031         UTCScale utc = TimeScalesFactory.getUTC();
1032         AbsoluteDate t = new AbsoluteDate("2015-06-30T23:59:59.999999", utc);
1033         Assertions.assertEquals(2015,        t.getComponents(utc).getDate().getYear());
1034         Assertions.assertEquals(   6,        t.getComponents(utc).getDate().getMonth());
1035         Assertions.assertEquals(  30,        t.getComponents(utc).getDate().getDay());
1036         Assertions.assertEquals(  23,        t.getComponents(utc).getTime().getHour());
1037         Assertions.assertEquals(  59,        t.getComponents(utc).getTime().getMinute());
1038         Assertions.assertEquals(  59.999999, t.getComponents(utc).getTime().getSecond(), 1.0e-6);
1039         Assertions.assertEquals("2015-06-30T23:59:59.999999", t.toStringWithoutUtcOffset(utc, 6));
1040         Assertions.assertEquals("2015-07-01T02:59:59.999999", t.toStringWithoutUtcOffset(TimeScalesFactory.getGLONASS(), 6));
1041     }
1042 
1043     @Test
1044     public void testMjdInLeap() {
1045         // inside a leap second
1046         AbsoluteDate date1 = new AbsoluteDate(2008, 12, 31, 23, 59, 60.5, utc);
1047 
1048         // check date to MJD conversion
1049         DateTimeComponents date1Components = date1.getComponents(utc);
1050         int mjd = date1Components.getDate().getMJD();
1051         double seconds = date1Components.getTime().getSecondsInUTCDay();
1052         Assertions.assertEquals(54831, mjd);
1053         Assertions.assertEquals(86400.5, seconds, 0);
1054 
1055         // check MJD to date conversion
1056         AbsoluteDate date2 = AbsoluteDate.createMJDDate(mjd, seconds, utc);
1057         Assertions.assertEquals(date1, date2);
1058 
1059         // check we still detect seconds overflow
1060         try {
1061             AbsoluteDate.createMJDDate(mjd, seconds + 1.0, utc);
1062             Assertions.fail("an exception should have been thrown");
1063         } catch (OrekitIllegalArgumentException oiae) {
1064             Assertions.assertEquals(OrekitMessages.OUT_OF_RANGE_SECONDS_NUMBER_DETAIL, oiae.getSpecifier());
1065             Assertions.assertEquals(86401.5, (Double) oiae.getParts()[0], 0);
1066             Assertions.assertEquals(0, ((Number) oiae.getParts()[1]).doubleValue(), 0);
1067             Assertions.assertEquals(86401, ((Number) oiae.getParts()[2]).doubleValue(), 0);
1068         }
1069 
1070     }
1071 
1072     @Test
1073     public void testIssueTimesStampAccuracy() {
1074         String testString = "2019-02-01T13:06:03.115";
1075         TimeScale timeScale=TimeScalesFactory.getUTC();
1076 
1077         DateTimeComponents expectedComponent = DateTimeComponents.parseDateTime(testString);
1078         AbsoluteDate expectedDate = new AbsoluteDate(expectedComponent, timeScale);
1079 
1080         ZonedDateTime actualComponent = LocalDateTime.from(DateTimeFormatter.ISO_DATE_TIME.parse(testString)).atZone(ZoneOffset.UTC);
1081         AbsoluteDate actualDate = new AbsoluteDate(Timestamp.from(actualComponent.toInstant()), timeScale);
1082         Assertions.assertEquals(0.0, expectedDate.durationFrom(actualDate), 1.0e-15);
1083 
1084     }
1085 
1086     @Test
1087     public void testGetComponentsIssue681and676and694() {
1088         // setup
1089         AbsoluteDate date = new AbsoluteDate(2009, 1, 1, utc);
1090         double attoSecond = 1.0e-18;
1091         double zeroUlp = FastMath.nextUp(0.0);
1092         double oneUlp = FastMath.ulp(1.0);
1093         double sixtyUlp = FastMath.ulp(60.0);
1094         double one = FastMath.nextDown(1.0);
1095         double sixty = FastMath.nextDown(60.0);
1096         double sixtyOne = FastMath.nextDown(61.0);
1097 
1098         // actions + verify
1099         // translate back to AbsoluteDate has up to half an ULP of error,
1100         // except when truncated when the error can be up to 1 ULP.
1101         check(date, 2009, 1, 1, 0, 0, 0, 1, 0, 0);
1102         check(date.shiftedBy(attoSecond), 2009, 1, 1, 0, 0, attoSecond, 0.5, 0, 0);
1103         check(date.shiftedBy(one), 2009, 1, 1, 0, 0, one, 0.5, 0, 0);
1104         // I could also see rounding to a valid time as being reasonable here
1105         check(date.shiftedBy(59).shiftedBy(one), 2009, 1, 1, 0, 0, sixty, 1, 1, 0);
1106         check(date.shiftedBy(86399).shiftedBy(one), 2009, 1, 1, 23, 59, sixty, 1, 1, 0);
1107         check(date.shiftedBy(-zeroUlp), 2009, 1, 1, 0, 0, 0, 0.5, 0, 0);
1108         check(date.shiftedBy(-oneUlp), 2008, 12, 31, 23, 59, sixtyOne, 1, 1, 0);
1109         check(date.shiftedBy(-1).shiftedBy(zeroUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1110         check(date.shiftedBy(-1).shiftedBy(-zeroUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1111         check(date.shiftedBy(-1).shiftedBy(-oneUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1112         check(date.shiftedBy(-1).shiftedBy(-sixtyUlp), 2008, 12, 31, 23, 59, sixty, 0.5, 0, 0);
1113         check(date.shiftedBy(-61).shiftedBy(attoSecond), 2008, 12, 31, 23, 59, attoSecond, 0.5, 0, 0);
1114 
1115         // check UTC weirdness.
1116         // These have more error because of additional multiplications and additions
1117         // up to 2 ULPs or ulp(60.0) of error.
1118         AbsoluteDate d = new AbsoluteDate(1966, 1, 1, utc);
1119         double ratePost = 0.0025920 / Constants.JULIAN_DAY;
1120         double factorPost = ratePost / (1 + ratePost);
1121         double ratePre = 0.0012960 / Constants.JULIAN_DAY;
1122         double factorPre = ratePre / (1 + ratePre);
1123         check(d, 1966, 1, 1, 0, 0, 0, 1, 0, 0);
1124         check(d.shiftedBy(zeroUlp), 1966, 1, 1, 0, 0, 0, 0.5, 0, 0);
1125         check(d.shiftedBy(attoSecond), 1966, 1, 1, 0, 0, attoSecond, 0.5, 0, 0);
1126         check(d.shiftedBy(one), 1966, 1, 1, 0, 0, one * (1 - factorPost), 1, 3, 0);
1127         check(d.shiftedBy(59).shiftedBy(one), 1966, 1, 1, 0, 0, sixty * (1 - factorPost), 1, 2, 0);
1128         check(d.shiftedBy(86399).shiftedBy(one), 1966, 1, 1, 23, 59, sixty - 86400 * factorPost, 1, 1, 0);
1129         check(d.shiftedBy(-zeroUlp), 1966, 1, 1, 0, 0, 0, 0.5, 0, 0);
1130         // actual leap is small ~1e-16, but during a leap rounding up to 60.0 is ok
1131         check(d.shiftedBy(-oneUlp), 1965, 12, 31, 23, 59, 60.0, 1, 0, 0);
1132         check(d.shiftedBy(-1).shiftedBy(zeroUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1133         check(d.shiftedBy(-1).shiftedBy(-zeroUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1134         check(d.shiftedBy(-1).shiftedBy(-oneUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1135         check(d.shiftedBy(-1).shiftedBy(-sixtyUlp), 1965, 12, 31, 23, 59, 59 + (1 + sixtyUlp) * factorPre, 0.5, 1, 0);
1136         // since second ~= 0 there is significant cancellation
1137         check(d.shiftedBy(-60).shiftedBy(zeroUlp), 1965, 12, 31, 23, 59, 60 * factorPre, 0, 0, sixtyUlp);
1138         check(d.shiftedBy(-60).shiftedBy(oneUlp), 1965, 12, 31, 23, 59, (oneUlp - oneUlp * factorPre) + 60 * factorPre, 0.5, 0, sixtyUlp);
1139 
1140         // check first whole second leap
1141         AbsoluteDate d2 = new AbsoluteDate(1972, 7, 1, utc);
1142         check(d2, 1972, 7, 1, 0, 0, 0, 1, 0, 0);
1143         check(d2.shiftedBy(attoSecond), 1972, 7, 1, 0, 0, attoSecond, 0.5, 0, 0);
1144         check(d2.shiftedBy(one), 1972, 7, 1, 0, 0, one, 0.5, 0, 0);
1145         check(d2.shiftedBy(59).shiftedBy(one), 1972, 7, 1, 0, 0, sixty, 1, 1, 0);
1146         check(d2.shiftedBy(86399).shiftedBy(one), 1972, 7, 1, 23, 59, sixty, 1, 1, 0);
1147         check(d2.shiftedBy(-zeroUlp), 1972, 7, 1, 0, 0, 0, 0.5, 0, 0);
1148         check(d2.shiftedBy(-oneUlp), 1972, 6, 30, 23, 59, sixtyOne, 1, 1, 0);
1149         check(d2.shiftedBy(-1).shiftedBy(zeroUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1150         check(d2.shiftedBy(-1).shiftedBy(-zeroUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1151         check(d2.shiftedBy(-1).shiftedBy(-oneUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1152         check(d2.shiftedBy(-1).shiftedBy(-sixtyUlp), 1972, 6, 30, 23, 59, sixty, 0.5, 0, 0);
1153         check(d2.shiftedBy(-61).shiftedBy(attoSecond), 1972, 6, 30, 23, 59, attoSecond, 0.5, 0, 0);
1154 
1155         // check first leap second, which was actually 1.422818 s.
1156         AbsoluteDate d3 = AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(-1230724800);
1157         check(d3, 1960, 12, 31, 23, 59, 60, 0.5, 0, 0);
1158         AbsoluteDate d4 = new AbsoluteDate(1961, 1, 1, utc);
1159         check(d4, 1961, 1, 1, 0, 0, 0, 0.5, 0, 0);
1160         // FIXME something wrong because a date a smidgen before 1961-01-01 is not in a leap second
1161         //check(d4.shiftedBy(-oneUlp), 1960, 12, 31, 23, 59, 61.422818, 0.5, 0, 0);
1162 
1163         // check NaN, this is weird that NaNs have valid ymdhm, but not second.
1164         DateTimeComponents actual = date.shiftedBy(Double.NaN).getComponents(utc);
1165         DateComponents dc = actual.getDate();
1166         TimeComponents tc = actual.getTime();
1167         MatcherAssert.assertThat(dc.getYear(), CoreMatchers.is(2000));
1168         MatcherAssert.assertThat(dc.getMonth(), CoreMatchers.is(1));
1169         MatcherAssert.assertThat(dc.getDay(), CoreMatchers.is(1));
1170         MatcherAssert.assertThat(tc.getHour(), CoreMatchers.is(0));
1171         MatcherAssert.assertThat(tc.getMinute(), CoreMatchers.is(0));
1172         MatcherAssert.assertThat("second", tc.getSecond(), CoreMatchers.is(Double.NaN));
1173         MatcherAssert.assertThat(tc.getMinutesFromUTC(), CoreMatchers.is(0));
1174         final double difference = new AbsoluteDate(actual, utc).durationFrom(date);
1175         MatcherAssert.assertThat(difference, CoreMatchers.is(Double.NaN));
1176     }
1177 
1178     private void check(AbsoluteDate date,
1179                        int year, int month, int day, int hour, int minute, double second,
1180                        double roundTripUlps, final int secondUlps, final double absTol) {
1181         DateTimeComponents actual = date.getComponents(utc);
1182         DateComponents d = actual.getDate();
1183         TimeComponents t = actual.getTime();
1184         MatcherAssert.assertThat(d.getYear(), CoreMatchers.is(year));
1185         MatcherAssert.assertThat(d.getMonth(), CoreMatchers.is(month));
1186         MatcherAssert.assertThat(d.getDay(), CoreMatchers.is(day));
1187         MatcherAssert.assertThat(t.getHour(), CoreMatchers.is(hour));
1188         MatcherAssert.assertThat(t.getMinute(), CoreMatchers.is(minute));
1189         MatcherAssert.assertThat("second", t.getSecond(),
1190                 OrekitMatchers.numberCloseTo(second, absTol, secondUlps));
1191         MatcherAssert.assertThat(t.getMinutesFromUTC(), CoreMatchers.is(0));
1192         final double tol = FastMath.ulp(second) * roundTripUlps;
1193         final double difference = new AbsoluteDate(actual, utc).durationFrom(date);
1194         MatcherAssert.assertThat(difference,
1195                 OrekitMatchers.closeTo(0, FastMath.max(absTol, tol)));
1196     }
1197 
1198     /** Check {@link AbsoluteDate#toStringRfc3339(TimeScale)}. */
1199     @Test
1200     public void testToStringRfc3339() {
1201         // setup
1202         AbsoluteDate date = new AbsoluteDate(2009, 1, 1, utc);
1203         double one = FastMath.nextDown(1.0);
1204         double zeroUlp = FastMath.nextUp(0.0);
1205         double oneUlp = FastMath.ulp(1.0);
1206         //double sixty = FastMath.nextDown(60.0);
1207         double sixtyUlp = FastMath.ulp(60.0);
1208 
1209         // action
1210         // test midnight
1211         check(date, "2009-01-01T00:00:00Z");
1212         check(date.shiftedBy(1), "2009-01-01T00:00:01Z");
1213         // test digits and rounding
1214         check(date.shiftedBy(new TimeOffset(12L, 345678912345678900L)), "2009-01-01T00:00:12.3456789123456789Z");
1215         check(date.shiftedBy(new TimeOffset(0L, 12345678912345678L)), "2009-01-01T00:00:00.012345678912345678Z");
1216         // test min and max values
1217         check(date.shiftedBy(zeroUlp), "2009-01-01T00:00:00Z");
1218         check(date.shiftedBy(59.0).shiftedBy(one), "2009-01-01T00:00:59.999999999999999889Z");
1219         check(date.shiftedBy(86399).shiftedBy(one), "2009-01-01T23:59:59.999999999999999889Z");
1220         check(date.shiftedBy(oneUlp), "2009-01-01T00:00:00.000000000000000222Z");
1221         check(date.shiftedBy(one), "2009-01-01T00:00:00.999999999999999889Z");
1222         check(date.shiftedBy(-zeroUlp), "2009-01-01T00:00:00Z");
1223         // test leap
1224         check(date.shiftedBy(-oneUlp), "2008-12-31T23:59:60.999999999999999778Z");
1225         check(date.shiftedBy(-1).shiftedBy(one), "2008-12-31T23:59:60.999999999999999889Z");
1226         check(date.shiftedBy(-0.5), "2008-12-31T23:59:60.5Z");
1227         check(date.shiftedBy(-1).shiftedBy(zeroUlp), "2008-12-31T23:59:60Z");
1228         check(date.shiftedBy(-1), "2008-12-31T23:59:60Z");
1229         check(date.shiftedBy(-1).shiftedBy(-zeroUlp), "2008-12-31T23:59:60Z");
1230         check(date.shiftedBy(-1).shiftedBy(-oneUlp), "2008-12-31T23:59:59.999999999999999778Z");
1231         check(date.shiftedBy(-2), "2008-12-31T23:59:59Z");
1232         check(date.shiftedBy(-1).shiftedBy(-sixtyUlp), "2008-12-31T23:59:59.999999999999992895Z");
1233         check(date.shiftedBy(-61).shiftedBy(zeroUlp), "2008-12-31T23:59:00Z");
1234         check(date.shiftedBy(-61).shiftedBy(oneUlp), "2008-12-31T23:59:00.000000000000000222Z");
1235         // test UTC weirdness
1236         AbsoluteDate d = new AbsoluteDate(1966, 1, 1, utc);
1237         check(d, "1966-01-01T00:00:00Z");
1238         check(d.shiftedBy(zeroUlp), "1966-01-01T00:00:00Z");
1239         check(d.shiftedBy(oneUlp), "1966-01-01T00:00:00.000000000000000222Z");
1240         // as we are after the 1966 leap, slope is 30ns/s
1241         // decimals should therefore be (1 - 2⁻⁵³) ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9999999700000007889776…
1242         // Orekit 13.0 is accurate to attosecond
1243         check(d.shiftedBy(one), "1966-01-01T00:00:00.999999970000000789Z");
1244         // as we are after the 1966 leap, slope is 30ns/s
1245         // decimals should therefore be [59 + (1 - 2⁻⁵³)] ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9999982000000538889760…
1246         // Orekit 13.0 is accurate to attosecond
1247         check(d.shiftedBy(59).shiftedBy(one), "1966-01-01T00:00:59.999998200000053889Z");
1248         // as we are after the 1966 leap, slope is 30ns/s
1249         // decimals should therefore be [86399 + (1 - 2⁻⁵³)] ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9974080000777598866449…
1250         // Orekit 13.0 is accurate to attosecond
1251         check(d.shiftedBy(86399).shiftedBy(one), "1966-01-01T23:59:59.997408000077759887Z");
1252         check(d.shiftedBy(-zeroUlp), "1966-01-01T00:00:00Z");
1253         // actual leap is small ~1e-16, Orekit 13.0 get it
1254         check(d.shiftedBy(-oneUlp), "1965-12-31T23:59:59.999999999999999779Z");
1255         // as we are before the 1966 leap, slope is 15ns/s
1256         // decimals should therefore be 15 / (10⁹ + 15) ≈ 0.000000014999999775000003375…
1257         // Orekit 13.0 is accurate to attosecond
1258         check(d.shiftedBy(-1).shiftedBy(zeroUlp), "1965-12-31T23:59:59.000000014999999776Z");
1259         check(d.shiftedBy(-1).shiftedBy(-zeroUlp), "1965-12-31T23:59:59.000000014999999776Z");
1260         // we subtract ulp(1) = 2⁻⁵² ≈ 222 as
1261         // Orekit 13.0 is accurate to attosecond
1262         check(d.shiftedBy(-1).shiftedBy(-oneUlp), "1965-12-31T23:59:59.000000014999999554Z");
1263         // we subtract ulp(60) = 2⁻⁴⁷ ≈ 7105 as
1264         // Orekit 13.0 is accurate to attosecond
1265         check(d.shiftedBy(-1).shiftedBy(-sixtyUlp), "1965-12-31T23:59:59.000000014999992671Z");
1266         // since second ~= 0 there is significant cancellation
1267         // 60 ⨉ 15 / (10⁹ + 15) ≈ 0.0000008999999865000002025
1268         check(d.shiftedBy(-60).shiftedBy(zeroUlp), "1965-12-31T23:59:00.000000899999986501Z");
1269         check(d.shiftedBy(-60).shiftedBy(oneUlp), "1965-12-31T23:59:00.000000899999986723Z");
1270 
1271         // check first leap second, which was actually 1.422818 s.
1272         check(new AbsoluteDate(1961, 1, 1, utc), "1961-01-01T00:00:00Z");
1273         AbsoluteDate d3 = AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(-1230724800);
1274         check(d3, "1960-12-31T23:59:60Z");
1275         // FIXME something wrong because a date a smidgen before 1961-01-01 is not in a leap second
1276         //check(d3.shiftedBy(FastMath.nextDown(1.422818)), "1960-12-31T23:59:61.422818Z");
1277 
1278         // test proleptic
1279         check(new AbsoluteDate(123, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1280               "0123-04-05T06:07:08.9Z");
1281 
1282         // there is no way to produce valid RFC3339 for these cases
1283         // I would rather print something useful than throw an exception
1284         // so these cases don't check for a correct answer, just an informative one
1285         check(new AbsoluteDate(-123, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1286               "-123-04-05T06:07:08.9Z");
1287         check(new AbsoluteDate(-1230, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1288               "-1230-04-05T06:07:08.9Z");
1289         // test far future
1290         check(new AbsoluteDate(12300, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1291               "12300-04-05T06:07:08.9Z");
1292         // test infinity
1293         check(AbsoluteDate.FUTURE_INFINITY, "5881610-07-11T23:59:59.999Z");
1294         check(AbsoluteDate.PAST_INFINITY, "-5877490-03-03T00:00:00Z");
1295         // test NaN
1296         check(date.shiftedBy(Double.NaN), "2000-01-01T00:00:NaNZ");
1297     }
1298 
1299     private void check(final AbsoluteDate d, final String s) {
1300         MatcherAssert.assertThat(d.toStringRfc3339(utc),
1301                 CoreMatchers.is(s));
1302         MatcherAssert.assertThat(d.getComponents(utc).toStringRfc3339(),
1303                 CoreMatchers.is(s));
1304     }
1305 
1306 
1307     /** Check {@link AbsoluteDate#toString()}. */
1308     @Test
1309     public void testToString() {
1310         // setup
1311         AbsoluteDate date = new AbsoluteDate(2009, 1, 1, utc);
1312         double one = FastMath.nextDown(1.0);
1313         double zeroUlp = FastMath.nextUp(0.0);
1314         double oneUlp = FastMath.ulp(1.0);
1315         //double sixty = FastMath.nextDown(60.0);
1316         double sixtyUlp = FastMath.ulp(60.0);
1317 
1318         // action
1319         // test midnight
1320         checkToString(date, "2009-01-01T00:00:00.000");
1321         checkToString(date.shiftedBy(1), "2009-01-01T00:00:01.000");
1322         // test digits and rounding
1323         checkToString(date.shiftedBy(new TimeOffset(12L, 345678912345678900L)), "2009-01-01T00:00:12.3456789123456789");
1324         checkToString(date.shiftedBy(new TimeOffset(0L, 12345678912345678L)), "2009-01-01T00:00:00.012345678912345678");
1325         // test min and max values
1326         checkToString(date.shiftedBy(zeroUlp), "2009-01-01T00:00:00.000");
1327         // Orekit 13.0 is accurate to attosecond
1328         checkToString(date.shiftedBy(59.0).shiftedBy(one), "2009-01-01T00:00:59.999999999999999889");
1329         // Orekit 13.0 is accurate to attosecond
1330         checkToString(date.shiftedBy(86399).shiftedBy(one), "2009-01-01T23:59:59.999999999999999889");
1331         // Orekit 13.0 is accurate to attosecond
1332         checkToString(date.shiftedBy(oneUlp), "2009-01-01T00:00:00.000000000000000222");
1333         // Orekit 13.0 is accurate to attosecond
1334         checkToString(date.shiftedBy(one), "2009-01-01T00:00:00.999999999999999889");
1335         checkToString(date.shiftedBy(-zeroUlp), "2009-01-01T00:00:00.000");
1336         // test leap
1337         // Orekit 10.1 throw OIAE, 10.2 rounds up, 13.0 is accurate to attosecond
1338         checkToString(date.shiftedBy(-oneUlp), "2008-12-31T23:59:60.999999999999999778");
1339         // Orekit 13.0 is accurate to attosecond
1340         checkToString(date.shiftedBy(-1).shiftedBy(one), "2008-12-31T23:59:60.999999999999999889");
1341         checkToString(date.shiftedBy(-0.5), "2008-12-31T23:59:60.500");
1342         checkToString(date.shiftedBy(-1).shiftedBy(zeroUlp), "2008-12-31T23:59:60.000");
1343         checkToString(date.shiftedBy(-1), "2008-12-31T23:59:60.000");
1344         checkToString(date.shiftedBy(-1).shiftedBy(-zeroUlp), "2008-12-31T23:59:60.000");
1345         // Orekit 13.0 is accurate to attosecond
1346         checkToString(date.shiftedBy(-1).shiftedBy(-oneUlp), "2008-12-31T23:59:59.999999999999999778");
1347         checkToString(date.shiftedBy(-2), "2008-12-31T23:59:59.000");
1348         // Orekit 13.0 is accurate to attosecond
1349         checkToString(date.shiftedBy(-1).shiftedBy(-sixtyUlp), "2008-12-31T23:59:59.999999999999992895");
1350         checkToString(date.shiftedBy(-61).shiftedBy(zeroUlp), "2008-12-31T23:59:00.000");
1351         // Orekit 13.0 is accurate to attosecond
1352         checkToString(date.shiftedBy(-61).shiftedBy(oneUlp), "2008-12-31T23:59:00.000000000000000222");
1353         // test UTC weirdness
1354         AbsoluteDate d = new AbsoluteDate(1966, 1, 1, utc);
1355         checkToString(d, "1966-01-01T00:00:00.000");
1356         checkToString(d.shiftedBy(zeroUlp), "1966-01-01T00:00:00.000");
1357         checkToString(d.shiftedBy(oneUlp), "1966-01-01T00:00:00.000000000000000222");
1358         // as we are after the 1966 leap, slope is 30ns/s
1359         // decimals should therefore be (1 - 2⁻⁵³) ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9999999700000007889776…
1360         // Orekit 13.0 is accurate to attosecond
1361         checkToString(d.shiftedBy(one), "1966-01-01T00:00:00.999999970000000789");
1362         // as we are after the 1966 leap, slope is 30ns/s
1363         // decimals should therefore be [59 + (1 - 2⁻⁵³)] ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9999982000000538889760…
1364         // Orekit 13.0 is accurate to attosecond
1365         checkToString(d.shiftedBy(59).shiftedBy(one), "1966-01-01T00:00:59.999998200000053889");
1366         // as we are after the 1966 leap, slope is 30ns/s
1367         // decimals should therefore be [86399 + (1 - 2⁻⁵³)] ⨉ 10⁹ / (10⁹ + 30) ≈ 0.9974080000777598866449…
1368         // Orekit 13.0 is accurate to attosecond
1369         checkToString(d.shiftedBy(86399).shiftedBy(one), "1966-01-01T23:59:59.997408000077759887");
1370         checkToString(d.shiftedBy(-zeroUlp), "1966-01-01T00:00:00.000");
1371         // actual leap is small ~1e-16, Orekit 13.0 get it
1372         checkToString(d.shiftedBy(-oneUlp), "1965-12-31T23:59:59.999999999999999779");
1373         // as we are before the 1966 leap, slope is 15ns/s
1374         // decimals should therefore be 15 / (10⁹ + 15) ≈ 0.000000014999999775000003375…
1375         // Orekit 13.0 is accurate to attosecond
1376         checkToString(d.shiftedBy(-1).shiftedBy(zeroUlp), "1965-12-31T23:59:59.000000014999999776");
1377         checkToString(d.shiftedBy(-1).shiftedBy(-zeroUlp), "1965-12-31T23:59:59.000000014999999776");
1378         // we subtract ulp(1) = 2⁻⁵² ≈ 222 as
1379         // Orekit 13.0 is accurate to attosecond
1380         checkToString(d.shiftedBy(-1).shiftedBy(-oneUlp), "1965-12-31T23:59:59.000000014999999554");
1381         // we subtract ulp(60) = 2⁻⁴⁷ ≈ 7105 as
1382         // Orekit 13.0 is accurate to attosecond
1383         checkToString(d.shiftedBy(-1).shiftedBy(-sixtyUlp), "1965-12-31T23:59:59.000000014999992671");
1384         // 60 ⨉ 15 / (10⁹ + 15) ≈ 0.0000008999999865000002025
1385         checkToString(d.shiftedBy(-60).shiftedBy(zeroUlp), "1965-12-31T23:59:00.000000899999986501");
1386         checkToString(d.shiftedBy(-60).shiftedBy(oneUlp), "1965-12-31T23:59:00.000000899999986723");
1387 
1388         // check first leap second, which was actually 1.422818 s.
1389         checkToString(new AbsoluteDate(1961, 1, 1, utc), "1961-01-01T00:00:00.000");
1390         AbsoluteDate d3 = AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(-1230724800);
1391         checkToString(d3, "1960-12-31T23:59:60.000");
1392         // FIXME something wrong because a date a smidgen before 1961-01-01 is not in a leap second
1393         //checkToString(d3.shiftedBy(FastMath.nextDown(1.422818)), "1960-12-31T23:59:61.423");
1394 
1395         // test proleptic
1396         checkToString(new AbsoluteDate(123, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1397                       "0123-04-05T06:07:08.900");
1398 
1399         // there is no way to produce valid RFC3339 for these cases
1400         // I would rather print something useful than throw an exception
1401         // so these cases don't check for a correct answer, just an informative one
1402         checkToString(new AbsoluteDate(-123, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1403                       "-123-04-05T06:07:08.900");
1404         checkToString(new AbsoluteDate(-1230, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1405                       "-1230-04-05T06:07:08.900");
1406         // test far future
1407         checkToString(new AbsoluteDate(12300, 4, 5, 6, 7, new TimeOffset(8, TimeOffset.SECOND, 900, TimeOffset.MILLISECOND), utc),
1408                       "12300-04-05T06:07:08.900");
1409         // test infinity
1410         checkToString(AbsoluteDate.FUTURE_INFINITY, "5881610-07-11T23:59:59.999");
1411         checkToString(AbsoluteDate.PAST_INFINITY, "-5877490-03-03T00:00:00.000");
1412         // test NaN
1413         checkToString(date.shiftedBy(Double.NaN), "2000-01-01T00:00:NaN");
1414     }
1415 
1416     private void checkToString(final AbsoluteDate d, final String s) {
1417         MatcherAssert.assertThat(d.toString(), CoreMatchers.is(s + "Z"));
1418         MatcherAssert.assertThat(d.getComponents(utc).toString(), CoreMatchers.is(s + "+00:00"));
1419     }
1420 
1421     @Test
1422     public void testToStringWithoutUtcOffset() {
1423         // setup
1424         AbsoluteDate date = new AbsoluteDate(2009, 1, 1, utc);
1425         double one = FastMath.nextDown(1.0);
1426         double zeroUlp = FastMath.nextUp(0.0);
1427         double oneUlp = FastMath.ulp(1.0);
1428         //double sixty = FastMath.nextDown(60.0);
1429         double sixtyUlp = FastMath.ulp(60.0);
1430 
1431         // action
1432         // test midnight
1433         checkToStringNoOffset(date, "2009-01-01T00:00:00.000");
1434         checkToStringNoOffset(date.shiftedBy(1), "2009-01-01T00:00:01.000");
1435         // test digits and rounding
1436         checkToStringNoOffset(date.shiftedBy(12.3456789123456789), "2009-01-01T00:00:12.346");
1437         checkToStringNoOffset(date.shiftedBy(0.0123456789123456789), "2009-01-01T00:00:00.012");
1438         // test min and max values
1439         checkToStringNoOffset(date.shiftedBy(zeroUlp), "2009-01-01T00:00:00.000");
1440         // Orekit 10.1 rounds up
1441         checkToStringNoOffset(date.shiftedBy(59.0).shiftedBy(one), "2009-01-01T00:01:00.000");
1442         // Orekit 10.1 rounds up
1443         checkToStringNoOffset(date.shiftedBy(86399).shiftedBy(one), "2009-01-02T00:00:00.000");
1444         checkToStringNoOffset(date.shiftedBy(oneUlp), "2009-01-01T00:00:00.000");
1445         checkToStringNoOffset(date.shiftedBy(one), "2009-01-01T00:00:01.000");
1446         checkToStringNoOffset(date.shiftedBy(-zeroUlp), "2009-01-01T00:00:00.000");
1447         // test leap
1448         // Orekit 10.1 throw OIAE, 10.2 rounds up
1449         checkToStringNoOffset(date.shiftedBy(-oneUlp), "2009-01-01T00:00:00.000");
1450         // Orekit 10.1 rounds up
1451         checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(one), "2009-01-01T00:00:00.000");
1452         checkToStringNoOffset(date.shiftedBy(-0.5), "2008-12-31T23:59:60.500");
1453         checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(zeroUlp), "2008-12-31T23:59:60.000");
1454         checkToStringNoOffset(date.shiftedBy(-1), "2008-12-31T23:59:60.000");
1455         checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-zeroUlp), "2008-12-31T23:59:60.000");
1456         checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-oneUlp), "2008-12-31T23:59:60.000");
1457         checkToStringNoOffset(date.shiftedBy(-2), "2008-12-31T23:59:59.000");
1458         // Orekit 10.1 rounds up
1459         checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-sixtyUlp), "2008-12-31T23:59:60.000");
1460         checkToStringNoOffset(date.shiftedBy(-61).shiftedBy(zeroUlp), "2008-12-31T23:59:00.000");
1461         checkToStringNoOffset(date.shiftedBy(-61).shiftedBy(oneUlp), "2008-12-31T23:59:00.000");
1462     }
1463 
1464 
1465     private void checkToStringNoOffset(final AbsoluteDate d, final String s) {
1466         MatcherAssert.assertThat(d.toStringWithoutUtcOffset(utc, 3), CoreMatchers.is(s));
1467         MatcherAssert.assertThat(
1468                 d.getComponents(utc).toStringWithoutUtcOffset(utc.minuteDuration(d), 3),
1469                 CoreMatchers.is(s));
1470     }
1471 
1472     /**
1473      * Check {@link AbsoluteDate#toString()} when UTC throws an exception. This ~is~ was
1474      * the most common issue new and old users face.
1475      */
1476     @Test
1477     public void testToStringException() {
1478         Utils.setDataRoot("no-data");
1479         try {
1480             DataContext.getDefault().getTimeScales().getUTC();
1481             Assertions.fail("Expected Exception");
1482         } catch (OrekitException e) {
1483             // expected
1484             Assertions.assertEquals(e.getSpecifier(), OrekitMessages.NO_IERS_UTC_TAI_HISTORY_DATA_LOADED);
1485         }
1486         // try some unusual values
1487         MatcherAssert.assertThat(present.toString(), CoreMatchers.is("2000-01-01T12:00:32.000 TAI"));
1488         MatcherAssert.assertThat(present.shiftedBy(Double.POSITIVE_INFINITY).toString(),
1489                                  CoreMatchers.is("5881610-07-11T23:59:59.999 TAI"));
1490         MatcherAssert.assertThat(present.shiftedBy(Double.NEGATIVE_INFINITY).toString(),
1491                                  CoreMatchers.is("-5877490-03-03T00:00:00.000 TAI"));
1492         MatcherAssert.assertThat(present.shiftedBy(Double.NaN).toString(),
1493                                  CoreMatchers.is("2000-01-01T00:00:NaN TAI"));
1494         // infinity is special cased, but I can make AbsoluteDate.offset larger than
1495         // Long.MAX_VALUE see #584
1496         Assertions.assertTrue(Double.isInfinite(present.shiftedBy(1e300).durationFrom(present)));
1497     }
1498 
1499     /** Test for issue 943: management of past and future infinity in equality checks. */
1500     @Test
1501     public void test_issue_943() {
1502 
1503         // Run issue test
1504         final AbsoluteDate date1 = new AbsoluteDate(AbsoluteDate.PAST_INFINITY, 0);
1505         final AbsoluteDate date2 = new AbsoluteDate(AbsoluteDate.PAST_INFINITY, 0);
1506         date1.durationFrom(date2);
1507         Assertions.assertEquals(date1, date2);
1508 
1509         // Check equality is as expected for PAST INFINITY
1510         final AbsoluteDate date3 = AbsoluteDate.PAST_INFINITY;
1511         final AbsoluteDate date4 = new AbsoluteDate(AbsoluteDate.PAST_INFINITY, 0);
1512         Assertions.assertEquals(date3, date4);
1513 
1514         // Check equality is as expected for FUTURE INFINITY
1515         final AbsoluteDate date5 = AbsoluteDate.FUTURE_INFINITY;
1516         final AbsoluteDate date6 = new AbsoluteDate(AbsoluteDate.FUTURE_INFINITY, 0);
1517         Assertions.assertEquals(date5, date6);
1518 
1519         // Check inequality is as expected
1520         final AbsoluteDate date7 = new AbsoluteDate(AbsoluteDate.PAST_INFINITY, 0);
1521         final AbsoluteDate date8 = new AbsoluteDate(AbsoluteDate.FUTURE_INFINITY, 0);
1522         Assertions.assertNotEquals(date7, date8);
1523 
1524         // Check inequality is as expected
1525         final AbsoluteDate date9 = new AbsoluteDate(new TimeOffset(Double.POSITIVE_INFINITY));
1526         final AbsoluteDate date10 = new AbsoluteDate(new TimeOffset(Double.POSITIVE_INFINITY));
1527         Assertions.assertEquals(date9, date10);
1528     }
1529 
1530     @Test
1531     public void testNegativeOffsetConstructor() {
1532         AbsoluteDate date = new AbsoluteDate(2019, 10, 11, 20, 40,
1533                                              FastMath.scalb(6629298651489277.0, -55),
1534                                              TimeScalesFactory.getTT());
1535         AbsoluteDate after = date.shiftedBy(Precision.EPSILON);
1536         Assertions.assertEquals(624098367L, date.getSeconds());
1537         Assertions.assertEquals(FastMath.nextAfter(1.0, Double.NEGATIVE_INFINITY), 1.0e-18 * date.getAttoSeconds(), 2.4e-15);
1538         Assertions.assertEquals(Precision.EPSILON, after.durationFrom(date), 1.0e-18);
1539     }
1540 
1541     @Test
1542     public void testNegativeOffsetShift() {
1543         AbsoluteDate reference = new AbsoluteDate(2019, 10, 11, 20, 40,
1544                                                   1.6667019180022178E-7,
1545                                                   TimeScalesFactory.getTAI());
1546         double dt = FastMath.scalb(6596520010750484.0, -39);
1547         AbsoluteDate shifted = reference.shiftedBy(dt);
1548         AbsoluteDate after = shifted.shiftedBy(Precision.EPSILON);
1549         Assertions.assertEquals(624110398L, shifted.getSeconds());
1550         Assertions.assertEquals((1.0 - 1.6922e-13) * 1.0e18, shifted.getAttoSeconds(), 1.0e-15);
1551         Assertions.assertEquals(Precision.EPSILON, after.durationFrom(shifted), 1.0e-18);
1552     }
1553 
1554     @Test
1555     public void testDurationFromWithTimeUnit() {
1556         AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc);
1557         for (TimeUnit timeUnit : TimeUnit.values()) {
1558             Assertions.assertEquals(0, reference.durationFrom(reference, timeUnit));
1559 
1560             long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS);
1561             for (int i = 1; i <= 365; i++) {
1562               AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY);
1563               AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY);
1564 
1565 
1566               Assertions.assertEquals(i * dayInTimeUnit, reference.durationFrom(minusDays, timeUnit));
1567 
1568               Assertions.assertEquals(-i * dayInTimeUnit, reference.durationFrom(plusDays, timeUnit));
1569             }
1570 
1571            for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) {
1572               AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns);
1573               AbsoluteDate plus = reference.shiftedBy(1e-9 * ns);
1574 
1575               double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1);
1576               Assertions.assertEquals(FastMath.round(deltaInTimeUnit), reference.durationFrom(minus, timeUnit),
1577                   String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1578 
1579               Assertions.assertEquals(FastMath.round(-deltaInTimeUnit), reference.durationFrom(plus, timeUnit),
1580                   String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1581             }
1582 
1583 
1584         }
1585     }
1586 
1587     @Test
1588     public void testConstructWithTimeUnitOffset() {
1589       AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc);
1590 
1591       for (TimeUnit timeUnit : TimeUnit.values()) {
1592         Assertions.assertEquals(0,
1593             FastMath.abs(reference.durationFrom(new AbsoluteDate(reference, 0, timeUnit))), 1e-10);
1594 
1595         long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS);
1596         for (int i = 1; i <= 365; i++) {
1597           AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY);
1598           AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY);
1599 
1600           Assertions.assertEquals(0,
1601               FastMath.abs(reference.durationFrom(new AbsoluteDate(minusDays, i * dayInTimeUnit, timeUnit))),
1602               1e-10,
1603               String.format("TimeUnit: %s", timeUnit));
1604           Assertions.assertEquals(0,
1605               FastMath.abs(reference.durationFrom(new AbsoluteDate(plusDays, -i * dayInTimeUnit, timeUnit))),
1606               1e-10,
1607               String.format("TimeUnit: %s", timeUnit));
1608         }
1609 
1610         for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) {
1611           if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) {
1612             //Skip everything larger than one second
1613             continue;
1614           }
1615           AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns);
1616           AbsoluteDate plus = reference.shiftedBy(1e-9 * ns);
1617 
1618           double deltaInTimeUnit =  ns / (double) timeUnit.toNanos(1);
1619           Assertions.assertEquals(0,
1620               FastMath.abs(reference.durationFrom(new AbsoluteDate(minus, FastMath.round(deltaInTimeUnit), timeUnit))),
1621               1.0 / timeUnit.convert(1, TimeUnit.SECONDS),
1622               String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1623           Assertions.assertEquals(0,
1624               FastMath.abs(reference.durationFrom(new AbsoluteDate(plus, FastMath.round(-deltaInTimeUnit), timeUnit))),
1625               1.0 / timeUnit.convert(1, TimeUnit.SECONDS),
1626               String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1627         }
1628       }
1629     }
1630 
1631     @Test
1632     public void testShiftedByWithTimeUnit() {
1633         AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc);
1634 
1635         for (TimeUnit timeUnit : TimeUnit.values()) {
1636             Assertions.assertEquals(0,
1637                 FastMath.abs(reference.durationFrom(reference.shiftedBy( 0, timeUnit))), 1e-10);
1638 
1639             long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS);
1640             for (int i = 1; i <= 365; i++) {
1641                 AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY);
1642                 AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY);
1643 
1644                 Assertions.assertEquals(0,
1645                     FastMath.abs(reference.durationFrom(minusDays.shiftedBy(i * dayInTimeUnit, timeUnit))),
1646                     1e-10,
1647                     String.format("TimeUnit: %s", timeUnit));
1648                 Assertions.assertEquals(0,
1649                     FastMath.abs(reference.durationFrom(plusDays.shiftedBy(-i * dayInTimeUnit, timeUnit))),
1650                     1e-10,
1651                     String.format("TimeUnit: %s", timeUnit));
1652             }
1653 
1654             for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) {
1655                 if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) {
1656                     //Skip everything larger than one second
1657                     continue;
1658                 }
1659                 AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns);
1660                 AbsoluteDate plus = reference.shiftedBy(1e-9 * ns);
1661 
1662                 double deltaInTimeUnit =  ns / (double) timeUnit.toNanos(1);
1663                 Assertions.assertEquals(0,
1664                     FastMath.abs(reference.durationFrom(minus.shiftedBy(FastMath.round(deltaInTimeUnit), timeUnit))),
1665                     1.0 / timeUnit.convert(1, TimeUnit.SECONDS),
1666                     String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1667                 Assertions.assertEquals(0,
1668                     FastMath.abs(reference.durationFrom(plus.shiftedBy(FastMath.round(-deltaInTimeUnit), timeUnit))),
1669                     1.0 / timeUnit.convert(1, TimeUnit.SECONDS),
1670                     String.format("TimeUnit: %s, ns: %d", timeUnit, ns));
1671             }
1672         }
1673     }
1674 
1675     @Test
1676     public void testGetJulianDates() {
1677 
1678         // GIVEN a reference date
1679         final TimeScale utc = TimeScalesFactory.getUTC();
1680 
1681         AbsoluteDate reference              = new AbsoluteDate(2024, 7, 4, 13, 0, 0, utc);
1682         AbsoluteDate referenceFromJDMethod  = AbsoluteDate.createJDDate(2460496, .0416667 * Constants.JULIAN_DAY, utc);
1683         AbsoluteDate referenceFromMJDMethod = AbsoluteDate.createMJDDate(60495, 0.54166670 * Constants.JULIAN_DAY, utc);
1684 
1685         // WHEN converting it to Julian Date or Modified Julian Date
1686         double mjdDateDefaultData = reference.getMJD();
1687         double jdDateDefaultData  = reference.getJD();
1688         double mjdDate            = reference.getMJD(utc);
1689         double jdDate             = reference.getJD(utc);
1690 
1691         // THEN
1692         // source : Time/Date Converter - HEASARC - NASA
1693         Assertions.assertEquals(2460496.0416667, jdDateDefaultData, 1.0e-6);
1694         Assertions.assertEquals(60495.54166670, mjdDateDefaultData, 1.0e-6);
1695         Assertions.assertEquals(jdDate, jdDateDefaultData, 1.0e-6);
1696         Assertions.assertEquals(mjdDateDefaultData, mjdDate);
1697 
1698         // Assert that static method are correct when creating date from JD or MJD
1699         Assertions.assertTrue(reference.isCloseTo(referenceFromJDMethod, 1e-2));
1700         Assertions.assertTrue(reference.isCloseTo(referenceFromMJDMethod, 1e-2));
1701     }
1702 
1703     @Test
1704     public void testLargeLeapSecond() {
1705         // this corresponds to issue 707
1706         Assertions.assertEquals(new AbsoluteDate(1961, 1, 1, utc).
1707                                 shiftedBy(new TimeOffset(22818, TimeOffset.MICROSECOND).negate()),
1708                                 new AbsoluteDate("1960-12-31T23:59:61.4", utc));
1709     }
1710 
1711     @Test
1712     public void testGetDayOfYear() {
1713         Assertions.assertEquals(0.501,
1714                                 new AbsoluteDate(2004,  1,  1,  0,  0,  0.001, utc).getDayOfYear(utc),
1715                                 1.0e-3);
1716         Assertions.assertEquals(1.000,
1717                                 new AbsoluteDate(2004,  1,  1, 12,  0,  0.000, utc).getDayOfYear(utc),
1718                                 1.0e-3);
1719         Assertions.assertEquals(366.0,
1720                                 new AbsoluteDate(2004, 12, 31, 12,  0,  0.000, utc).getDayOfYear(utc),
1721                                 1.0e-3);
1722         Assertions.assertEquals(366.499999988426,
1723                                 new AbsoluteDate(2004, 12, 31, 23, 59, 59.999, utc).getDayOfYear(utc),
1724                                 1.0e-12);
1725         Assertions.assertEquals(0.500000011574,
1726                                 new AbsoluteDate(2004, 12, 31, 23, 59, 59.999, utc).shiftedBy(0.002).getDayOfYear(utc),
1727                                 1.0e-12);
1728     }
1729 
1730     @BeforeEach
1731     public void setUp() {
1732         Utils.setDataRoot("regular-data");
1733         utc = TimeScalesFactory.getUTC();
1734         present = new AbsoluteDate(new DateComponents(2000, 1, 1),
1735                                     new TimeComponents(12, 0, 0), utc);
1736         presentToo = new AnyTimeStamped(present.shiftedBy(0));
1737         past = new AnyTimeStamped(present.shiftedBy(-1000));
1738         future = new AnyTimeStamped(present.shiftedBy(1000));
1739     }
1740 
1741     private TimeScale utc;
1742     private AbsoluteDate present;
1743     private AnyTimeStamped past;
1744     private AnyTimeStamped presentToo;
1745     private AnyTimeStamped future;
1746 
1747     static class AnyTimeStamped implements TimeStamped {
1748         AbsoluteDate date;
1749         public AnyTimeStamped(AbsoluteDate date) {
1750             this.date = date;
1751         }
1752 
1753         @Override
1754         public AbsoluteDate getDate() {
1755             return date;
1756         }
1757     }
1758 }