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