Coverage for tests/test_dateTime.py: 12%
301 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-25 02:29 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-25 02:29 -0800
1#
2# LSST Data Management System
3#
4# Copyright 2008-2017 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <http://www.lsstcorp.org/LegalNotices/>.
22#
24import os
25import pickle
26import time
27import unittest
29import astropy.time
31from lsst.daf.base import DateTime
32import lsst.pex.exceptions as pexExcept
35class DateTimeTestCase(unittest.TestCase):
36 """A test case for DateTime.
37 """
39 def setUp(self):
40 self.timeScales = (DateTime.TAI, DateTime.TT, DateTime.UTC)
41 self.dateSystems = (DateTime.JD, DateTime.MJD, DateTime.EPOCH)
43 def testMJD(self):
44 ts = DateTime(45205.125, DateTime.MJD, DateTime.UTC)
45 self.assertEqual(ts.nsecs(DateTime.UTC), 399006000000000000)
46 self.assertEqual(ts.nsecs(DateTime.TAI), 399006021000000000)
47 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.UTC), 45205.125)
48 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.TAI), 45205.125 + 21.0/86400.0)
49 self.assertTrue(ts.isValid())
51 def testLeapSecond(self):
52 trials = ((45205., 21),
53 (41498.99, 10),
54 (41499.01, 11),
55 (57203.99, 35),
56 (57204.01, 36),
57 (57000., 35),
58 (57210., 36))
59 for mjd, diff in trials:
60 ts = DateTime(mjd, DateTime.MJD, DateTime.UTC)
61 delta = ts.nsecs(DateTime.TAI) - ts.nsecs(DateTime.UTC)
62 self.assertEqual(delta/1E9, diff)
64 def testNsecs(self):
65 ts = DateTime(1192755473000000000, DateTime.UTC)
66 self.assertEqual(ts.nsecs(DateTime.UTC), 1192755473000000000)
67 self.assertEqual(ts.nsecs(DateTime.TAI), 1192755506000000000)
68 self.assertEqual(ts.nsecs(), 1192755506000000000)
69 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.UTC), 54392.040196759262)
70 ts2 = ts
71 self.assertEqual(ts, ts2)
72 ts2 = DateTime(1192755473000000000, DateTime.UTC)
73 self.assertEqual(ts, ts2)
74 ts2 = DateTime(1234567890000000000, DateTime.UTC)
75 self.assertNotEqual(ts, ts2)
77 def testBoundaryMJD(self):
78 ts = DateTime(47892.0, DateTime.MJD, DateTime.UTC)
79 self.assertEqual(ts.nsecs(DateTime.UTC), 631152000000000000)
80 self.assertEqual(ts.nsecs(DateTime.TAI), 631152025000000000)
81 self.assertEqual(ts.get(DateTime.MJD, DateTime.UTC), 47892.0)
83 def testCrossBoundaryNsecs(self):
84 ts = DateTime(631151998000000000, DateTime.UTC)
85 self.assertEqual(ts.nsecs(DateTime.UTC), 631151998000000000)
86 self.assertEqual(ts.nsecs(DateTime.TAI), 631152022000000000)
88 def testNsecsTAI(self):
89 ts = DateTime(1192755506000000000, DateTime.TAI)
90 self.assertEqual(ts.nsecs(DateTime.UTC), 1192755473000000000)
91 self.assertEqual(ts.nsecs(DateTime.TAI), 1192755506000000000)
92 self.assertEqual(ts.nsecs(), 1192755506000000000)
93 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.UTC), 54392.040196759262)
94 self.assertTrue(ts.isValid())
96 def testNsecsDefault(self):
97 ts = DateTime(1192755506000000000)
98 self.assertEqual(ts.nsecs(DateTime.UTC), 1192755473000000000)
99 self.assertEqual(ts.nsecs(DateTime.TAI), 1192755506000000000)
100 self.assertEqual(ts.nsecs(), 1192755506000000000)
101 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.UTC), 54392.040196759262)
102 self.assertTrue(ts.isValid())
104 def testNow(self):
105 successes = 0
106 for _ in range(10):
107 secs = time.time()
108 ts = DateTime.now()
109 diff = ts.nsecs(DateTime.UTC)/1.0e9 - secs
110 if diff > -0.001 and diff < 0.1:
111 successes += 1
112 self.assertGreaterEqual(successes, 3)
114 def testIsoEpoch(self):
115 ts = DateTime("19700101T000000Z", DateTime.UTC)
116 self.assertEqual(ts.nsecs(DateTime.UTC), 0)
117 self.assertEqual(ts.toString(ts.UTC), "1970-01-01T00:00:00.000000000Z")
119 def testIsoUTCBasic(self):
120 """Test basic ISO string input and output of UTC dates
121 """
122 for dateSep in ("-", ""): # "-" date separator is optional
123 for timeSep in (":", ""): # ":" time separator is optional
124 for decPt in (".", ","): # "." or "," may be used as decimal point
125 dateStr = "2009{0}04{0}02T07{1}26{1}39{2}314159265Z".format(dateSep, timeSep, decPt)
126 ts = DateTime(dateStr, DateTime.UTC)
127 self.assertEqual(ts.nsecs(DateTime.TT), 1238657265498159265)
128 self.assertEqual(ts.nsecs(DateTime.TAI), 1238657233314159265)
129 self.assertEqual(ts.nsecs(DateTime.UTC), 1238657199314159265)
130 self.assertEqual(ts.toString(ts.UTC), "2009-04-02T07:26:39.314159265Z")
132 def testIsoNonUTCBasics(self):
133 """Test basic ISO string input and output of TAI and TT dates
134 """
135 for scale in (DateTime.TAI, DateTime.TT):
136 for dateSep in ("-", ""): # "-" date separator is optional
137 for timeSep in (":", ""): # ":" time separator is optional
138 for decPt in (".", ","): # "." or "," may be used as decimal point
139 dateStr = "2009{0}04{0}02T07{1}26{1}39{2}314159265".format(dateSep, timeSep, decPt)
140 ts = DateTime(dateStr, scale)
141 self.assertEqual(ts.toString(scale), "2009-04-02T07:26:39.314159265")
142 self.assertTrue(ts.isValid())
144 def testIsoExpanded(self):
145 ts = DateTime("2009-04-02T07:26:39.314159265Z", DateTime.UTC)
146 self.assertEqual(ts.nsecs(DateTime.TAI), 1238657233314159265)
147 self.assertEqual(ts.nsecs(DateTime.UTC), 1238657199314159265)
148 self.assertEqual(ts.toString(ts.UTC), "2009-04-02T07:26:39.314159265Z")
149 self.assertTrue(ts.isValid())
151 def testIsoNoNSecs(self):
152 ts = DateTime("2009-04-02T07:26:39Z", DateTime.UTC)
153 self.assertEqual(ts.nsecs(DateTime.TAI), 1238657233000000000)
154 self.assertEqual(ts.nsecs(DateTime.UTC), 1238657199000000000)
155 self.assertEqual(ts.toString(ts.UTC), "2009-04-02T07:26:39.000000000Z")
156 self.assertTrue(ts.isValid())
158 def testSOFA(self):
159 """The SOFA documentation includes an example conversion:
160 https://www.iausofa.org/2017_0420_C/sofa/sofa_ts_c.pdf
161 (page 8, section 2.4)
163 The value in those docs is only ~single precision, so I re-computed
164 it with pyerfa to get a more correct value.
165 """
166 with self.subTest("jd to jyear"):
167 self.assertEqual(DateTime(2457073.05631, DateTime.JD, DateTime.TAI).get(DateTime.EPOCH),
168 2015.1349933196439)
169 with self.subTest("jyear to jd"):
170 self.assertEqual(DateTime(2015.1349933196, DateTime.EPOCH, DateTime.TAI).get(DateTime.JD),
171 2457073.056309984)
173 def testAstropyComparison(self):
174 """Astropy's Time module is based on ERFA, providing a well verified
175 comparison point.
176 """
177 def check_times(dateTime, time):
178 with self.subTest("jyear"):
179 self.assertAlmostEqual(dateTime.get(DateTime.EPOCH), time.tai.jyear)
180 with self.subTest("mjd"):
181 self.assertEqual(dateTime.get(DateTime.MJD), time.tai.mjd)
182 with self.subTest("jd"):
183 self.assertEqual(dateTime.get(DateTime.JD), time.tai.jd)
185 # Unix epoch comparison
186 dateTime = DateTime("19700101T000000Z", DateTime.UTC)
187 time = astropy.time.Time("1970-01-01T00:00:00", format="isot", scale="utc")
188 check_times(dateTime, time)
190 # J2000 epoch comparison
191 dateTime = DateTime(2000.0, DateTime.EPOCH, DateTime.UTC)
192 time = astropy.time.Time(2000.0, format="jyear", scale="utc")
193 check_times(dateTime, time)
195 # random future MJD epoch comparison
196 dateTime = DateTime(65432.1, DateTime.MJD, DateTime.TAI)
197 time = astropy.time.Time(65432.1, format="mjd", scale="tai")
198 check_times(dateTime, time)
200 def testIsoThrow(self):
201 with self.assertRaises(pexExcept.DomainError):
202 DateTime("2009-04-01T23:36:05", DateTime.UTC) # Z time zone required for UTC
203 for scale in (DateTime.TAI, DateTime.TT):
204 with self.assertRaises(pexExcept.DomainError):
205 DateTime("2009-04-01T23:36:05Z", scale) # Z time zone forbidden for TAI or TT
207 for scale in self.timeScales:
208 with self.assertRaises(pexExcept.DomainError):
209 DateTime("20090401", scale) # time required
210 with self.assertRaises(pexExcept.DomainError):
211 DateTime("20090401T", DateTime.UTC) # time required
212 with self.assertRaises(pexExcept.DomainError):
213 DateTime("2009-04-01T", DateTime.UTC) # time required
214 with self.assertRaises(pexExcept.DomainError):
215 DateTime("2009-04-01T23:36:05-0700", DateTime.UTC) # time zone offset not supported
216 with self.assertRaises(pexExcept.DomainError):
217 DateTime("2009/04/01T23:36:05Z", DateTime.UTC) # "/" not valid
218 with self.assertRaises(pexExcept.DomainError):
219 DateTime("2009-04-01T23:36", DateTime.UTC) # partial time
220 with self.assertRaises(pexExcept.DomainError):
221 DateTime("2009-04", DateTime.UTC) # partial date without time
222 with self.assertRaises(pexExcept.DomainError):
223 DateTime("2009-04T23:36.05", DateTime.UTC) # partial date with time
224 with self.assertRaises(pexExcept.DomainError):
225 DateTime("09-04-01T23:36:05", DateTime.UTC) # 2 digit year
227 # earliest allowed UTC date is the earliest date in the leap second
228 # table
229 try:
230 minLeapSecUTC = "1961-01-01T00:00:00Z"
231 dt = DateTime(minLeapSecUTC, DateTime.UTC)
232 dt.toString(DateTime.UTC)
233 except Exception:
234 self.fail("minLeapSecUTC={} failed, but should be OK".format(minLeapSecUTC))
235 with self.assertRaises(pexExcept.DomainError):
236 DateTime("1960-01-01T23:59:59Z", DateTime.UTC) # just before leap second table starts
238 # earliest allowed date for TAI and TT is year = 1902
239 for timeSys in (DateTime.TAI, DateTime.TT):
240 try:
241 earliestDate = "1902-01-01T00:00:00"
242 dt = DateTime(earliestDate, timeSys)
243 dt.toString(DateTime.TAI)
244 dt.toString(DateTime.TT)
245 except Exception:
246 self.fail("{} system={} failed, but should be OK".format(earliestDate, timeSys))
248 # dates before the leap second table can be created using TAI or TT,
249 # but not viewed in UTC
250 earlyDt = DateTime("1960-01-01T00:00:00", DateTime.TAI)
251 with self.assertRaises(pexExcept.DomainError):
252 earlyDt.toString(DateTime.UTC)
254 with self.assertRaises(pexExcept.DomainError):
255 DateTime("1901-12-12T23:59:59Z", DateTime.TAI) # too early
256 with self.assertRaises(pexExcept.DomainError):
257 DateTime("1700-01-01T00:00:00Z", DateTime.TAI) # way too early
258 with self.assertRaises(pexExcept.DomainError):
259 DateTime("2262-01-01T00:00:00Z", DateTime.TAI) # too late
260 with self.assertRaises(pexExcept.DomainError):
261 DateTime("3200-01-01T00:00:00Z", DateTime.TAI) # way too late
263 def testWraparound(self):
264 """Test that a date later than 2038-01-19, 03:14:07 does not wrap
265 around.
267 This will fail on old versions of unix, and indicates that DateTime
268 is not safe.
269 """
270 dateStr = "2040-01-01T00:00:00.000000000"
271 self.assertEqual(str(DateTime(dateStr, DateTime.TAI)), "DateTime(\"{}\", TAI)".format(dateStr))
273 def testDM7622(self):
274 """Test DM-7622: date with unix time = -1 seconds must be usable
276 Note that the call in question parses the ISO string without paying
277 attention to the scale (it applies the scale later),
278 so the same ISO string is wanted in all cases
279 (except with a trailing Z for UTC, and without for TAI and TT)
280 """
281 negOneSecIso = "1969-12-31T23:59:59.000000000"
282 for scale in self.timeScales:
283 dateStr = negOneSecIso + ("Z" if scale == DateTime.UTC else "")
284 try:
285 dt = DateTime(dateStr, scale)
286 except Exception:
287 self.fail("Date {} in scale {} unusable".format(dateStr, scale))
288 self.assertEqual(dt.nsecs(scale), int(-1e9))
290 def testStr(self):
291 timeStr1 = "2004-03-01T12:39:45.1"
292 fullTimeStr1 = "2004-03-01T12:39:45.100000000"
293 dt1 = DateTime(timeStr1, DateTime.TAI)
294 self.assertEqual(str(dt1), "DateTime(\"{}\", TAI)".format(fullTimeStr1))
295 self.assertEqual(repr(dt1), "DateTime(\"{}\", TAI)".format(fullTimeStr1))
297 timeStr2 = "2004-03-01T12:39:45.000000001"
298 dt2 = DateTime(timeStr2, DateTime.TAI)
299 self.assertEqual(str(dt2), "DateTime(\"{}\", TAI)".format(timeStr2))
300 self.assertEqual(repr(dt2), "DateTime(\"{}\", TAI)".format(timeStr2))
302 def testNsecsTT(self):
303 ts = DateTime(1192755538184000000, DateTime.TT)
304 self.assertEqual(ts.nsecs(DateTime.UTC), 1192755473000000000)
305 self.assertEqual(ts.nsecs(DateTime.TAI), 1192755506000000000)
306 self.assertEqual(ts.nsecs(), 1192755506000000000)
307 self.assertAlmostEqual(ts.get(DateTime.MJD, DateTime.UTC), 54392.040196759262)
308 self.assertTrue(ts.isValid())
310 def testFracSecs(self):
311 ts = DateTime("2004-03-01T12:39:45.1Z", DateTime.UTC)
312 self.assertEqual(ts.toString(ts.UTC), '2004-03-01T12:39:45.100000000Z')
313 ts = DateTime("2004-03-01T12:39:45.01Z", DateTime.UTC)
314 self.assertEqual(ts.toString(ts.UTC), '2004-03-01T12:39:45.010000000Z')
315 ts = DateTime("2004-03-01T12:39:45.000000001Z", DateTime.UTC) # nanosecond
316 self.assertEqual(ts.toString(ts.UTC), '2004-03-01T12:39:45.000000001Z')
317 ts = DateTime("2004-03-01T12:39:45.0000000001Z", DateTime.UTC) # too small
318 self.assertEqual(ts.toString(ts.UTC), '2004-03-01T12:39:45.000000000Z')
320 def testInvalid(self):
321 ts = DateTime()
322 self.assertFalse(ts.isValid())
323 for scale in self.timeScales:
324 self.assertEqual(ts.nsecs(scale), DateTime.invalid_nsecs)
325 for system in self.dateSystems:
326 with self.assertRaises(RuntimeError):
327 ts.get(system, scale)
328 with self.assertRaises(RuntimeError):
329 ts.gmtime(scale)
330 with self.assertRaises(RuntimeError):
331 ts.timespec(scale)
332 with self.assertRaises(RuntimeError):
333 ts.timeval(scale)
334 with self.assertRaises(RuntimeError):
335 ts.toString(scale)
336 with self.assertRaises(RuntimeError):
337 ts.toPython()
338 self.assertEqual(repr(ts), "DateTime()")
340 def testNegative(self):
341 ts = DateTime("1969-03-01T00:00:32Z", DateTime.UTC)
342 self.assertEqual(ts.toString(ts.UTC), '1969-03-01T00:00:32.000000000Z')
343 ts = DateTime("1969-01-01T00:00:00Z", DateTime.UTC)
344 self.assertEqual(ts.toString(ts.UTC), '1969-01-01T00:00:00.000000000Z')
345 ts = DateTime("1969-01-01T00:00:40Z", DateTime.UTC)
346 self.assertEqual(ts.toString(ts.UTC), '1969-01-01T00:00:40.000000000Z')
347 ts = DateTime("1969-01-01T00:00:38Z", DateTime.UTC)
348 self.assertEqual(ts.toString(ts.UTC), '1969-01-01T00:00:38.000000000Z')
349 ts = DateTime("1969-03-01T12:39:45Z", DateTime.UTC)
350 self.assertEqual(ts.toString(ts.UTC), '1969-03-01T12:39:45.000000000Z')
351 ts = DateTime("1969-03-01T12:39:45.000000001Z", DateTime.UTC)
352 self.assertEqual(ts.toString(ts.UTC), '1969-03-01T12:39:45.000000001Z')
353 self.assertTrue(ts.isValid())
355 # Note slight inaccuracy in UTC-TAI-UTC round-trip
356 ts = DateTime("1969-03-01T12:39:45.12345Z", DateTime.UTC)
357 self.assertEqual(ts.toString(ts.UTC), '1969-03-01T12:39:45.123449996Z')
358 ts = DateTime("1969-03-01T12:39:45.123456Z", DateTime.UTC)
359 self.assertEqual(ts.toString(ts.UTC), '1969-03-01T12:39:45.123455996Z')
361 ts = DateTime(-1, DateTime.TAI)
362 self.assertEqual(ts.toString(ts.UTC), '1969-12-31T23:59:51.999918239Z')
363 ts = DateTime(0, DateTime.TAI)
364 self.assertEqual(ts.toString(ts.UTC), '1969-12-31T23:59:51.999918240Z')
365 ts = DateTime(1, DateTime.TAI)
366 self.assertEqual(ts.toString(ts.UTC), '1969-12-31T23:59:51.999918241Z')
368 ts = DateTime(-1, DateTime.UTC)
369 self.assertEqual(ts.toString(ts.UTC), '1969-12-31T23:59:59.999999999Z')
370 ts = DateTime(0, DateTime.UTC)
371 self.assertEqual(ts.toString(ts.UTC), '1970-01-01T00:00:00.000000000Z')
372 ts = DateTime(1, DateTime.UTC)
373 self.assertEqual(ts.toString(ts.UTC), '1970-01-01T00:00:00.000000001Z')
375 def testConvert(self):
376 year = 2012
377 month = 7
378 day = 19
379 hour = 18
380 minute = 29
381 second = 33
383 ts = DateTime(year, month, day, hour, minute, second, DateTime.UTC)
384 dt = ts.toPython(DateTime.UTC)
386 self.assertEqual(dt.year, year)
387 self.assertEqual(dt.month, month)
388 self.assertEqual(dt.day, day)
389 self.assertEqual(dt.hour, hour)
390 self.assertEqual(dt.minute, minute)
391 self.assertEqual(dt.second, second)
393 def testPickle(self):
394 ts = DateTime(int(1192755473000000000), DateTime.UTC)
395 nts = pickle.loads(pickle.dumps(ts))
396 self.assertEqual(nts.nsecs(DateTime.UTC), int(1192755473000000000))
399class TimeZoneBaseTestCase(DateTimeTestCase):
400 timezone = ""
402 def setUp(self):
403 DateTimeTestCase.setUp(self)
404 self.tz = os.environ.setdefault('TZ', "")
405 os.environ['TZ'] = self.timezone
407 def tearDown(self):
408 if self.tz == "":
409 del os.environ['TZ']
410 else:
411 os.environ['TZ'] = self.tz
414class BritishTimeTestCase(TimeZoneBaseTestCase):
415 timezone = "Europe/London"
418class BritishTime2TestCase(TimeZoneBaseTestCase):
419 timezone = "GMT0BST"
422class PacificTimeTestCase(TimeZoneBaseTestCase):
423 timezone = "PST8PDT"
426if __name__ == '__main__': 426 ↛ 427line 426 didn't jump to line 427, because the condition on line 426 was never true
427 unittest.main()