1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.time;
18
19 import org.hamcrest.MatcherAssert;
20 import org.hipparchus.random.RandomGenerator;
21 import org.hipparchus.random.Well1024a;
22 import org.hipparchus.util.Binary64Field;
23 import org.junit.jupiter.api.AfterEach;
24 import org.junit.jupiter.api.Assertions;
25 import org.junit.jupiter.api.BeforeEach;
26 import org.junit.jupiter.api.Test;
27 import org.orekit.OrekitMatchers;
28 import org.orekit.Utils;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitMessages;
31 import org.orekit.utils.Constants;
32
33 import java.lang.reflect.Field;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.concurrent.ExecutorService;
38 import java.util.concurrent.Executors;
39 import java.util.concurrent.TimeUnit;
40
41 public class UTCScaleTest {
42
43 @Test
44 public void testAfter() {
45 AbsoluteDate d1 = new AbsoluteDate(new DateComponents(2020, 12, 31),
46 new TimeComponents(23, 59, 59),
47 utc);
48 Assertions.assertEquals("2020-12-31T23:59:59.000Z", d1.toString());
49 }
50
51 @Test
52 public void testNoLeap() {
53 Assertions.assertEquals("UTC", utc.toString());
54 AbsoluteDate d1 = new AbsoluteDate(new DateComponents(1999, 12, 31),
55 new TimeComponents(23, 59, 59),
56 utc);
57 AbsoluteDate d2 = new AbsoluteDate(new DateComponents(2000, 1, 1),
58 new TimeComponents(0, 0, 1),
59 utc);
60 Assertions.assertEquals(2.0, d2.durationFrom(d1), 1.0e-10);
61 }
62
63 @Test
64 public void testLeap2006() {
65 AbsoluteDate leapDate =
66 new AbsoluteDate(new DateComponents(2006, 1, 1), TimeComponents.H00, utc);
67 AbsoluteDate d1 = leapDate.shiftedBy(-1);
68 AbsoluteDate d2 = leapDate.shiftedBy(+1);
69 Assertions.assertEquals(2.0, d2.durationFrom(d1), 1.0e-10);
70
71 AbsoluteDate d3 = new AbsoluteDate(new DateComponents(2005, 12, 31),
72 new TimeComponents(23, 59, 59),
73 utc);
74 AbsoluteDate d4 = new AbsoluteDate(new DateComponents(2006, 1, 1),
75 new TimeComponents(0, 0, 1),
76 utc);
77 Assertions.assertEquals(3.0, d4.durationFrom(d3), 1.0e-10);
78 }
79
80 @Test
81 public void testDuringLeap() {
82 AbsoluteDate d = new AbsoluteDate(new DateComponents(1983, 6, 30),
83 new TimeComponents(23, 59, 59),
84 utc);
85 Assertions.assertEquals("1983-06-30T23:58:59.000", d.shiftedBy(-60).toString(utc));
86 Assertions.assertEquals(60, utc.minuteDuration(d.shiftedBy(-60)));
87 Assertions.assertFalse(utc.insideLeap(d.shiftedBy(-60)));
88 Assertions.assertEquals("1983-06-30T23:59:59.000", d.toString(utc));
89 Assertions.assertEquals(61, utc.minuteDuration(d));
90 Assertions.assertFalse(utc.insideLeap(d));
91 d = d.shiftedBy(0.251);
92 Assertions.assertEquals("1983-06-30T23:59:59.251", d.toString(utc));
93 Assertions.assertEquals(61, utc.minuteDuration(d));
94 Assertions.assertFalse(utc.insideLeap(d));
95 d = d.shiftedBy(0.251);
96 Assertions.assertEquals("1983-06-30T23:59:59.502", d.toString(utc));
97 Assertions.assertEquals(61, utc.minuteDuration(d));
98 Assertions.assertFalse(utc.insideLeap(d));
99 d = d.shiftedBy(0.251);
100 Assertions.assertEquals("1983-06-30T23:59:59.753", d.toString(utc));
101 Assertions.assertEquals(61, utc.minuteDuration(d));
102 Assertions.assertFalse(utc.insideLeap(d));
103 d = d.shiftedBy( 0.251);
104 Assertions.assertEquals("1983-06-30T23:59:60.004", d.toString(utc));
105 Assertions.assertEquals(61, utc.minuteDuration(d));
106 Assertions.assertTrue(utc.insideLeap(d));
107 d = d.shiftedBy(0.251);
108 Assertions.assertEquals("1983-06-30T23:59:60.255", d.toString(utc));
109 Assertions.assertEquals(61, utc.minuteDuration(d));
110 Assertions.assertTrue(utc.insideLeap(d));
111 d = d.shiftedBy(0.251);
112 Assertions.assertEquals("1983-06-30T23:59:60.506", d.toString(utc));
113 Assertions.assertEquals(61, utc.minuteDuration(d));
114 d = d.shiftedBy(0.251);
115 Assertions.assertEquals("1983-06-30T23:59:60.757", d.toString(utc));
116 Assertions.assertEquals(61, utc.minuteDuration(d));
117 Assertions.assertTrue(utc.insideLeap(d));
118 d = d.shiftedBy(0.251);
119 Assertions.assertEquals("1983-07-01T00:00:00.008", d.toString(utc));
120 Assertions.assertEquals(60, utc.minuteDuration(d));
121 Assertions.assertFalse(utc.insideLeap(d));
122 }
123
124 @Test
125 public void testWrapBeforeLeap() {
126 AbsoluteDate t = new AbsoluteDate("2015-06-30T23:59:59.999999", utc);
127 Assertions.assertEquals("2015-06-30T23:59:60.000+00:00",
128 t.getComponents(utc).toString(utc.minuteDuration(t)));
129 }
130
131 @Test
132 public void testMinuteDuration() {
133 final AbsoluteDate t0 = new AbsoluteDate("1983-06-30T23:58:59.000", utc);
134 for (double dt = 0; dt < 63; dt += 0.3) {
135 if (dt < 1.0) {
136
137 Assertions.assertEquals(60, utc.minuteDuration(t0.shiftedBy(dt)));
138 } else if (dt < 62.0) {
139
140 Assertions.assertEquals(61, utc.minuteDuration(t0.shiftedBy(dt)));
141 } else {
142
143 Assertions.assertEquals(60, utc.minuteDuration(t0.shiftedBy(dt)));
144 }
145 }
146 }
147
148
149
150
151
152
153
154 @Test
155 public void testMinuteDurationConsistentWithLeap() throws ReflectiveOperationException {
156
157
158 Field field = UTCScale.class.getDeclaredField("offsets");
159 field.setAccessible(true);
160 UTCTAIOffset[] offsets = (UTCTAIOffset[]) field.get(utc);
161
162
163 for (UTCTAIOffset offset : offsets) {
164
165 final AbsoluteDate start = offset.getDate();
166 final AbsoluteDate end = offset.getValidityStart();
167 AbsoluteDate d = start.shiftedBy(end.durationFrom(start) / 2.0);
168 int excess = utc.minuteDuration(d) - 60;
169 TimeOffset leap = offset.getLeap();
170
171 Assertions.assertTrue(leap.toDouble() <= excess, "at MJD" + offset.getMJD() + ": " + leap + " <= " + excess);
172 Assertions.assertTrue(leap.toDouble() > (excess - 1));
173
174 d = start.shiftedBy(-30);
175 int newExcess = utc.minuteDuration(d) - 60;
176 TimeOffset newLeap = offset.getLeap();
177
178 Assertions.assertTrue(newLeap.toDouble() <= newExcess, "at MJD" + offset.getMJD() + ": " + newLeap + " <= " + newExcess);
179 Assertions.assertTrue(leap.toDouble() > (excess - 1));
180 Assertions.assertEquals(excess, newExcess);
181 Assertions.assertEquals(leap, newLeap);
182 MatcherAssert.assertThat("" + offset.getValidityStart(), leap.toDouble(),
183 OrekitMatchers.numberCloseTo(end.durationFrom(start), 1e-16, 1));
184 }
185 }
186
187 @Test
188 public void testSymmetry() {
189 TimeScale scale = TimeScalesFactory.getGPS();
190 for (double dt = -10000; dt < 10000; dt += 123.456789) {
191 AbsoluteDate date = AbsoluteDate.J2000_EPOCH.shiftedBy(dt * Constants.JULIAN_DAY);
192 double dt1 = scale.offsetFromTAI(date).toDouble();
193 DateTimeComponents components = date.getComponents(scale);
194 double dt2 = scale.offsetToTAI(components.getDate(), components.getTime()).toDouble();
195 Assertions.assertEquals( 0.0, dt1 + dt2, 1.0e-10);
196 }
197 }
198
199 @Test
200 public void testOffsets() {
201
202
203 checkOffset(1950, 1, 1, 0);
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 checkOffset(1961, 1, 2, -(1.422818 + 1 * 0.001296));
220 checkOffset(1961, 8, 2, -(1.372818 + 213 * 0.001296));
221 checkOffset(1962, 1, 2, -(1.845858 + 1 * 0.0011232));
222 checkOffset(1963, 11, 2, -(1.945858 + 670 * 0.0011232));
223 checkOffset(1964, 1, 2, -(3.240130 - 365 * 0.001296));
224 checkOffset(1964, 4, 2, -(3.340130 - 274 * 0.001296));
225 checkOffset(1964, 9, 2, -(3.440130 - 121 * 0.001296));
226 checkOffset(1965, 1, 2, -(3.540130 + 1 * 0.001296));
227 checkOffset(1965, 3, 2, -(3.640130 + 60 * 0.001296));
228 checkOffset(1965, 7, 2, -(3.740130 + 182 * 0.001296));
229 checkOffset(1965, 9, 2, -(3.840130 + 244 * 0.001296));
230 checkOffset(1966, 1, 2, -(4.313170 + 1 * 0.002592));
231 checkOffset(1968, 2, 2, -(4.213170 + 762 * 0.002592));
232
233
234 checkOffset(1972, 3, 5, -10);
235 checkOffset(1972, 7, 14, -11);
236 checkOffset(1979, 12, 31, -18);
237 checkOffset(1980, 1, 22, -19);
238 checkOffset(2006, 7, 7, -33);
239
240 }
241
242 private void checkOffset(int year, int month, int day, double offset) {
243 AbsoluteDate date = new AbsoluteDate(year, month, day, utc);
244 Assertions.assertEquals(offset, utc.offsetFromTAI(date).toDouble(), 1.0e-10);
245 }
246
247 @Test
248 public void testCreatingInLeapDateUTC() {
249 AbsoluteDate previous = null;
250 final double step = 0.0625;
251 for (double seconds = 59.0; seconds < 61.0; seconds += step) {
252 final AbsoluteDate date = new AbsoluteDate(2008, 12, 31, 23, 59, seconds, utc);
253 if (previous != null) {
254 Assertions.assertEquals(step, date.durationFrom(previous), 1.0e-12);
255 }
256 previous = date;
257 }
258 AbsoluteDate ad0 = new AbsoluteDate("2008-12-31T23:59:60", utc);
259 Assertions.assertTrue(ad0.toString(utc).startsWith("2008-12-31T23:59:"));
260 AbsoluteDate ad1 = new AbsoluteDate("2008-12-31T23:59:59", utc).shiftedBy(1);
261 Assertions.assertEquals(0, ad1.durationFrom(ad0), 1.0e-15);
262 Assertions.assertEquals(1, new AbsoluteDate("2009-01-01T00:00:00", utc).durationFrom(ad0), 1.0e-15);
263 Assertions.assertEquals(2, new AbsoluteDate("2009-01-01T00:00:01", utc).durationFrom(ad0), 1.0e-15);
264 }
265
266 @Test
267 public void testCreatingInLeapDateLocalTime50HoursWest() {
268
269 AbsoluteDate previous = null;
270 final double step = 0.0625;
271 for (double seconds = 59.0; seconds < 61.0; seconds += step) {
272 final AbsoluteDate date = new AbsoluteDate(new DateComponents(2008, 12, 29),
273 new TimeComponents(21, 59, seconds, -50 * 60),
274 utc);
275 if (previous != null) {
276 Assertions.assertEquals(step, date.durationFrom(previous), 1.0e-12);
277 }
278 previous = date;
279 }
280 AbsoluteDate ad0 = new AbsoluteDate("2008-12-29T21:59:60-50:00", utc);
281 Assertions.assertTrue(ad0.toString(utc).startsWith("2008-12-31T23:59:"));
282 AbsoluteDate ad1 = new AbsoluteDate("2008-12-29T21:59:59-50:00", utc).shiftedBy(1);
283 Assertions.assertEquals(0, ad1.durationFrom(ad0), 1.0e-15);
284 Assertions.assertEquals(1, new AbsoluteDate("2008-12-29T22:00:00-50:00", utc).durationFrom(ad0), 1.0e-15);
285 Assertions.assertEquals(2, new AbsoluteDate("2008-12-29T22:00:01-50:00", utc).durationFrom(ad0), 1.0e-15);
286 }
287
288 @Test
289 public void testCreatingInLeapDateLocalTime50HoursEast() {
290
291 AbsoluteDate previous = null;
292 final double step = 0.0625;
293 for (double seconds = 59.0; seconds < 61.0; seconds += step) {
294 final AbsoluteDate date = new AbsoluteDate(new DateComponents(2009, 1, 3),
295 new TimeComponents(1, 59, seconds, +50 * 60),
296 utc);
297 if (previous != null) {
298 Assertions.assertEquals(step, date.durationFrom(previous), 1.0e-12);
299 }
300 previous = date;
301 }
302 AbsoluteDate ad0 = new AbsoluteDate("2009-01-03T01:59:60+50:00", utc);
303 Assertions.assertTrue(ad0.toString(utc).startsWith("2008-12-31T23:59:"));
304 AbsoluteDate ad1 = new AbsoluteDate("2009-01-03T01:59:59+50:00", utc).shiftedBy(1);
305 Assertions.assertEquals(0, ad1.durationFrom(ad0), 1.0e-15);
306 Assertions.assertEquals(1, new AbsoluteDate("2009-01-03T02:00:00+50:00", utc).durationFrom(ad0), 1.0e-15);
307 Assertions.assertEquals(2, new AbsoluteDate("2009-01-03T02:00:01+50:00", utc).durationFrom(ad0), 1.0e-15);
308 }
309
310 @Test
311 public void testDisplayDuringLeap() {
312 AbsoluteDate t0 = utc.getLastKnownLeapSecond().shiftedBy(-1.0);
313 for (double dt = 0.0; dt < 3.0; dt += 0.375) {
314 AbsoluteDate t = t0.shiftedBy(dt);
315 double seconds = t.getComponents(utc).getTime().getSecond();
316 if (dt < 2.0) {
317 Assertions.assertEquals(dt + 59.0, seconds, 1.0e-12);
318 } else {
319 Assertions.assertEquals(dt - 2.0, seconds, 1.0e-12);
320 }
321 }
322 }
323
324 @Test
325 public void testMultithreading() {
326
327
328 RandomGenerator random = new Well1024a(6392073424L);
329 List<AbsoluteDate> datesList = new ArrayList<>();
330 List<Double> offsetsList = new ArrayList<>();
331 AbsoluteDate reference = utc.getFirstKnownLeapSecond().shiftedBy(-Constants.JULIAN_YEAR);
332 double testRange = utc.getLastKnownLeapSecond().durationFrom(reference) + Constants.JULIAN_YEAR;
333 for (int i = 0; i < 10000; ++i) {
334 AbsoluteDate randomDate = reference.shiftedBy(random.nextDouble() * testRange);
335 datesList.add(randomDate);
336 offsetsList.add(utc.offsetFromTAI(randomDate).toDouble());
337 }
338
339
340 ExecutorService executorService = Executors.newFixedThreadPool(100);
341
342 for (int i = 0; i < datesList.size(); ++i) {
343 final AbsoluteDate date = datesList.get(i);
344 final double offset = offsetsList.get(i);
345 executorService.execute(() -> Assertions.assertEquals(offset, utc.offsetFromTAI(date).toDouble(), 1.0e-12));
346 }
347
348 try {
349 executorService.shutdown();
350 Assertions.assertTrue(executorService.awaitTermination(3, TimeUnit.SECONDS));
351 } catch (InterruptedException ie) {
352 Assertions.fail(ie.getLocalizedMessage());
353 }
354
355 }
356
357 @Test
358 public void testIssue89() {
359 AbsoluteDate firstDayLastLeap = utc.getLastKnownLeapSecond().shiftedBy(10.0);
360 AbsoluteDate rebuilt = new AbsoluteDate(firstDayLastLeap.toString(utc), utc);
361 Assertions.assertEquals(0.0, rebuilt.durationFrom(firstDayLastLeap), 1.0e-12);
362 }
363
364 @Test
365 public void testOffsetToTAIBeforeFirstLeapSecond() {
366 TimeScale scale = TimeScalesFactory.getUTC();
367
368 DateComponents dateComponents = new DateComponents(1950, 1, 1);
369 double actual = scale.offsetToTAI(dateComponents, TimeComponents.H00).toDouble();
370 Assertions.assertEquals(0.0, actual, 1.0e-10);
371 }
372
373 @Test
374 public void testEmptyOffsets() {
375 Utils.setDataRoot("no-data");
376
377 TimeScalesFactory.addUTCTAIOffsetsLoader(Collections::emptyList);
378
379 try {
380 TimeScalesFactory.getUTC();
381 Assertions.fail("an exception should have been thrown");
382 } catch (OrekitException oe) {
383 Assertions.assertEquals(OrekitMessages.NO_IERS_UTC_TAI_HISTORY_DATA_LOADED, oe.getSpecifier());
384 }
385
386 }
387
388 @Test
389 public void testInfinityRegularDate() {
390 TimeScale scale = TimeScalesFactory.getUTC();
391 Assertions.assertEquals(-37.0,
392 scale.offsetFromTAI(AbsoluteDate.FUTURE_INFINITY).toDouble(),
393 1.0e-15);
394 Assertions.assertEquals(0.0,
395 scale.offsetFromTAI(AbsoluteDate.PAST_INFINITY).toDouble(),
396 1.0e-15);
397 }
398
399 @Test
400 public void testInfinityFieldDate() {
401 TimeScale scale = TimeScalesFactory.getUTC();
402 Assertions.assertEquals(-37.0,
403 scale.offsetFromTAI(FieldAbsoluteDate.getFutureInfinity(Binary64Field.getInstance())).getReal(),
404 1.0e-15);
405 Assertions.assertEquals(0.0,
406 scale.offsetFromTAI(FieldAbsoluteDate.getPastInfinity(Binary64Field.getInstance())).getReal(),
407 1.0e-15);
408 }
409
410 @Test
411 public void testFirstAndLast() {
412
413 AbsoluteDate first = utc.getFirstKnownLeapSecond();
414 AbsoluteDate last = utc.getLastKnownLeapSecond();
415
416
417
418 Assertions.assertEquals(new AbsoluteDate(2016, 12, 31, 23, 59, 60, utc), last);
419 Assertions.assertEquals(new AbsoluteDate(1960, 12, 31, 23, 59, 60, utc), first);
420 }
421
422 @Test
423 public void testGetUTCTAIOffsets() {
424 final List<UTCTAIOffset> offsets = utc.getUTCTAIOffsets();
425 Assertions.assertEquals(41, offsets.size());
426 final UTCTAIOffset firstOffset = offsets.get(0);
427 final UTCTAIOffset lastOffset = offsets.get(offsets.size() - 1);
428 Assertions.assertEquals(37300, firstOffset.getMJD());
429 Assertions.assertEquals(57754, lastOffset.getMJD());
430 }
431
432 @BeforeEach
433 public void setUp() {
434 Utils.setDataRoot("regular-data");
435 utc = TimeScalesFactory.getUTC();
436 }
437
438 @AfterEach
439 public void tearDown() {
440 utc = null;
441 }
442
443 private UTCScale utc;
444
445 }