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.util.Date;
22  import java.util.TimeZone;
23  
24  import org.hamcrest.CoreMatchers;
25  import org.hamcrest.MatcherAssert;
26  import org.hipparchus.Field;
27  import org.hipparchus.CalculusFieldElement;
28  import org.hipparchus.util.Decimal64Field;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.Precision;
31  import org.junit.Assert;
32  import org.junit.Before;
33  import org.junit.Test;
34  import org.orekit.OrekitMatchers;
35  import org.orekit.Utils;
36  import org.orekit.errors.OrekitException;
37  import org.orekit.errors.OrekitIllegalArgumentException;
38  import org.orekit.errors.OrekitMessages;
39  import org.orekit.utils.Constants;
40  
41  public class FieldAbsoluteDateTest {
42  
43      private TimeScale utc;
44  
45      @Before
46      public void setUp() {
47          Utils.setDataRoot("regular-data");
48          utc = TimeScalesFactory.getUTC();
49      }
50  
51      @Test
52      public void testStandardEpoch() {
53          doTestStandardEpoch(Decimal64Field.getInstance());
54      }
55  
56      @Test
57      public void testStandardEpochStrings() {
58          doTestStandardEpochStrings(Decimal64Field.getInstance());
59      }
60  
61      @Test
62      public void testJulianEpochRate() {
63          doTestJulianEpochRate(Decimal64Field.getInstance());
64      }
65  
66      @Test
67      public void testBesselianEpochRate() {
68          doTestBesselianEpochRate(Decimal64Field.getInstance());
69      }
70  
71      @Test
72      public void testLieske() {
73          doTestLieske(Decimal64Field.getInstance());
74      }
75  
76      @Test
77      public void testParse() {
78          doTestParse(Decimal64Field.getInstance());
79      }
80  
81      @Test
82      public void testLocalTimeParsing() {
83          doTestLocalTimeParsing(Decimal64Field.getInstance());
84      }
85  
86      @Test
87      public void testTimeZoneDisplay() {
88          doTestTimeZoneDisplay(Decimal64Field.getInstance());
89      }
90  
91      @Test
92      public void testLocalTimeLeapSecond() throws IOException {
93          doTestLocalTimeLeapSecond(Decimal64Field.getInstance());
94      }
95  
96      @Test
97      public void testTimeZoneLeapSecond() {
98          doTestTimeZoneLeapSecond(Decimal64Field.getInstance());
99      }
100 
101     @Test
102     public void testParseLeap() {
103         doTestParseLeap(Decimal64Field.getInstance());
104     }
105 
106     @Test
107     public void testOutput() {
108         doTestOutput(Decimal64Field.getInstance());
109     }
110 
111     @Test
112     public void testJ2000() {
113         doTestJ2000(Decimal64Field.getInstance());
114     }
115 
116     @Test
117     public void testFraction() {
118         doTestFraction(Decimal64Field.getInstance());
119     }
120 
121     @Test
122     public void testScalesOffset() {
123         doTestScalesOffset(Decimal64Field.getInstance());
124     }
125 
126     @Test
127     public void testUTC() {
128         doTestUTC(Decimal64Field.getInstance());
129     }
130 
131     @Test
132     public void test1970() {
133         doTest1970(Decimal64Field.getInstance());
134     }
135 
136     @Test
137     public void testUtcGpsOffset() {
138         doTestUtcGpsOffset(Decimal64Field.getInstance());
139     }
140 
141     @Test
142     public void testGpsDate() {
143         doTestGpsDate(Decimal64Field.getInstance());
144     }
145 
146     @Test
147     public void testMJDDate() {
148         doTestMJDDate(Decimal64Field.getInstance());
149     }
150 
151     @Test
152     public void testJDDate() {
153         doTestJDDate(Decimal64Field.getInstance());
154     }
155 
156     @Test
157     public void testOffsets() {
158         doTestOffsets(Decimal64Field.getInstance());
159     }
160 
161     @Test
162     public void testBeforeAndAfterLeap() {
163         doTestBeforeAndAfterLeap(Decimal64Field.getInstance());
164     }
165 
166     @Test
167     public void testSymmetry() {
168         doTestSymmetry(Decimal64Field.getInstance());
169     }
170 
171     @Test
172     public void testEquals() {
173         doTestEquals(Decimal64Field.getInstance());
174     }
175 
176     @Test
177     public void testIsEqualTo() { doTestIsEqualTo(Decimal64Field.getInstance()); }
178 
179     @Test
180     public void testIsCloseTo() { doTestIsCloseTo(Decimal64Field.getInstance()); }
181 
182     @Test
183     public void testIsBefore() { doTestIsBefore(Decimal64Field.getInstance()); }
184 
185     @Test
186     public void testIsAfter() { doTestIsAfter(Decimal64Field.getInstance()); }
187 
188     @Test
189     public void testIsBeforeOrEqualTo() { doTestIsBeforeOrEqualTo(Decimal64Field.getInstance()); }
190 
191     @Test
192     public void testIsAfterOrEqualTo() { doTestIsAfterOrEqualTo(Decimal64Field.getInstance()); }
193 
194     @Test
195     public void testIsBetween() { doTestIsBetween(Decimal64Field.getInstance()); }
196 
197     @Test
198     public void testIsBetweenOrEqualTo() { doTestIsBetweenOrEqualTo(Decimal64Field.getInstance()); }
199 
200     @Test
201     public void testComponents() {
202         doTestComponents(Decimal64Field.getInstance());
203     }
204 
205     @Test
206     public void testMonth() {
207         doTestMonth(Decimal64Field.getInstance());
208     }
209 
210     @Test
211     public void testCCSDSUnsegmentedNoExtension() {
212         doTestCCSDSUnsegmentedNoExtension(Decimal64Field.getInstance());
213     }
214 
215     @Test
216     public void testCCSDSUnsegmentedWithExtendedPreamble() {
217         doTestCCSDSUnsegmentedWithExtendedPreamble(Decimal64Field.getInstance());
218     }
219 
220     @Test
221     public void testCCSDSDaySegmented() {
222         doTestCCSDSDaySegmented(Decimal64Field.getInstance());
223     }
224 
225     @Test
226     public void testCCSDSCalendarSegmented() {
227         doTestCCSDSCalendarSegmented(Decimal64Field.getInstance());
228     }
229 
230     @Test
231     public void testExpandedConstructors() {
232         doTestExpandedConstructors(Decimal64Field.getInstance());
233     }
234 
235     @Test
236     public void testHashcode() {
237         doTestHashcode(Decimal64Field.getInstance());
238     }
239 
240     @Test
241     public void testInfinity() {
242         doTestInfinity(Decimal64Field.getInstance());
243     }
244 
245     @Test
246     public void testAccuracy() {
247         doTestAccuracy(Decimal64Field.getInstance());
248     }
249 
250     @Test
251     public void testAccuracyIssue348() {
252         doTestAccuracyIssue348(Decimal64Field.getInstance());
253     }
254 
255     @Test
256     public void testIterationAccuracy() {
257         doTestIterationAccuracy(Decimal64Field.getInstance());
258     }
259 
260     @Test
261     public void testIssue142() {
262         doTestIssue142(Decimal64Field.getInstance());
263     }
264 
265     @Test
266     public void testIssue148() {
267         doTestIssue148(Decimal64Field.getInstance());
268     }
269 
270     @Test
271     public void testIssue149() {
272         doTestIssue149(Decimal64Field.getInstance());
273     }
274 
275     @Test
276     public void testWrapAtMinuteEnd() {
277         doTestWrapAtMinuteEnd(Decimal64Field.getInstance());
278     }
279 
280     @Test
281     public void testIssue508() {
282         doTestIssue508(Decimal64Field.getInstance());
283     }
284 
285     @Test
286     public void testGetComponentsIssue681and676() {
287         doTestGetComponentsIssue681and676(Decimal64Field.getInstance());
288     }
289 
290     private <T extends CalculusFieldElement<T>> void doTestStandardEpoch(final Field<T> field) {
291 
292         TimeScale tai = TimeScalesFactory.getTAI();
293         TimeScale tt  = TimeScalesFactory.getTT();
294 
295         FieldAbsoluteDate<T> JuEp  = FieldAbsoluteDate.getJulianEpoch(field);
296         FieldAbsoluteDate<T> MJuEp = FieldAbsoluteDate.getModifiedJulianEpoch(field);
297         FieldAbsoluteDate<T> FiEp  = FieldAbsoluteDate.getFiftiesEpoch(field);
298         FieldAbsoluteDate<T> CCSDS = FieldAbsoluteDate.getCCSDSEpoch(field);
299         FieldAbsoluteDate<T> GaEp  = FieldAbsoluteDate.getGalileoEpoch(field);
300         FieldAbsoluteDate<T> GPSEp = FieldAbsoluteDate.getGPSEpoch(field);
301         FieldAbsoluteDate<T> JTTEP = FieldAbsoluteDate.getJ2000Epoch(field);
302 
303         Assert.assertEquals(-210866760000000l, JuEp.toDate(tt).getTime());
304         Assert.assertEquals(-3506716800000l, MJuEp.toDate(tt).getTime());
305         Assert.assertEquals(-631152000000l, FiEp.toDate(tt).getTime());
306         Assert.assertEquals(-378691200000l, CCSDS.toDate(tai).getTime());
307         Assert.assertEquals(935280019000l,  GaEp.toDate(tai).getTime());
308         Assert.assertEquals(315964819000l,  GPSEp.toDate(tai).getTime());
309         Assert.assertEquals(946728000000l,  JTTEP.toDate(tt).getTime());
310 
311     }
312 
313     private <T extends CalculusFieldElement<T>> void doTestStandardEpochStrings(final Field<T> field) {
314 
315         Assert.assertEquals("-4712-01-01T12:00:00.000",
316                             FieldAbsoluteDate.getJulianEpoch(field).toString(TimeScalesFactory.getTT()));
317         Assert.assertEquals("1858-11-17T00:00:00.000",
318                             FieldAbsoluteDate.getModifiedJulianEpoch(field).toString(TimeScalesFactory.getTT()));
319         Assert.assertEquals("1950-01-01T00:00:00.000",
320                             FieldAbsoluteDate.getFiftiesEpoch(field).toString(TimeScalesFactory.getTT()));
321         Assert.assertEquals("1958-01-01T00:00:00.000",
322                             FieldAbsoluteDate.getCCSDSEpoch(field).toString(TimeScalesFactory.getTAI()));
323         Assert.assertEquals("1999-08-21T23:59:47.000",
324                             FieldAbsoluteDate.getGalileoEpoch(field).toString(TimeScalesFactory.getUTC()));
325         Assert.assertEquals("1980-01-06T00:00:00.000",
326                             FieldAbsoluteDate.getGPSEpoch(field).toString(TimeScalesFactory.getUTC()));
327         Assert.assertEquals("2000-01-01T12:00:00.000",
328                             FieldAbsoluteDate.getJ2000Epoch(field).toString(TimeScalesFactory.getTT()));
329         Assert.assertEquals("1970-01-01T00:00:00.000",
330                             FieldAbsoluteDate.getJavaEpoch(field).toString(TimeScalesFactory.getUTC()));
331     }
332 
333     private <T extends CalculusFieldElement<T>> void doTestJulianEpochRate(final Field<T> field) {
334 
335         for (int i = 0; i < 10; ++i) {
336             FieldAbsoluteDate<T> j200i = FieldAbsoluteDate.createJulianEpoch(field.getZero().add(2000.0+i));
337             FieldAbsoluteDate<T> j2000 = FieldAbsoluteDate.getJ2000Epoch(field);
338             double expected    = i * Constants.JULIAN_YEAR;
339             Assert.assertEquals(expected, j200i.durationFrom(j2000).getReal(), 4.0e-15 * expected);
340         }
341 
342     }
343 
344     private <T extends CalculusFieldElement<T>> void doTestBesselianEpochRate(final Field<T> field) {
345 
346         for (int i = 0; i < 10; ++i) {
347             FieldAbsoluteDate<T> b195i = FieldAbsoluteDate.createBesselianEpoch(field.getZero().add(1950.0 + i));
348             FieldAbsoluteDate<T> b1950 = FieldAbsoluteDate.createBesselianEpoch(field.getZero().add(1950.0));
349             double expected    = i * Constants.BESSELIAN_YEAR;
350             Assert.assertEquals(expected, b195i.durationFrom(b1950).getReal(), 4.0e-15 * expected);
351         }
352 
353     }
354 
355     private <T extends CalculusFieldElement<T>> void doTestLieske(final Field<T> field) {
356 
357         // the following test values correspond to table 1 in the paper:
358         // Precession Matrix Based on IAU (1976) System of Astronomical Constants,
359         // Jay H. Lieske, Astronomy and Astrophysics, vol. 73, no. 3, Mar. 1979, p. 282-284
360         // http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1979A%26A....73..282L&defaultprint=YES&filetype=.pdf
361 
362         // published table, with limited accuracy
363 
364 
365         final double publishedEpsilon = 1.0e-6 * Constants.JULIAN_YEAR;
366         checkEpochs(field, 1899.999142, 1900.000000, publishedEpsilon);
367         checkEpochs(field, 1900.000000, 1900.000858, publishedEpsilon);
368         checkEpochs(field, 1950.000000, 1949.999790, publishedEpsilon);
369         checkEpochs(field, 1950.000210, 1950.000000, publishedEpsilon);
370         checkEpochs(field, 2000.000000, 1999.998722, publishedEpsilon);
371         checkEpochs(field, 2000.001278, 2000.000000, publishedEpsilon);
372 
373         // recomputed table, using directly Lieske formulas (i.e. *not* Orekit implementation) with high accuracy
374         final double accurateEpsilon = 1.2e-13 * Constants.JULIAN_YEAR;
375         checkEpochs(field, 1899.99914161068724704, 1900.00000000000000000, accurateEpsilon);
376         checkEpochs(field, 1900.00000000000000000, 1900.00085837097878165, accurateEpsilon);
377         checkEpochs(field, 1950.00000000000000000, 1949.99979044229979466, accurateEpsilon);
378         checkEpochs(field, 1950.00020956217615449, 1950.00000000000000000, accurateEpsilon);
379         checkEpochs(field, 2000.00000000000000000, 1999.99872251362080766, accurateEpsilon);
380         checkEpochs(field, 2000.00127751366506194, 2000.00000000000000000, accurateEpsilon);
381 
382     }
383 
384     private <T extends CalculusFieldElement<T>> void checkEpochs(final Field<T> field, final double besselianEpoch, final double julianEpoch, final double epsilon) {
385         final FieldAbsoluteDate<T> b = FieldAbsoluteDate.createBesselianEpoch(field.getZero().add(besselianEpoch));
386         final FieldAbsoluteDate<T> j = FieldAbsoluteDate.createJulianEpoch(field.getZero().add(julianEpoch));
387         Assert.assertEquals(0.0, b.durationFrom(j).getReal(), epsilon);
388     }
389 
390     private <T extends CalculusFieldElement<T>> void doTestParse(final Field<T> field) {
391 
392         Assert.assertEquals(FieldAbsoluteDate.getModifiedJulianEpoch(field),
393                             new FieldAbsoluteDate<>(field, "1858-W46-3", TimeScalesFactory.getTT()));
394         Assert.assertEquals(FieldAbsoluteDate.getJulianEpoch(field),
395                             new FieldAbsoluteDate<>(field, "-4712-01-01T12:00:00.000", TimeScalesFactory.getTT()));
396         Assert.assertEquals(FieldAbsoluteDate.getFiftiesEpoch(field),
397                             new FieldAbsoluteDate<>(field, "1950-01-01", TimeScalesFactory.getTT()));
398         Assert.assertEquals(FieldAbsoluteDate.getCCSDSEpoch(field),
399                             new FieldAbsoluteDate<>(field, "1958-001", TimeScalesFactory.getTAI()));
400     }
401 
402     private <T extends CalculusFieldElement<T>> void doTestLocalTimeParsing(final Field<T> field) {
403         TimeScale utc = TimeScalesFactory.getUTC();
404         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2011-12-31T23:00:00",       utc),
405                             new FieldAbsoluteDate<>(field, "2012-01-01T03:30:00+04:30", utc));
406         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2011-12-31T23:00:00",       utc),
407                             new FieldAbsoluteDate<>(field, "2012-01-01T03:30:00+0430",  utc));
408         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2011-12-31T23:30:00",       utc),
409                             new FieldAbsoluteDate<>(field, "2012-01-01T03:30:00+04",    utc));
410         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2012-01-01T05:17:00",       utc),
411                             new FieldAbsoluteDate<>(field, "2011-12-31T22:17:00-07:00", utc));
412         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2012-01-01T05:17:00",       utc),
413                             new FieldAbsoluteDate<>(field, "2011-12-31T22:17:00-0700",  utc));
414         Assert.assertEquals(new FieldAbsoluteDate<>(field, "2012-01-01T05:17:00",       utc),
415                             new FieldAbsoluteDate<>(field, "2011-12-31T22:17:00-07",    utc));
416     }
417 
418     private <T extends CalculusFieldElement<T>> void doTestTimeZoneDisplay(final Field<T> field) {
419         final TimeScale utc = TimeScalesFactory.getUTC();
420         final FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, "2000-01-01T01:01:01.000", utc);
421         Assert.assertEquals("2000-01-01T01:01:01.000Z",      date.toString());
422         Assert.assertEquals("2000-01-01T11:01:01.000+10:00", date.toString( 600));
423         Assert.assertEquals("1999-12-31T23:01:01.000-02:00", date.toString(-120));
424 
425         // winter time, Europe is one hour ahead of UTC
426         final TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
427         Assert.assertEquals("2001-01-22T11:30:00.000+01:00",
428                             new FieldAbsoluteDate<>(field, "2001-01-22T10:30:00", utc).toString(tz));
429 
430         // summer time, Europe is two hours ahead of UTC
431         Assert.assertEquals("2001-06-23T11:30:00.000+02:00",
432                             new FieldAbsoluteDate<>(field, "2001-06-23T09:30:00", utc).toString(tz));
433 
434     }
435 
436     private <T extends CalculusFieldElement<T>> void doTestLocalTimeLeapSecond(final Field<T> field) throws IOException {
437 
438         TimeScale utc = TimeScalesFactory.getUTC();
439         FieldAbsoluteDate<T> beforeLeap = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:59.8", utc);
440         FieldAbsoluteDate<T> inLeap     = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:60.5", utc);
441         Assert.assertEquals(0.7, inLeap.durationFrom(beforeLeap).getReal(), 1.0e-12);
442        for (int minutesFromUTC = -1500; minutesFromUTC < -1499; ++minutesFromUTC) {
443             DateTimeComponents dtcBeforeLeap = beforeLeap.getComponents(minutesFromUTC);
444             DateTimeComponents dtcInsideLeap = inLeap.getComponents(minutesFromUTC);
445 
446 
447             Assert.assertEquals(dtcBeforeLeap.getDate(), dtcInsideLeap.getDate());
448 
449             Assert.assertEquals(dtcBeforeLeap.getTime().getHour(), dtcInsideLeap.getTime().getHour());
450             Assert.assertEquals(dtcBeforeLeap.getTime().getMinute(), dtcInsideLeap.getTime().getMinute());
451             Assert.assertEquals(minutesFromUTC, dtcBeforeLeap.getTime().getMinutesFromUTC());
452             Assert.assertEquals(minutesFromUTC, dtcInsideLeap.getTime().getMinutesFromUTC());
453             Assert.assertEquals(59.8, dtcBeforeLeap.getTime().getSecond(), 1.0e-10);
454             Assert.assertEquals(60.5, dtcInsideLeap.getTime().getSecond(), 1.0e-10);
455         }
456 
457     }
458 
459     private <T extends CalculusFieldElement<T>> void doTestTimeZoneLeapSecond(final Field<T> field) {
460 
461         TimeScale utc = TimeScalesFactory.getUTC();
462         final TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
463         FieldAbsoluteDate<T> localBeforeMidnight = new FieldAbsoluteDate<>(field, "2012-06-30T21:59:59.800", utc);
464         Assert.assertEquals("2012-06-30T23:59:59.800+02:00",
465                             localBeforeMidnight.toString(tz));
466         Assert.assertEquals("2012-07-01T00:00:00.800+02:00",
467                             localBeforeMidnight.shiftedBy(1.0).toString(tz));
468 
469         FieldAbsoluteDate<T> beforeLeap = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:59.8", utc);
470         FieldAbsoluteDate<T> inLeap     = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:60.5", utc);
471         Assert.assertEquals(0.7, inLeap.durationFrom(beforeLeap).getReal(), 1.0e-12);
472         Assert.assertEquals("2012-07-01T01:59:59.800+02:00", beforeLeap.toString(tz));
473         Assert.assertEquals("2012-07-01T01:59:60.500+02:00", inLeap.toString(tz));
474 
475     }
476 
477     private <T extends CalculusFieldElement<T>> void doTestParseLeap(final Field<T> field) {
478         TimeScale utc = TimeScalesFactory.getUTC();
479         FieldAbsoluteDate<T> beforeLeap = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:59.8", utc);
480         FieldAbsoluteDate<T> inLeap     = new FieldAbsoluteDate<>(field, "2012-06-30T23:59:60.5", utc);
481         Assert.assertEquals(0.7, inLeap.durationFrom(beforeLeap).getReal(), 1.0e-12);
482         Assert.assertEquals("2012-06-30T23:59:60.500", inLeap.toString(utc));
483     }
484 
485     private <T extends CalculusFieldElement<T>> void doTestOutput(final Field<T> field) {
486         TimeScale tt = TimeScalesFactory.getTT();
487         Assert.assertEquals("1950-01-01T01:01:01.000",
488                             FieldAbsoluteDate.getFiftiesEpoch(field).shiftedBy(3661.0).toString(tt));
489         Assert.assertEquals("2000-01-01T13:01:01.000",
490                             FieldAbsoluteDate.getJ2000Epoch(field).shiftedBy(3661.0).toString(tt));
491     }
492 
493     private <T extends CalculusFieldElement<T>> void doTestJ2000(final Field<T> field) {
494         FieldAbsoluteDate<T> FAD = new FieldAbsoluteDate<>(field);
495         Assert.assertEquals("2000-01-01T12:00:00.000",
496                             FAD.toString(TimeScalesFactory.getTT()));
497         Assert.assertEquals("2000-01-01T11:59:27.816",
498                             FAD.toString(TimeScalesFactory.getTAI()));
499         Assert.assertEquals("2000-01-01T11:58:55.816",
500                             FAD.toString(utc));
501         Assert.assertEquals("2000-01-01T12:00:00.000",
502                             FieldAbsoluteDate.getJ2000Epoch(field).toString(TimeScalesFactory.getTT()));
503         Assert.assertEquals("2000-01-01T11:59:27.816",
504                             FieldAbsoluteDate.getJ2000Epoch(field).toString(TimeScalesFactory.getTAI()));
505         Assert.assertEquals("2000-01-01T11:58:55.816",
506                             FieldAbsoluteDate.getJ2000Epoch(field).toString(utc));
507     }
508 
509     private <T extends CalculusFieldElement<T>> void doTestFraction(final Field<T> field) {
510         FieldAbsoluteDate<T> d =
511             new FieldAbsoluteDate<>(field, new DateComponents(2000, 01, 01), new TimeComponents(11, 59, 27.816),
512                              TimeScalesFactory.getTAI());
513         Assert.assertEquals(0, d.durationFrom(FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), 1.0e-10);
514     }
515 
516     private <T extends CalculusFieldElement<T>> void doTestScalesOffset(final Field<T> field) {
517         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2006, 02, 24),
518                                                             new TimeComponents(15, 38, 00),
519                                                             utc);
520         Assert.assertEquals(33,
521                             date.timeScalesOffset(TimeScalesFactory.getTAI(), utc).getReal(),
522                             1.0e-10);
523     }
524 
525     private <T extends CalculusFieldElement<T>> void doTestUTC(final Field<T> field) {
526         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new DateComponents(2002, 01, 01),
527                                                             new TimeComponents(00, 00, 01),
528                                                             utc);
529         Assert.assertEquals("2002-01-01T00:00:01.000Z", date.toString());
530     }
531 
532     private <T extends CalculusFieldElement<T>> void doTest1970(final Field<T> field) {
533         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, new Date(0l), utc);
534         Assert.assertEquals("1970-01-01T00:00:00.000Z", date.toString());
535     }
536 
537     private <T extends CalculusFieldElement<T>> void doTestUtcGpsOffset(final Field<T> field) {
538         FieldAbsoluteDate<T> date1   = new FieldAbsoluteDate<>(field, new DateComponents(2005, 8, 9),
539                                                                new TimeComponents(16, 31, 17),
540                                                                utc);
541         FieldAbsoluteDate<T> date2   = new FieldAbsoluteDate<>(field, new DateComponents(2006, 8, 9),
542                                                                new TimeComponents(16, 31, 17),
543                                                                utc);
544         FieldAbsoluteDate<T> dateRef = new FieldAbsoluteDate<>(field, new DateComponents(1980, 1, 6),
545                                                                TimeComponents.H00,
546                                                                utc);
547 
548         // 13 seconds offset between GPS time and UTC in 2005
549         long noLeapGap = ((9347 * 24 + 16) * 60 + 31) * 60 + 17;
550         long realGap   = (long) date1.durationFrom(dateRef).getReal();
551         Assert.assertEquals(13l, realGap - noLeapGap);
552 
553         // 14 seconds offset between GPS time and UTC in 2006
554         noLeapGap = ((9712 * 24 + 16) * 60 + 31) * 60 + 17;
555         realGap   = (long) date2.durationFrom(dateRef).getReal();
556         Assert.assertEquals(14l, realGap - noLeapGap);
557 
558     }
559 
560     private <T extends CalculusFieldElement<T>> void doTestGpsDate(final Field<T> field) {
561         FieldAbsoluteDate<T> date = FieldAbsoluteDate.createGPSDate(1387, field.getZero().add(318677000.0));
562         FieldAbsoluteDate<T> ref  = new FieldAbsoluteDate<>(field, new DateComponents(2006, 8, 9),
563                                                             new TimeComponents(16, 31, 03),
564                                                             utc);
565         Assert.assertEquals(0, date.durationFrom(ref).getReal(), 1.0e-15);
566     }
567 
568     private <T extends CalculusFieldElement<T>> void doTestMJDDate(final Field<T> field) {
569         FieldAbsoluteDate<T> dateA = FieldAbsoluteDate.createMJDDate(51544, field.getZero().add(0.5 * Constants.JULIAN_DAY),
570                                                                      TimeScalesFactory.getTT());
571         Assert.assertEquals(0.0, FieldAbsoluteDate.getJ2000Epoch(field).durationFrom(dateA).getReal(), 1.0e-15);
572         FieldAbsoluteDate<T> dateB = FieldAbsoluteDate.createMJDDate(53774, field.getZero(), TimeScalesFactory.getUTC());
573         FieldAbsoluteDate<T> dateC = new FieldAbsoluteDate<>(field, "2006-02-08T00:00:00", TimeScalesFactory.getUTC());
574         Assert.assertEquals(0.0, dateC.durationFrom(dateB).getReal(), 1.0e-15);
575     }
576 
577     private <T extends CalculusFieldElement<T>> void doTestJDDate(final Field<T> field) {
578         final FieldAbsoluteDate<T> date = FieldAbsoluteDate.createJDDate(2400000, field.getZero().add(0.5 * Constants.JULIAN_DAY),
579                                                                          TimeScalesFactory.getTT());
580         Assert.assertEquals(0.0, FieldAbsoluteDate.getModifiedJulianEpoch(field).durationFrom(date).getReal(), 1.0e-15);
581     }
582 
583     private <T extends CalculusFieldElement<T>> void doTestOffsets(final Field<T> field) {
584         final TimeScale tai = TimeScalesFactory.getTAI();
585         FieldAbsoluteDate<T> leapStartUTC = new FieldAbsoluteDate<>(field, 1976, 12, 31, 23, 59, 59, utc);
586         FieldAbsoluteDate<T> leapEndUTC   = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0,  0, utc);
587         FieldAbsoluteDate<T> leapStartTAI = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0, 14, tai);
588         FieldAbsoluteDate<T> leapEndTAI   = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0, 16, tai);
589         Assert.assertEquals(leapStartUTC, leapStartTAI);
590         Assert.assertEquals(leapEndUTC, leapEndTAI);
591         Assert.assertEquals(1, leapEndUTC.offsetFrom(leapStartUTC, utc).getReal(), 1.0e-10);
592         Assert.assertEquals(1, leapEndTAI.offsetFrom(leapStartTAI, utc).getReal(), 1.0e-10);
593         Assert.assertEquals(2, leapEndUTC.offsetFrom(leapStartUTC, tai).getReal(), 1.0e-10);
594         Assert.assertEquals(2, leapEndTAI.offsetFrom(leapStartTAI, tai).getReal(), 1.0e-10);
595         Assert.assertEquals(2, leapEndUTC.durationFrom(leapStartUTC).getReal(),    1.0e-10);
596         Assert.assertEquals(2, leapEndTAI.durationFrom(leapStartTAI).getReal(),    1.0e-10);
597     }
598 
599     private <T extends CalculusFieldElement<T>> void doTestBeforeAndAfterLeap(final Field<T> field) {
600         final TimeScale tai = TimeScalesFactory.getTAI();
601         FieldAbsoluteDate<T> leapStart = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0, 14, tai);
602         FieldAbsoluteDate<T> leapEnd   = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0, 16, tai);
603         for (int i = -10; i < 10; ++i) {
604             final double dt = 1.1 * (2 * i - 1);
605             FieldAbsoluteDate<T> d1 = leapStart.shiftedBy(dt);
606             FieldAbsoluteDate<T> d2 = new FieldAbsoluteDate<>(leapStart, dt, tai);
607             FieldAbsoluteDate<T> d3 = new FieldAbsoluteDate<>(leapStart, dt, utc);
608             FieldAbsoluteDate<T> d4 = new FieldAbsoluteDate<>(leapEnd,   dt, tai);
609             FieldAbsoluteDate<T> d5 = new FieldAbsoluteDate<>(leapEnd,   dt, utc);
610             Assert.assertTrue(FastMath.abs(d1.durationFrom(d2).getReal()) < 1.0e-10);
611             if (dt < 0) {
612                 Assert.assertTrue(FastMath.abs(d2.durationFrom(d3).getReal()) < 1.0e-10);
613                 Assert.assertTrue(d4.durationFrom(d5).getReal() > (1.0 - 1.0e-10));
614             } else {
615                 Assert.assertTrue(d2.durationFrom(d3).getReal() < (-1.0 + 1.0e-10));
616                 Assert.assertTrue(FastMath.abs(d4.durationFrom(d5).getReal()) < 1.0e-10);
617             }
618         }
619     }
620 
621     private <T extends CalculusFieldElement<T>> void doTestSymmetry(final Field<T> field) {
622         final TimeScale tai = TimeScalesFactory.getTAI();
623         FieldAbsoluteDate<T> leapStart = new FieldAbsoluteDate<>(field, 1977,  1,  1,  0,  0, 14, tai);
624         for (int i = -10; i < 10; ++i) {
625             final double dt = 1.1 * (2 * i - 1);
626             Assert.assertEquals(dt, new FieldAbsoluteDate<>(leapStart, dt, utc).offsetFrom(leapStart, utc).getReal(), 1.0e-10);
627             Assert.assertEquals(dt, new FieldAbsoluteDate<>(leapStart, dt, tai).offsetFrom(leapStart, tai).getReal(), 1.0e-10);
628             Assert.assertEquals(dt, leapStart.shiftedBy(dt).durationFrom(leapStart).getReal(), 1.0e-10);
629         }
630     }
631 
632     @SuppressWarnings("unlikely-arg-type")
633     private <T extends CalculusFieldElement<T>> void doTestEquals(final Field<T> field) {
634         FieldAbsoluteDate<T> d1 =
635             new FieldAbsoluteDate<>(field, new DateComponents(2006, 2, 25),
636                                     new TimeComponents(17, 10, 34),
637                                     utc);
638         FieldAbsoluteDate<T> d2 = new FieldAbsoluteDate<>(field, new DateComponents(2006, 2, 25),
639                                                           new TimeComponents(17, 10, 0),
640                                                           utc).shiftedBy(34);
641         Assert.assertTrue(d1.equals(d2));
642         Assert.assertFalse(d1.equals(this));
643     }
644 
645     private <T extends CalculusFieldElement<T>> void doTestIsEqualTo(final Field<T> field) {
646         TestDates<T> dates = new TestDates<>(field);
647         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
648         Assert.assertTrue(present.isEqualTo(dates.present));
649         Assert.assertTrue(present.isEqualTo(dates.presentToo));
650         Assert.assertFalse(present.isEqualTo(dates.past));
651         Assert.assertFalse(present.isEqualTo(dates.future));
652     }
653 
654     private <T extends CalculusFieldElement<T>> void doTestIsCloseTo(final Field<T> field) {
655         double tolerance = 10;
656         TestDates<T> dates = new TestDates<>(field);
657         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
658         FieldTimeStamped<T> closeToPresent = new AnyFieldTimeStamped<>(field, dates.presentDate.shiftedBy(5));
659         Assert.assertTrue(present.isCloseTo(present, tolerance));
660         Assert.assertTrue(present.isCloseTo(dates.presentToo, tolerance));
661         Assert.assertTrue(present.isCloseTo(closeToPresent, tolerance));
662         Assert.assertFalse(present.isCloseTo(dates.past, tolerance));
663         Assert.assertFalse(present.isCloseTo(dates.future, tolerance));
664     }
665 
666     private <T extends CalculusFieldElement<T>> void doTestIsBefore(final Field<T> field) {
667         TestDates<T> dates = new TestDates<>(field);
668         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
669         Assert.assertFalse(present.isBefore(dates.past));
670         Assert.assertFalse(present.isBefore(present));
671         Assert.assertFalse(present.isBefore(dates.presentToo));
672         Assert.assertTrue(present.isBefore(dates.future));
673     }
674 
675     private <T extends CalculusFieldElement<T>> void doTestIsAfter(final Field<T> field) {
676         TestDates<T> dates = new TestDates<>(field);
677         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
678         Assert.assertTrue(present.isAfter(dates.past));
679         Assert.assertFalse(present.isAfter(present));
680         Assert.assertFalse(present.isAfter(dates.presentToo));
681         Assert.assertFalse(present.isAfter(dates.future));
682     }
683 
684     private <T extends CalculusFieldElement<T>> void doTestIsBeforeOrEqualTo(final Field<T> field) {
685         TestDates<T> dates = new TestDates<>(field);
686         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
687         Assert.assertFalse(present.isBeforeOrEqualTo(dates.past));
688         Assert.assertTrue(present.isBeforeOrEqualTo(present));
689         Assert.assertTrue(present.isBeforeOrEqualTo(dates.presentToo));
690         Assert.assertTrue(present.isBeforeOrEqualTo(dates.future));
691     }
692 
693     private <T extends CalculusFieldElement<T>> void doTestIsAfterOrEqualTo(final Field<T> field) {
694         TestDates<T> dates = new TestDates<>(field);
695         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
696         Assert.assertTrue(present.isAfterOrEqualTo(dates.past));
697         Assert.assertTrue(present.isAfterOrEqualTo(present));
698         Assert.assertTrue(present.isAfterOrEqualTo(dates.presentToo));
699         Assert.assertFalse(present.isAfterOrEqualTo(dates.future));
700     }
701 
702     private <T extends CalculusFieldElement<T>> void doTestIsBetween(final Field<T> field) {
703         TestDates<T> dates = new TestDates<>(field);
704         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
705         Assert.assertTrue(present.isBetween(dates.past, dates.future));
706         Assert.assertTrue(present.isBetween(dates.future, dates.past));
707         Assert.assertFalse(dates.past.getDate().isBetween(present, dates.future));
708         Assert.assertFalse(dates.past.getDate().isBetween(dates.future, present));
709         Assert.assertFalse(dates.future.getDate().isBetween(dates.past, present));
710         Assert.assertFalse(dates.future.getDate().isBetween(present, dates.past));
711         Assert.assertFalse(present.isBetween(present, dates.future));
712         Assert.assertFalse(present.isBetween(dates.past, present));
713         Assert.assertFalse(present.isBetween(dates.past, dates.past));
714         Assert.assertFalse(present.isBetween(present, present));
715         Assert.assertFalse(present.isBetween(present, dates.presentToo));
716     }
717 
718     private <T extends CalculusFieldElement<T>> void doTestIsBetweenOrEqualTo(final Field<T> field) {
719         TestDates<T> dates = new TestDates<>(field);
720         FieldAbsoluteDate<T> present = dates.getPresentFieldAbsoluteDate();
721         Assert.assertTrue(present.isBetweenOrEqualTo(dates.past, dates.future));
722         Assert.assertTrue(present.isBetweenOrEqualTo(dates.future, dates.past));
723         Assert.assertFalse(dates.past.getDate().isBetweenOrEqualTo(present, dates.future));
724         Assert.assertFalse(dates.past.getDate().isBetweenOrEqualTo(dates.future, present));
725         Assert.assertFalse(dates.future.getDate().isBetweenOrEqualTo(dates.past, present));
726         Assert.assertFalse(dates.future.getDate().isBetweenOrEqualTo(present, dates.past));
727         Assert.assertTrue(present.isBetweenOrEqualTo(present, dates.future));
728         Assert.assertTrue(present.isBetweenOrEqualTo(dates.past, present));
729         Assert.assertFalse(present.isBetweenOrEqualTo(dates.past, dates.past));
730         Assert.assertTrue(present.isBetweenOrEqualTo(present, present));
731         Assert.assertTrue(present.isBetweenOrEqualTo(present, dates.presentToo));
732     }
733 
734     private <T extends CalculusFieldElement<T>> void doTestComponents(final Field<T> field) {
735         // this is NOT J2000.0,
736         // it is either a few seconds before or after depending on time scale
737         DateComponents date = new DateComponents(2000, 1, 1);
738         TimeComponents time = new TimeComponents(11, 59, 10);
739         TimeScale[] scales = {
740             TimeScalesFactory.getTAI(), TimeScalesFactory.getUTC(),
741             TimeScalesFactory.getTT(), TimeScalesFactory.getTCG()
742         };
743         for (int i = 0; i < scales.length; ++i) {
744             FieldAbsoluteDate<T> in = new FieldAbsoluteDate<>(field, date, time, scales[i]);
745             for (int j = 0; j < scales.length; ++j) {
746                 DateTimeComponents pair = in.getComponents(scales[j]);
747                 if (i == j) {
748                     Assert.assertEquals(date, pair.getDate());
749                     Assert.assertEquals(time, pair.getTime());
750                 } else {
751                     Assert.assertNotSame(date, pair.getDate());
752                     Assert.assertNotSame(time, pair.getTime());
753                 }
754             }
755         }
756     }
757 
758     private <T extends CalculusFieldElement<T>> void doTestMonth(final Field<T> field) {
759         TimeScale utc = TimeScalesFactory.getUTC();
760         Assert.assertEquals(new FieldAbsoluteDate<>(field, 2011, 2, 23, utc),
761                             new FieldAbsoluteDate<>(field, 2011, Month.FEBRUARY, 23, utc));
762         Assert.assertEquals(new FieldAbsoluteDate<>(field, 2011, 2, 23, 1, 2, 3.4, utc),
763                             new FieldAbsoluteDate<>(field, 2011, Month.FEBRUARY, 23, 1, 2, 3.4, utc));
764     }
765 
766     private <T extends CalculusFieldElement<T>> void doTestCCSDSUnsegmentedNoExtension(final Field<T> field) {
767         FieldAbsoluteDate<T> reference = new FieldAbsoluteDate<>(field, "2002-05-23T12:34:56.789", utc);
768         double lsb = FastMath.pow(2.0, -24);
769 
770         byte[] timeCCSDSEpoch = new byte[] { 0x53, 0x7F, 0x40, -0x70, -0x37, -0x05, -0x19 };
771         for (int preamble = 0x00; preamble < 0x80; ++preamble) {
772             if (preamble == 0x1F) {
773                 // using CCSDS reference epoch
774                 FieldAbsoluteDate<T> ccsds1 =
775                                 FieldAbsoluteDate.parseCCSDSUnsegmentedTimeCode(field, (byte) preamble, (byte) 0x0, timeCCSDSEpoch, null);
776                 Assert.assertEquals(0, ccsds1.durationFrom(reference).getReal(), lsb / 2);
777             } else {
778                 try {
779                     FieldAbsoluteDate.parseCCSDSUnsegmentedTimeCode(field, (byte) preamble, (byte) 0x0, timeCCSDSEpoch, null);
780                     Assert.fail("an exception should have been thrown");
781                 } catch (OrekitException iae) {
782                     // expected
783                 }
784 
785             }
786         }
787 
788         // missing epoch
789         byte[] timeJ2000Epoch = new byte[] { 0x04, 0x7E, -0x0B, -0x10, -0x07, 0x16, -0x79 };
790         try {
791             FieldAbsoluteDate.parseCCSDSUnsegmentedTimeCode(field, (byte) 0x2F, (byte) 0x0, timeJ2000Epoch, null);
792             Assert.fail("an exception should have been thrown");
793         } catch (OrekitException iae) {
794             // expected
795         }
796 
797         // using J2000.0 epoch
798         FieldAbsoluteDate<T> ccsds3 =
799                         FieldAbsoluteDate.parseCCSDSUnsegmentedTimeCode(field, (byte) 0x2F, (byte) 0x0, timeJ2000Epoch,
800                                                                         FieldAbsoluteDate.getJ2000Epoch(field));
801         Assert.assertEquals(0, ccsds3.durationFrom(reference).getReal(), lsb / 2);
802 
803     }
804 
805     private <T extends CalculusFieldElement<T>> void doTestCCSDSUnsegmentedWithExtendedPreamble(final Field<T> field) {
806 
807         FieldAbsoluteDate<T> reference = new FieldAbsoluteDate<>(field, "2095-03-03T22:02:45.789012345678901", utc);
808         int leap = (int) FastMath.rint(utc.offsetFromTAI(reference.toAbsoluteDate()));
809         double lsb = FastMath.pow(2.0, -48);
810 
811         byte extendedPreamble = (byte) -0x80;
812         byte identification   = (byte)  0x10;
813         byte coarseLength1    = (byte)  0x0C; // four (3 + 1) bytes
814         byte fineLength1      = (byte)  0x03; // 3 bytes
815         byte coarseLength2    = (byte)  0x20; // 1 additional byte for coarse time
816         byte fineLength2      = (byte)  0x0C; // 3 additional bytes for fine time
817         byte[] timeCCSDSEpoch = new byte[] {
818              0x01,  0x02,  0x03,  0x04,  (byte)(0x05 - leap), // 5 bytes for coarse time (seconds)
819             -0x37, -0x04, -0x4A, -0x74, -0x2C, -0x3C          // 6 bytes for fine time (sub-seconds)
820         };
821         byte preamble1 = (byte) (extendedPreamble | identification | coarseLength1 | fineLength1);
822         byte preamble2 = (byte) (coarseLength2 | fineLength2);
823         FieldAbsoluteDate<T> ccsds1 =
824                         FieldAbsoluteDate.parseCCSDSUnsegmentedTimeCode(field, preamble1, preamble2, timeCCSDSEpoch, null);
825         Assert.assertEquals(0, ccsds1.durationFrom(reference).getReal(), lsb / 2);
826 
827     }
828 
829     private <T extends CalculusFieldElement<T>> void doTestCCSDSDaySegmented(final Field<T> field) {
830         FieldAbsoluteDate<T> reference = new FieldAbsoluteDate<>(field, "2002-05-23T12:34:56.789012345678", TimeScalesFactory.getUTC());
831         double lsb = 1.0e-13;
832         byte[] timeCCSDSEpoch = new byte[] { 0x3F, 0x55, 0x02, -0x4D, 0x2C, -0x6B, 0x00, -0x44, 0x61, 0x4E };
833 
834         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
835             if (preamble == 0x42) {
836                 // using CCSDS reference epoch
837 
838                 FieldAbsoluteDate<T> ccsds1 =
839                                 FieldAbsoluteDate.parseCCSDSDaySegmentedTimeCode(field, (byte) preamble, timeCCSDSEpoch, null);
840                 Assert.assertEquals(0, ccsds1.durationFrom(reference).getReal(), lsb / 2);
841             } else {
842                 try {
843                     FieldAbsoluteDate.parseCCSDSDaySegmentedTimeCode(field, (byte) preamble, timeCCSDSEpoch, null);
844                     Assert.fail("an exception should have been thrown");
845                 } catch (OrekitException iae) {
846                     // expected
847                 }
848 
849             }
850         }
851 
852         // missing epoch
853         byte[] timeJ2000Epoch = new byte[] { 0x03, 0x69, 0x02, -0x4D, 0x2C, -0x6B, 0x00, -0x44, 0x61, 0x4E };
854         try {
855             FieldAbsoluteDate.parseCCSDSDaySegmentedTimeCode(field, (byte) 0x4A, timeJ2000Epoch, null);
856             Assert.fail("an exception should have been thrown");
857         } catch (OrekitException iae) {
858             // expected
859         }
860 
861         // using J2000.0 epoch
862         FieldAbsoluteDate<T> ccsds3 =
863                         FieldAbsoluteDate.parseCCSDSDaySegmentedTimeCode(field, (byte) 0x4A, timeJ2000Epoch, DateComponents.J2000_EPOCH);
864         Assert.assertEquals(0, ccsds3.durationFrom(reference).getReal(), lsb / 2);
865 
866         // limit to microsecond
867         byte[] timeMicrosecond = new byte[] { 0x03, 0x69, 0x02, -0x4D, 0x2C, -0x6B, 0x00, 0x0C };
868         FieldAbsoluteDate<T> ccsds4 =
869                         FieldAbsoluteDate.parseCCSDSDaySegmentedTimeCode(field, (byte) 0x49, timeMicrosecond, DateComponents.J2000_EPOCH);
870         Assert.assertEquals(-0.345678e-6, ccsds4.durationFrom(reference).getReal(), lsb / 2);
871 
872     }
873 
874     private <T extends CalculusFieldElement<T>> void doTestCCSDSCalendarSegmented(final Field<T> field) {
875 
876         FieldAbsoluteDate<T> reference = new FieldAbsoluteDate<>(field, "2002-05-23T12:34:56.789012345678", TimeScalesFactory.getUTC());
877         double lsb = 1.0e-13;
878         FieldAbsoluteDate<T> FAD = new FieldAbsoluteDate<>(field);
879         // month of year / day of month variation
880         byte[] timeMonthDay = new byte[] { 0x07, -0x2E, 0x05, 0x17, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C, 0x22, 0x38, 0x4E };
881         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
882             if (preamble == 0x56) {
883                 FieldAbsoluteDate<T> ccsds1 =
884                     FAD.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeMonthDay);
885                 Assert.assertEquals(0, ccsds1.durationFrom(reference).getReal(), lsb / 2);
886             } else {
887                 try {
888                     FAD.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeMonthDay);
889                     Assert.fail("an exception should have been thrown");
890                 } catch (OrekitException iae) {
891                     // expected
892                 } catch (IllegalArgumentException iae) {
893                     // should happen when preamble specifies day of year variation
894                     // since there is no day 1303 (= 5 * 256 + 23) in any year ...
895                     Assert.assertEquals(preamble & 0x08, 0x08);
896                 }
897 
898             }
899         }
900 
901         // day of year variation
902         byte[] timeDay = new byte[] { 0x07, -0x2E, 0x00, -0x71, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C, 0x22, 0x38, 0x4E };
903         for (int preamble = 0x00; preamble < 0x100; ++preamble) {
904             if (preamble == 0x5E) {
905                 FieldAbsoluteDate<T> ccsds1 =
906                     FAD.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeDay);
907                 Assert.assertEquals(0, ccsds1.durationFrom(reference).getReal(), lsb / 2);
908             } else {
909                 try {
910                     FAD.parseCCSDSCalendarSegmentedTimeCode((byte) preamble, timeDay);
911                     Assert.fail("an exception should have been thrown");
912                 } catch (OrekitException iae) {
913                     // expected
914                 } catch (IllegalArgumentException iae) {
915                     // should happen when preamble specifies month of year / day of month variation
916                     // since there is no month 0 in any year ...
917                     Assert.assertEquals(preamble & 0x08, 0x00);
918                 }
919 
920             }
921         }
922 
923         // limit to microsecond
924         byte[] timeMicrosecond = new byte[] { 0x07, -0x2E, 0x00, -0x71, 0x0C, 0x22, 0x38, 0x4E, 0x5A, 0x0C };
925         FieldAbsoluteDate<T> ccsds4 =
926             FAD.parseCCSDSCalendarSegmentedTimeCode((byte) 0x5B, timeMicrosecond);
927         Assert.assertEquals(-0.345678e-6, ccsds4.durationFrom(reference).getReal(), lsb / 2);
928 
929     }
930 
931     private <T extends CalculusFieldElement<T>> void doTestExpandedConstructors(final Field<T> field) {
932         Assert.assertEquals(new FieldAbsoluteDate<>(field, new DateComponents(2002, 05, 28),
933                                                     new TimeComponents(15, 30, 0),
934                                                     TimeScalesFactory.getUTC()),
935                      new FieldAbsoluteDate<>(field, 2002, 05, 28, 15, 30, 0, TimeScalesFactory.getUTC()));
936         Assert.assertEquals(new FieldAbsoluteDate<>(field, new DateComponents(2002, 05, 28), TimeComponents.H00,
937                                                     TimeScalesFactory.getUTC()),
938                      new FieldAbsoluteDate<>(field, 2002, 05, 28, TimeScalesFactory.getUTC()));
939         try {
940             new FieldAbsoluteDate<>(field, 2002, 05, 28, 25, 30, 0, TimeScalesFactory.getUTC());
941         } catch (OrekitIllegalArgumentException oiae) {
942             Assert.assertEquals(OrekitMessages.NON_EXISTENT_HMS_TIME, oiae.getSpecifier());
943             Assert.assertEquals(25, ((Integer) oiae.getParts()[0]).intValue());
944             Assert.assertEquals(30, ((Integer) oiae.getParts()[1]).intValue());
945             Assert.assertEquals( 0, ((Double) oiae.getParts()[2]).doubleValue(), 1.0e-15);
946         }
947     }
948 
949     private <T extends CalculusFieldElement<T>> void doTestHashcode(final Field<T> field) {
950         FieldAbsoluteDate<T> d1 =
951             new FieldAbsoluteDate<>(field, new DateComponents(2006, 2, 25),
952                                     new TimeComponents(17, 10, 34),
953                                     utc);
954         FieldAbsoluteDate<T> d2 = new FieldAbsoluteDate<>(field, new DateComponents(2006, 2, 25),
955                                                           new TimeComponents(17, 10, 0),
956                                                           utc).shiftedBy(34);
957         Assert.assertEquals(d1.hashCode(), d2.hashCode());
958         Assert.assertTrue(d1.hashCode() != d1.shiftedBy(1.0e-3).hashCode());
959     }
960 
961     private <T extends CalculusFieldElement<T>> void doTestInfinity(final Field<T> field) {
962         Assert.assertTrue(FieldAbsoluteDate.getJulianEpoch(field).compareTo(FieldAbsoluteDate.getPastInfinity(field)) > 0);
963         Assert.assertTrue(FieldAbsoluteDate.getJulianEpoch(field).compareTo(FieldAbsoluteDate.getFutureInfinity(field)) < 0);
964         Assert.assertTrue(FieldAbsoluteDate.getJ2000Epoch(field).compareTo(FieldAbsoluteDate.getPastInfinity(field)) > 0);
965         Assert.assertTrue(FieldAbsoluteDate.getJ2000Epoch(field).compareTo(FieldAbsoluteDate.getFutureInfinity(field)) < 0);
966         Assert.assertTrue(FieldAbsoluteDate.getPastInfinity(field).compareTo(FieldAbsoluteDate.getJulianEpoch(field)) < 0);
967         Assert.assertTrue(FieldAbsoluteDate.getPastInfinity(field).compareTo(FieldAbsoluteDate.getJ2000Epoch(field)) < 0);
968         Assert.assertTrue(FieldAbsoluteDate.getPastInfinity(field).compareTo(FieldAbsoluteDate.getFutureInfinity(field)) < 0);
969         Assert.assertTrue(FieldAbsoluteDate.getFutureInfinity(field).compareTo(FieldAbsoluteDate.getJulianEpoch(field)) > 0);
970         Assert.assertTrue(FieldAbsoluteDate.getFutureInfinity(field).compareTo(FieldAbsoluteDate.getJ2000Epoch(field)) > 0);
971         Assert.assertTrue(FieldAbsoluteDate.getFutureInfinity(field).compareTo(FieldAbsoluteDate.getPastInfinity(field)) > 0);
972         Assert.assertTrue(Double.isInfinite(FieldAbsoluteDate.getFutureInfinity(field).durationFrom(FieldAbsoluteDate.getJ2000Epoch(field)).getReal()));
973         Assert.assertTrue(Double.isInfinite(FieldAbsoluteDate.getFutureInfinity(field).durationFrom(FieldAbsoluteDate.getPastInfinity(field)).getReal()));
974         Assert.assertTrue(Double.isInfinite(FieldAbsoluteDate.getPastInfinity(field).durationFrom(FieldAbsoluteDate.getJ2000Epoch(field)).getReal()));
975         Assert.assertEquals("5881610-07-11T23:59:59.999Z",  FieldAbsoluteDate.getFutureInfinity(field).toString());
976         Assert.assertEquals("-5877490-03-03T00:00:00.000Z", FieldAbsoluteDate.getPastInfinity(field).toString());
977 
978         final FieldAbsoluteDate<T> j2000     = FieldAbsoluteDate.getJ2000Epoch(field);
979         final FieldAbsoluteDate<T> arbitrary = FieldAbsoluteDate.getArbitraryEpoch(field);
980         Assert.assertTrue(j2000.durationFrom(arbitrary.shiftedBy(Double.NEGATIVE_INFINITY)).getReal()
981                           == Double.POSITIVE_INFINITY);
982         Assert.assertTrue(j2000.durationFrom(arbitrary.shiftedBy(Double.POSITIVE_INFINITY)).getReal()
983                           == Double.NEGATIVE_INFINITY);
984         Assert.assertTrue(j2000.durationFrom(arbitrary.shiftedBy(field.getZero().add(Double.NEGATIVE_INFINITY))).getReal()
985                           == Double.POSITIVE_INFINITY);
986         Assert.assertTrue(j2000.durationFrom(arbitrary.shiftedBy(field.getZero().add(Double.POSITIVE_INFINITY))).getReal()
987                           == Double.NEGATIVE_INFINITY);
988 
989     }
990 
991     private <T extends CalculusFieldElement<T>> void doTestAccuracy(final Field<T> field) {
992         TimeScale tai = TimeScalesFactory.getTAI();
993         double sec = 0.281;
994         FieldAbsoluteDate<T> t = new FieldAbsoluteDate<>(field, 2010, 6, 21, 18, 42, sec, tai);
995         double recomputedSec = t.getComponents(tai).getTime().getSecond();
996         Assert.assertEquals(sec, recomputedSec, FastMath.ulp(sec));
997     }
998 
999     private <T extends CalculusFieldElement<T>> void doTestAccuracyIssue348(final Field<T> field)
1000         {
1001         FieldAbsoluteDate<T> tF = new FieldAbsoluteDate<>(field,
1002                                                           new DateComponents(1970, 01, 01),
1003                                                           new TimeComponents(3, 25, 45.6789),
1004                                                           TimeScalesFactory.getUTC());
1005         AbsoluteDate tA = tF.toAbsoluteDate();
1006         double delta = -0.01;
1007         T recomputedDelta = tF.shiftedBy(delta).durationFrom(tA);
1008         Assert.assertEquals(delta, recomputedDelta.getReal(), 1.0e-17);
1009     }
1010 
1011     private <T extends CalculusFieldElement<T>> void doTestIterationAccuracy(final Field<T> field) {
1012 
1013         final TimeScale tai = TimeScalesFactory.getTAI();
1014         final FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2010, 6, 21, 18, 42, 0.281, tai);
1015 
1016         // 0.1 is not representable exactly in double precision
1017         // we will accumulate error, between -0.5ULP and -3ULP at each iteration
1018         checkIteration(0.1, t0, 10000, 3.0, -1.19, 1.0e-4);
1019 
1020         // 0.125 is representable exactly in double precision
1021         // error will be null
1022         checkIteration(0.125, t0, 10000, 1.0e-15, 0.0, 1.0e-15);
1023     }
1024 
1025     private <T extends CalculusFieldElement<T>> void checkIteration(final double step, final FieldAbsoluteDate<T> t0, final int nMax,
1026                                 final double maxErrorFactor,
1027                                 final double expectedMean, final double meanTolerance) {
1028         final double epsilon = FastMath.ulp(step);
1029         FieldAbsoluteDate<T> iteratedDate = t0;
1030         double mean = 0;
1031         for (int i = 1; i < nMax; ++i) {
1032             iteratedDate = iteratedDate.shiftedBy(step);
1033             FieldAbsoluteDate<T> directDate = t0.shiftedBy(i * step);
1034             final T error = iteratedDate.durationFrom(directDate);
1035             mean += error.getReal() / (i * epsilon);
1036             Assert.assertEquals(0.0, iteratedDate.durationFrom(directDate).getReal(), maxErrorFactor * i * epsilon);
1037         }
1038         mean /= nMax;
1039         Assert.assertEquals(expectedMean, mean, meanTolerance);
1040     }
1041 
1042     private <T extends CalculusFieldElement<T>> void doTestIssue142(final Field<T> field) {
1043         final FieldAbsoluteDate<T> epoch = FieldAbsoluteDate.getJavaEpoch(field);
1044         final TimeScale utc = TimeScalesFactory.getUTC();
1045 
1046         Assert.assertEquals("1970-01-01T00:00:00.000", epoch.toString(utc));
1047         Assert.assertEquals(0.0, epoch.durationFrom(new FieldAbsoluteDate<>(field, 1970, 1, 1, utc)).getReal(), 1.0e-15);
1048         Assert.assertEquals(8.000082,
1049                             epoch.durationFrom(new FieldAbsoluteDate<>(field, DateComponents.JAVA_EPOCH, TimeScalesFactory.getTAI())).getReal(),
1050                             1.0e-15);
1051 
1052         //Milliseconds - April 1, 2006, in UTC
1053         long msOffset = 1143849600000l;
1054         final FieldAbsoluteDate<T> ad = new FieldAbsoluteDate<>(epoch, msOffset / 1000, TimeScalesFactory.getUTC());
1055         Assert.assertEquals("2006-04-01T00:00:00.000", ad.toString(utc));
1056     }
1057 
1058     private <T extends CalculusFieldElement<T>> void doTestIssue148(final Field<T> field) {
1059         final TimeScale utc = TimeScalesFactory.getUTC();
1060         FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2012, 6, 30, 23, 59, 50.0, utc);
1061         DateTimeComponents components = t0.shiftedBy(11.0 - 200 * Precision.EPSILON).getComponents(utc);
1062         Assert.assertEquals(2012, components.getDate().getYear());
1063         Assert.assertEquals(   6, components.getDate().getMonth());
1064         Assert.assertEquals(  30, components.getDate().getDay());
1065         Assert.assertEquals(  23, components.getTime().getHour());
1066         Assert.assertEquals(  59, components.getTime().getMinute());
1067         Assert.assertEquals(  61 - 200 * Precision.EPSILON,
1068                             components.getTime().getSecond(), 1.0e-15);
1069     }
1070 
1071     private <T extends CalculusFieldElement<T>> void doTestIssue149(final Field<T> field) {
1072         final TimeScale utc = TimeScalesFactory.getUTC();
1073         FieldAbsoluteDate<T> t0 = new FieldAbsoluteDate<>(field, 2012, 6, 30, 23, 59, 59, utc);
1074         DateTimeComponents components = t0.shiftedBy(1.0 - Precision.EPSILON).getComponents(utc);
1075         Assert.assertEquals(2012, components.getDate().getYear());
1076         Assert.assertEquals(   6, components.getDate().getMonth());
1077         Assert.assertEquals(  30, components.getDate().getDay());
1078         Assert.assertEquals(  23, components.getTime().getHour());
1079         Assert.assertEquals(  59, components.getTime().getMinute());
1080         Assert.assertEquals(  60 - Precision.EPSILON,
1081                             components.getTime().getSecond(), 1.0e-15);
1082     }
1083 
1084     private <T extends CalculusFieldElement<T>> void doTestWrapAtMinuteEnd(final Field<T> field) {
1085         TimeScale tai = TimeScalesFactory.getTAI();
1086         TimeScale utc = TimeScalesFactory.getUTC();
1087         FieldAbsoluteDate<T> date0 = new FieldAbsoluteDate<>(field, DateComponents.J2000_EPOCH, TimeComponents.H12, tai);
1088         FieldAbsoluteDate<T> ref = date0.shiftedBy(496891466.0).shiftedBy(0.7320114066633323);
1089         FieldAbsoluteDate<T> date = ref.shiftedBy(33 * -597.9009700426262);
1090         DateTimeComponents dtc = date.getComponents(utc);
1091         Assert.assertEquals(2015, dtc.getDate().getYear());
1092         Assert.assertEquals(   9, dtc.getDate().getMonth());
1093         Assert.assertEquals(  30, dtc.getDate().getDay());
1094         Assert.assertEquals(   7, dtc.getTime().getHour());
1095         Assert.assertEquals(  54, dtc.getTime().getMinute());
1096         Assert.assertEquals(60 - 9.094947e-13, dtc.getTime().getSecond(), 1.0e-15);
1097         Assert.assertEquals("2015-09-30T07:54:59.99999999999909",
1098                             date.toString(utc));
1099         FieldAbsoluteDate<T> beforeMidnight = new FieldAbsoluteDate<>(field, 2008, 2, 29, 23, 59, 59.9994, utc);
1100         FieldAbsoluteDate<T> stillBeforeMidnight = beforeMidnight.shiftedBy(2.0e-4);
1101         Assert.assertEquals(59.9994, beforeMidnight.getComponents(utc).getTime().getSecond(), 1.0e-15);
1102         Assert.assertEquals(59.9996, stillBeforeMidnight.getComponents(utc).getTime().getSecond(), 1.0e-15);
1103         Assert.assertEquals("2008-02-29T23:59:59.9994", beforeMidnight.toString(utc));
1104         Assert.assertEquals("2008-02-29T23:59:59.9996", stillBeforeMidnight.toString(utc));
1105     }
1106 
1107     private <T extends CalculusFieldElement<T>> void doTestIssue508(final Field<T> field) {
1108         AbsoluteDate date = new AbsoluteDate(2000, 2, 24, 17, 5, 30.047, TimeScalesFactory.getUTC());
1109         FieldAbsoluteDate<T> tA = new FieldAbsoluteDate<>(field, date);
1110         FieldAbsoluteDate<T> tB = new FieldAbsoluteDate<>(date, field.getZero());
1111         Assert.assertEquals(0.0, tA.durationFrom(tB).getReal(), Precision.SAFE_MIN);
1112     }
1113 
1114     public <T extends CalculusFieldElement<T>> void doTestGetComponentsIssue681and676(
1115             final Field<T> field) {
1116 
1117         // setup
1118         FieldAbsoluteDate<T> date = new FieldAbsoluteDate<>(field, 2009, 1, 1, utc);
1119         double zeroUlp = FastMath.nextUp(0.0);
1120         double oneUlp = FastMath.ulp(1.0);
1121         double sixtyUlp = FastMath.ulp(60.0);
1122         double one = FastMath.nextDown(1.0);
1123         double sixty = FastMath.nextDown(60.0);
1124         double sixtyOne = FastMath.nextDown(61.0);
1125 
1126         // actions + verify
1127         // translate back to AbsoluteDate has up to half an ULP of error,
1128         // except when truncated when the error can be up to 1 ULP.
1129         check(date, 2009, 1, 1, 0, 0, 0, 1, 0, 0);
1130         check(date.shiftedBy(zeroUlp), 2009, 1, 1, 0, 0, zeroUlp, 0.5, 0, 0);
1131         check(date.shiftedBy(oneUlp), 2009, 1, 1, 0, 0, oneUlp, 0.5, 0, 0);
1132         check(date.shiftedBy(one), 2009, 1, 1, 0, 0, one, 0.5, 0, 0);
1133         // I could also see rounding to a valid time as being reasonable here
1134         check(date.shiftedBy(59).shiftedBy(one), 2009, 1, 1, 0, 0, sixty, 1, 0, 0);
1135         check(date.shiftedBy(86399).shiftedBy(one), 2009, 1, 1, 23, 59, sixty, 1, 0, 0);
1136         check(date.shiftedBy(-zeroUlp), 2009, 1, 1, 0, 0, 0, 0.5, 0, 0);
1137         check(date.shiftedBy(-oneUlp), 2008, 12, 31, 23, 59, sixtyOne, 1, 0, 0);
1138         check(date.shiftedBy(-1).shiftedBy(zeroUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1139         check(date.shiftedBy(-1).shiftedBy(-zeroUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1140         check(date.shiftedBy(-1).shiftedBy(-oneUlp), 2008, 12, 31, 23, 59, 60.0, 0.5, 0, 0);
1141         check(date.shiftedBy(-1).shiftedBy(-sixtyUlp), 2008, 12, 31, 23, 59, sixty, 0.5, 0, 0);
1142         check(date.shiftedBy(-61).shiftedBy(zeroUlp), 2008, 12, 31, 23, 59, zeroUlp, 0.5, 0, 0);
1143         check(date.shiftedBy(-61).shiftedBy(oneUlp), 2008, 12, 31, 23, 59, oneUlp, 0.5, 0, 0);
1144 
1145         // check UTC weirdness.
1146         // These have more error because of additional multiplications and additions
1147         // up to 2 ULPs or ulp(60.0) of error.
1148         FieldAbsoluteDate<T> d = new FieldAbsoluteDate<>(field, 1966, 1, 1, utc);
1149         double ratePost = 0.0025920 / Constants.JULIAN_DAY;
1150         double factorPost = ratePost / (1 + ratePost);
1151         double ratePre = 0.0012960 / Constants.JULIAN_DAY;
1152         double factorPre = ratePre / (1 + ratePre);
1153         check(d, 1966, 1, 1, 0, 0, 0, 1, 0, 0);
1154         check(d.shiftedBy(zeroUlp), 1966, 1, 1, 0, 0, 0, 0.5, 0, 0);
1155         check(d.shiftedBy(oneUlp), 1966, 1, 1, 0, 0, oneUlp, 0.5, 0, 0);
1156         check(d.shiftedBy(one), 1966, 1, 1, 0, 0, one * (1 - factorPost), 0.5, 2, 0);
1157         check(d.shiftedBy(59).shiftedBy(one), 1966, 1, 1, 0, 0, sixty * (1 - factorPost), 1, 1, 0);
1158         check(d.shiftedBy(86399).shiftedBy(one), 1966, 1, 1, 23, 59, sixty - 86400 * factorPost, 1, 1, 0);
1159         check(d.shiftedBy(-zeroUlp), 1966, 1, 1, 0, 0, 0, 0.5, 0, 0);
1160         // actual leap is small ~1e-16, but during a leap rounding up to 60.0 is ok
1161         check(d.shiftedBy(-oneUlp), 1965, 12, 31, 23, 59, 60.0, 1, 0, 0);
1162         check(d.shiftedBy(-1).shiftedBy(zeroUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1163         check(d.shiftedBy(-1).shiftedBy(-zeroUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1164         check(d.shiftedBy(-1).shiftedBy(-oneUlp), 1965, 12, 31, 23, 59, 59 + factorPre, 0.5, 0, 0);
1165         check(d.shiftedBy(-1).shiftedBy(-sixtyUlp), 1965, 12, 31, 23, 59, 59 + (1 + sixtyUlp) * factorPre, 0.5, 1, 0);
1166         // since second ~= 0 there is significant cancellation
1167         check(d.shiftedBy(-60).shiftedBy(zeroUlp), 1965, 12, 31, 23, 59, 60 * factorPre, 0, 0, sixtyUlp);
1168         check(d.shiftedBy(-60).shiftedBy(oneUlp), 1965, 12, 31, 23, 59, (oneUlp - oneUlp * factorPre) + 60 * factorPre, 0.5, 0, sixtyUlp);
1169 
1170         // check first whole second leap
1171         FieldAbsoluteDate<T> d2 = new FieldAbsoluteDate<>(field, 1972, 7, 1, utc);
1172         check(d2, 1972, 7, 1, 0, 0, 0, 1, 0, 0);
1173         check(d2.shiftedBy(zeroUlp), 1972, 7, 1, 0, 0, zeroUlp, 0.5, 0, 0);
1174         check(d2.shiftedBy(oneUlp), 1972, 7, 1, 0, 0, oneUlp, 0.5, 0, 0);
1175         check(d2.shiftedBy(one), 1972, 7, 1, 0, 0, one, 0.5, 0, 0);
1176         check(d2.shiftedBy(59).shiftedBy(one), 1972, 7, 1, 0, 0, sixty, 1, 0, 0);
1177         check(d2.shiftedBy(86399).shiftedBy(one), 1972, 7, 1, 23, 59, sixty, 1, 0, 0);
1178         check(d2.shiftedBy(-zeroUlp), 1972, 7, 1, 0, 0, 0, 0.5, 0, 0);
1179         check(d2.shiftedBy(-oneUlp), 1972, 6, 30, 23, 59, sixtyOne, 1, 0, 0);
1180         check(d2.shiftedBy(-1).shiftedBy(zeroUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1181         check(d2.shiftedBy(-1).shiftedBy(-zeroUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1182         check(d2.shiftedBy(-1).shiftedBy(-oneUlp), 1972, 6, 30, 23, 59, 60.0, 0.5, 0, 0);
1183         check(d2.shiftedBy(-1).shiftedBy(-sixtyUlp), 1972, 6, 30, 23, 59, sixty, 0.5, 0, 0);
1184         check(d2.shiftedBy(-61).shiftedBy(zeroUlp), 1972, 6, 30, 23, 59, zeroUlp, 0.5, 0, 0);
1185         check(d2.shiftedBy(-61).shiftedBy(oneUlp), 1972, 6, 30, 23, 59, oneUlp, 0.5, 0, 0);
1186 
1187         // check NaN, this is weird that NaNs have valid ymdhm, but not second.
1188         DateTimeComponents actual = date.shiftedBy(Double.NaN).getComponents(utc);
1189         DateComponents dc = actual.getDate();
1190         TimeComponents tc = actual.getTime();
1191         MatcherAssert.assertThat(dc.getYear(), CoreMatchers.is(2009));
1192         MatcherAssert.assertThat(dc.getMonth(), CoreMatchers.is(1));
1193         MatcherAssert.assertThat(dc.getDay(), CoreMatchers.is(1));
1194         MatcherAssert.assertThat(tc.getHour(), CoreMatchers.is(0));
1195         MatcherAssert.assertThat(tc.getMinute(), CoreMatchers.is(0));
1196         MatcherAssert.assertThat("second", tc.getSecond(), CoreMatchers.is(Double.NaN));
1197         MatcherAssert.assertThat(tc.getMinutesFromUTC(), CoreMatchers.is(0));
1198         final double difference = new FieldAbsoluteDate<>(field, actual, utc)
1199                 .durationFrom(date).getReal();
1200         MatcherAssert.assertThat(difference, CoreMatchers.is(Double.NaN));
1201     }
1202 
1203     private <T extends CalculusFieldElement<T>> void check(FieldAbsoluteDate<T> date,
1204                        int year, int month, int day, int hour, int minute, double second,
1205                        double roundTripUlps, final int secondUlps, final double absTol) {
1206         DateTimeComponents actual = date.getComponents(utc);
1207         DateComponents d = actual.getDate();
1208         TimeComponents t = actual.getTime();
1209         MatcherAssert.assertThat(d.getYear(), CoreMatchers.is(year));
1210         MatcherAssert.assertThat(d.getMonth(), CoreMatchers.is(month));
1211         MatcherAssert.assertThat(d.getDay(), CoreMatchers.is(day));
1212         MatcherAssert.assertThat(t.getHour(), CoreMatchers.is(hour));
1213         MatcherAssert.assertThat(t.getMinute(), CoreMatchers.is(minute));
1214         MatcherAssert.assertThat("second", t.getSecond(),
1215                 OrekitMatchers.numberCloseTo(second, absTol, secondUlps));
1216         MatcherAssert.assertThat(t.getMinutesFromUTC(), CoreMatchers.is(0));
1217         final double tol = FastMath.ulp(second) * roundTripUlps;
1218         final double difference = new FieldAbsoluteDate<>(date.getField(), actual, utc)
1219                 .durationFrom(date).getReal();
1220         MatcherAssert.assertThat(difference,
1221                 OrekitMatchers.closeTo(0, FastMath.max(absTol, tol)));
1222     }
1223 
1224     static class AnyFieldTimeStamped<T extends CalculusFieldElement<T>> implements FieldTimeStamped<T> {
1225         AbsoluteDate date;
1226         Field<T> field;
1227 
1228         public AnyFieldTimeStamped(Field<T> field, AbsoluteDate date) {
1229             this.date = date;
1230             this.field = field;
1231         }
1232 
1233         @Override
1234         public FieldAbsoluteDate<T> getDate() {
1235             return new FieldAbsoluteDate<>(field, date);
1236         }
1237     }
1238 
1239     static class TestDates<T extends CalculusFieldElement<T>> {
1240         private final AbsoluteDate presentDate;
1241         private final AnyFieldTimeStamped<T> present;
1242         private final AnyFieldTimeStamped<T> past;
1243         private final AnyFieldTimeStamped<T> presentToo;
1244         private final AnyFieldTimeStamped<T> future;
1245 
1246         public TestDates(Field<T> field) {
1247             presentDate = new AbsoluteDate(new DateComponents(2000, 1, 1),
1248                                                 new TimeComponents(12, 00, 00),
1249                                                 TimeScalesFactory.getUTC());
1250             present    = new AnyFieldTimeStamped<>(field, presentDate);
1251             presentToo = new AnyFieldTimeStamped<>(field, presentDate.shiftedBy(0));
1252             past       = new AnyFieldTimeStamped<>(field, presentDate.shiftedBy(-1000));
1253             future     = new AnyFieldTimeStamped<>(field, presentDate.shiftedBy(1000));
1254         }
1255 
1256         public FieldAbsoluteDate<T> getPresentFieldAbsoluteDate() {
1257             return present.getDate();
1258         }
1259     }
1260 
1261 }