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