1import datetime as dt_module 2import numbers 3import numpy as np 4import sys 5from assay import assert_raises 6from pytz import timezone 7from numpy import array, inf 8from skyfield import api 9from skyfield.constants import DAY_S, T0 10from skyfield.data import iers 11from skyfield.timelib import ( 12 GREGORIAN_START, GREGORIAN_START_ENGLAND, Time, Timescale, 13 calendar_tuple, compute_calendar_date, julian_date, julian_day, utc, 14) 15from datetime import datetime 16 17one_second = 1.0 / DAY_S 18epsilon = one_second * 42.0e-6 # 20.1e-6 is theoretical best precision 19 20continuous_timescale = ['tai', 'tt', 'tdb', 'ut1'] 21time_scale_name = ['utc', 'tai', 'tt', 'tdb', 'ut1'] 22time_value = [(1973, 1, 18, 1, 35, 37.5), 2441700.56640625] 23 24def test_timescale_with_old_fashioned_leap_second_table(): 25 # Skyfield no longer uses the awkward old-style leap second table, 26 # written before I knew better, with inf's on both ends; but in case 27 # any users built such tables and supplied them manually: 28 delta_t = array([[2442046.5005113888], [44.4782581]]) 29 leap_dates = array([-inf, 2441317.5, 2441499.5, 2441683.5, 2442048.5, inf]) 30 leap_offsets = array([10, 10, 10, 11, 12, 13]) 31 ts = Timescale(delta_t, leap_dates, leap_offsets) 32 t = ts.tai(1973, 1, 1, 0, 0, [11, 12]) 33 assert t.utc.T.tolist() == [ 34 [1972, 12, 31, 23, 59, 60], 35 [1973, 1, 1, 0, 0, 0], 36 ] 37 38 # The offset prior to the first leap second should be +10. 39 t = ts.tai(1970, 1, 1) 40 assert t.utc == (1969, 12, 31, 23, 59, 50) 41 42def ts(): 43 yield api.load.timescale() 44 45def ts_either(): 46 # This fixture is for "tests that should pass given *either* a 47 # built-in Timescale or one loaded from a file." Without a few such 48 # tests, an adjustment to the loading scheme can introduce a bug 49 # that no tests catch, because most tests use the builtin tables 50 # intead of loading from a file. The tests below that use this 51 # fixture are ones that broke in the past when the loader code and 52 # timescale disagreed about something. 53 yield api.load.timescale() 54 yield api.load.timescale(builtin=False) 55 56def a(*args): 57 return np.array(args) 58 59def all_kinds_of_time_array(ts): 60 yield ts.utc(2020, 10, [8, 9]) 61 yield ts.tai(2020, 10, [8, 9]) 62 yield ts.tt(2020, 10, [8, 9]) 63 yield ts.tdb(2020, 10, [8, 9]) 64 yield ts.ut1(2020, 10, [8, 9]) 65 66 jd = a(2459130.5, 2459131.5) 67 68 yield ts.tai_jd(jd) 69 yield ts.tt_jd(jd) 70 yield ts.tdb_jd(jd) 71 yield ts.ut1_jd(jd) 72 yield Time(ts, jd) 73 74 for jd, fraction in [ 75 (a(2459130.5, 2459131.5), 0.25), 76 (2459130.5, a(0.0, 0.25)), 77 (a(2459130.5, 2459131.5), a(0.0, 0.25)), 78 ]: 79 yield ts.tai_jd(jd, fraction) 80 yield ts.tt_jd(jd, fraction) 81 yield ts.tdb_jd(jd, fraction) 82 # yield ts.ut1_jd(jd, fraction) # not yet supported 83 84 # We only support direct Time instantiation for the final case, 85 # where jd and fraction are arrays that already agree in their 86 # dimensions. 87 88 yield Time(ts, jd, fraction) 89 90def test_time_creation_methods(ts, continuous_timescale, time_value): 91 method = getattr(ts, continuous_timescale) 92 if isinstance(time_value, tuple): 93 t = method(*time_value) 94 else: 95 t = method(jd=time_value) # TODO: deprecate 96 assert getattr(t, continuous_timescale) == 2441700.56640625 97 98 # Also go ahead and test the calendar and formatting operations. 99 100 tup = getattr(t, continuous_timescale + '_calendar')() 101 assert tup == (1973, 1, 18, 1, 35, 37.5) 102 103 strftime = getattr(t, continuous_timescale + '_strftime') 104 string = strftime() 105 assert string == '1973-01-18 01:35:38 ' + continuous_timescale.upper() 106 107 if sys.version_info <= (3,): 108 return # we do not currently support %f under Python 2 109 110 string = strftime('%S.%f') 111 assert string == '37.500000' 112 113def test_months_overflow_correctly(ts): 114 assert ts.tt(2020, -1).tt_strftime('%Y-%m') == '2019-11' 115 assert ts.tt(2020, 15).tt_strftime('%Y-%m') == '2021-03' 116 assert ts.tt(2020, [-1, 0, 1, 13, 14, 15]).tt_strftime('%Y-%m') == [ 117 '2019-11', '2019-12', '2020-01', '2021-01', '2021-02', '2021-03', 118 ] 119 120def test_days_overflow_correctly(ts): 121 months = range(1, 13) 122 assert ts.tt(2020, months, -1).tt_strftime('%Y-%m-%d') == [ 123 '2019-12-30', '2020-01-30', '2020-02-28', '2020-03-30', 124 '2020-04-29', '2020-05-30', '2020-06-29', '2020-07-30', 125 '2020-08-30', '2020-09-29', '2020-10-30', '2020-11-29', 126 ] 127 assert ts.tt(2020, months, 0).tt_strftime('%Y-%m-%d') == [ 128 '2019-12-31', '2020-01-31', '2020-02-29', '2020-03-31', 129 '2020-04-30', '2020-05-31', '2020-06-30', '2020-07-31', 130 '2020-08-31', '2020-09-30', '2020-10-31', '2020-11-30', 131 ] 132 assert ts.tt(2020, months, 32).tt_strftime('%Y-%m-%d') == [ 133 '2020-02-01', '2020-03-03', '2020-04-01', '2020-05-02', 134 '2020-06-01', '2020-07-02', '2020-08-01', '2020-09-01', 135 '2020-10-02', '2020-11-01', '2020-12-02', '2021-01-01', 136 ] 137 138def test_time_can_be_indexed(ts): 139 for t in all_kinds_of_time_array(ts): 140 t[0] 141 142def test_is_time_iterable(ts, time_scale_name): 143 t = getattr(ts, time_scale_name)(2020, 9, (25, 26)) 144 for item in t: 145 pass 146 147def test_strftime_on_prehistoric_dates(ts_either): 148 if sys.version_info <= (3,): 149 return # Python 2 time.strftime() complains about negative years 150 151 ts = ts_either 152 t = ts.tt(-746, 2, 26) 153 assert t.utc_strftime('%Y %S') == '-746 18' 154 assert t.ut1_strftime('%Y %S') == '-746 28' 155 assert t.tai_strftime('%Y %S') == '-746 28' 156 assert t.tt_strftime('%Y %S') == '-746 00' 157 assert t.tdb_strftime('%Y %S') == '-746 00' 158 159 t = ts.tt(-746, 2, [26, 26]) 160 assert t.utc_strftime('%Y %S') == ['-746 18'] * 2 161 assert t.ut1_strftime('%Y %S') == ['-746 28'] * 2 162 assert t.tai_strftime('%Y %S') == ['-746 28'] * 2 163 assert t.tt_strftime('%Y %S') == ['-746 00'] * 2 164 assert t.tdb_strftime('%Y %S') == ['-746 00'] * 2 165 166def test_strftime_with_microseconds(): 167 if sys.version_info <= (3,): 168 return # we do not currently support %f under Python 2 169 170 ts = api.load.timescale(builtin=False) # load "ci/finals2000A.all" 171 t = ts.tt(1980, 9, 12) 172 assert t.utc_strftime('%Y %S %f') == '1980 08 816000' 173 assert t.ut1_strftime('%Y %S %f') == '1980 08 892775' 174 assert t.tai_strftime('%Y %S %f') == '1980 27 816000' 175 assert t.tt_strftime('%Y %S %f') == '1980 00 000000' 176 assert t.tdb_strftime('%Y %S %f') == '1980 59 998471' 177 178 t = ts.tt(1980, 9, [12, 12]) 179 assert t.utc_strftime('%Y %S %f') == ['1980 08 816000'] * 2 180 assert t.ut1_strftime('%Y %S %f') == ['1980 08 892775'] * 2 181 assert t.tai_strftime('%Y %S %f') == ['1980 27 816000'] * 2 182 assert t.tt_strftime('%Y %S %f') == ['1980 00 000000'] * 2 183 assert t.tdb_strftime('%Y %S %f') == ['1980 59 998471'] * 2 184 185def test_tai_fraction_loses_no_precision(ts): 186 t = ts.tai_jd(2459008.0, 0.0123456789) 187 assert t.whole == 2459008.0 188 assert t.tai_fraction == 0.0123456789 189 190def test_tdb_fraction_loses_no_precision(ts): 191 t = ts.tdb_jd(2459008.0, 0.0123456789) 192 assert t.whole == 2459008.0 193 assert t.tdb_fraction == 0.0123456789 194 195def test_tai_seconds_preserve_10_decimal_places_in_calendar_seconds(ts): 196 t = ts.tai(2020, 6, 7, 2, 2, 12.0123456789) 197 c = t.tai_calendar() 198 assert c[:5] == (2020, 6, 7, 2, 2) 199 assert '%.10f' % c[5] == '12.0123456789' 200 201def test_tt_seconds_preserve_10_decimal_places_in_calendar_seconds(ts): 202 t = ts.tt(2020, 6, 7, 2, 2, 12.0123456789) 203 c = t.tt_calendar() 204 assert c[:5] == (2020, 6, 7, 2, 2) 205 assert '%.10f' % c[5] == '12.0123456789' 206 207time_params_with_array = [ 208 ((2018, 2019, 2020), 3, 25, 13, 1, 10), 209 (2018, (3, 4, 5), 25, 13, 1, 10), 210 (2018, 3, (25, 26, 27), 13, 1, 10), 211 (2018, 3, 25, (13, 14, 15), 1, 10), 212 (2018, 3, 25, 13, (1, 2, 3), 10), 213 (2018, 3, 25, 13, 1, (10, 11, 12)), 214] 215 216def test_time_creation_with_arrays(ts, time_scale_name, time_params_with_array): 217 print(time_scale_name) 218 t = getattr(ts, time_scale_name)(*time_params_with_array) 219 t.utc_jpl() # a reasonably complicated operation 220 221def test_timescale_utc_method_with_array_inside(ts): 222 seconds = np.arange(48.0, 58.0, 1.0) 223 t = ts.utc(1973, 12, 29, 23, 59, seconds) 224 assert seconds.shape == t.shape 225 for i, second in enumerate(seconds): 226 assert t.tai[i] == ts.utc(1973, 12, 29, 23, 59, second).tai 227 228def test_that_building_time_from_naive_datetime_raises_exception(ts): 229 with assert_raises(ValueError) as info: 230 ts.from_datetime(datetime(1973, 12, 29, 23, 59, 48)) 231 assert 'import timezone' in str(info.exception) 232 233def test_building_time_from_single_utc_datetime(ts): 234 t = ts.from_datetime(datetime(1973, 12, 29, 23, 59, 48, tzinfo=utc)) 235 assert t.tai == 2442046.5 236 t = ts.utc(datetime(1973, 12, 29, 23, 59, 48, tzinfo=utc)) 237 assert t.tai == 2442046.5 238 239def test_building_time_from_single_utc_datetime_with_timezone(ts): 240 tz = timezone('US/Eastern') 241 t = ts.from_datetime(tz.localize(datetime(2020, 5, 10, 12, 44, 13, 797865))) 242 dt, leap_second = t.utc_datetime_and_leap_second() 243 assert dt == datetime(2020, 5, 10, 16, 44, 13, 797865, tzinfo=utc) 244 assert leap_second == 0 245 246def test_building_time_from_list_of_utc_datetimes(ts): 247 datetimes = [ 248 datetime(1973, 12, 29, 23, 59, 48, tzinfo=utc), 249 datetime(1973, 12, 30, 23, 59, 48, tzinfo=utc), 250 datetime(1973, 12, 31, 23, 59, 48, tzinfo=utc), 251 datetime(1974, 1, 1, 23, 59, 47, tzinfo=utc), 252 datetime(1974, 1, 2, 23, 59, 47, tzinfo=utc), 253 datetime(1974, 1, 3, 23, 59, 47, tzinfo=utc), 254 ] 255 t = ts.from_datetimes(datetimes) 256 assert list(t.tai) == [ 257 2442046.5, 2442047.5, 2442048.5, 2442049.5, 2442050.5, 2442051.5, 258 ] 259 t = ts.utc(datetimes) 260 assert list(t.tai) == [ 261 2442046.5, 2442047.5, 2442048.5, 2442049.5, 2442050.5, 2442051.5, 262 ] 263 264def test_building_time_from_python_date(ts): 265 d = dt_module.date(2020, 7, 22) 266 t = ts.utc(d) 267 assert t.utc == (2020, 7, 22, 0, 0, 0.0) 268 269def test_timescale_linspace(ts): 270 t0 = ts.tt(2021, 11, 3, 6) 271 t1 = ts.tt(2021, 11, 5, 18) 272 t = ts.linspace(t0, t1, 3) 273 assert [n for a in t.tt_calendar() for n in a] == [ 274 2021, 2021, 2021, 275 11, 11, 11, 276 3, 4, 5, 277 6, 12, 18, 278 0, 0, 0, 279 0, 0, 0, 280 ] 281 282def test_converting_ut1_to_tt(ts): 283 ten_thousand_years = 365 * 10000 284 285 jd = api.T0 - ten_thousand_years 286 t = ts.ut1(jd=jd) 287 del t.ut1_fraction # force re-computation of UT1 288 print(jd - t.ut1) 289 assert abs(jd - t.ut1) < 1e-10 290 291 jd = api.T0 + ten_thousand_years 292 t = ts.ut1(jd=jd) 293 del t.ut1_fraction # force re-computation of UT1 294 print(jd - t.ut1) 295 assert abs(jd - t.ut1) < 1e-10 296 297def test_indexing_time(ts): 298 t = ts.utc(1974, 10, range(1, 6)) 299 assert t.shape == (5,) 300 t0 = t[0] 301 assert t.tai[0] == t0.tai 302 assert t.tt[0] == t0.tt 303 assert t.tdb[0] == t0.tdb 304 assert t.ut1[0] == t0.ut1 305 assert t.delta_t[0] == t0.delta_t 306 307def test_slicing_time(ts): 308 t = ts.utc(1974, 10, range(1, 6)) 309 assert t.shape == (5,) 310 t24 = t[2:4] 311 assert t24.shape == (2,) 312 assert (t.tai[2:4] == t24.tai).all() 313 assert (t.tt[2:4] == t24.tt).all() 314 assert (t.tdb[2:4] == t24.tdb).all() 315 assert (t.ut1[2:4] == t24.ut1).all() 316 assert (t.delta_t[2:4] == t24.delta_t).all() 317 318def test_early_utc(ts_either): 319 ts = ts_either 320 t = ts.utc(1915, 12, 2, 3, 4, 5.6786786) 321 assert abs(t.tt - 2420833.6283317441) < epsilon 322 assert t.utc_iso() == '1915-12-02T03:04:06Z' 323 324def test_astimezone(ts): 325 t = ts.utc(1969, 7, 20, 20, 18) 326 tz = timezone('US/Eastern') 327 dt = t.astimezone(tz) 328 assert dt == tz.localize(datetime(1969, 7, 20, 16, 18, 0, 0)) 329 330def test_astimezone_and_leap_second(ts): 331 t = ts.utc(1969, 7, 20, 20, 18) 332 tz = timezone('US/Eastern') 333 dt, leap_second = t.astimezone_and_leap_second(tz) 334 assert dt == tz.localize(datetime(1969, 7, 20, 16, 18, 0, 0)) 335 assert leap_second == 0 336 337def test_toordinal(ts): 338 t = ts.utc(1973, 12, 31, 11, 59, 60) 339 assert t.toordinal() == 720623.5 340 341def test_utc_datetime(ts): 342 t = ts.utc(1969, 7, 20, 20, 18, 42.186479) 343 dt = t.utc_datetime() 344 assert dt == datetime(1969, 7, 20, 20, 18, 42, 186479, utc) 345 346def test_utc_datetime_and_leap_second(ts): 347 t = ts.utc(1969, 7, 20, 20, 18) 348 dt, leap_second = t.utc_datetime_and_leap_second() 349 assert dt == datetime(1969, 7, 20, 20, 18, 0, 0, utc) 350 assert leap_second == 0 351 352def test_utc_datetime_microseconds_round_trip(ts): 353 dt = datetime(2020, 5, 10, 11, 50, 9, 727799, tzinfo=utc) 354 t = ts.from_datetime(dt) 355 dt2, leap_second = t.utc_datetime_and_leap_second() 356 assert dt2 == dt 357 assert leap_second == 0 358 359def test_utc_datetime_agrees_with_public_utc_tuple(ts): 360 # https://github.com/skyfielders/python-skyfield/issues/542 361 # The %j day-of-year was advancing to the next day before strftime. 362 t = ts.utc(2021, 1, 1, 23, 59, 59.9999798834251798497) 363 assert t.utc[:5] == (2021, 1, 1, 23, 59) 364 assert t.utc_strftime("%j") == '001' 365 366def test_iso_of_decimal_that_rounds_up(ts): 367 t = ts.utc(1915, 12, 2, 3, 4, 5.6786786) 368 assert t.utc_iso(places=0) == '1915-12-02T03:04:06Z' 369 assert t.utc_iso(places=1) == '1915-12-02T03:04:05.7Z' 370 assert t.utc_iso(places=2) == '1915-12-02T03:04:05.68Z' 371 assert t.utc_iso(places=3) == '1915-12-02T03:04:05.679Z' 372 assert t.utc_iso(places=4) == '1915-12-02T03:04:05.6787Z' 373 374def test_iso_of_decimal_that_rounds_down(ts): 375 t = ts.utc(2014, 12, 21, 6, 3, 1.234234) 376 assert t.utc_iso(places=0) == '2014-12-21T06:03:01Z' 377 assert t.utc_iso(places=1) == '2014-12-21T06:03:01.2Z' 378 assert t.utc_iso(places=2) == '2014-12-21T06:03:01.23Z' 379 assert t.utc_iso(places=3) == '2014-12-21T06:03:01.234Z' 380 assert t.utc_iso(places=4) == '2014-12-21T06:03:01.2342Z' 381 382def test_iso_of_leap_second_with_fraction(ts): 383 t = ts.utc(1973, 12, 31, 23, 59, 60.12349) 384 assert t.utc_iso(places=0) == '1973-12-31T23:59:60Z' 385 assert t.utc_iso(places=1) == '1973-12-31T23:59:60.1Z' 386 assert t.utc_iso(places=2) == '1973-12-31T23:59:60.12Z' 387 assert t.utc_iso(places=3) == '1973-12-31T23:59:60.123Z' 388 assert t.utc_iso(places=4) == '1973-12-31T23:59:60.1235Z' 389 390def test_iso_of_array_showing_whole_seconds(ts): 391 t = ts.utc(1973, 12, 31, 23, 59, np.arange(58.75, 63.1, 0.5)) 392 assert t.utc_iso(places=0) == [ 393 '1973-12-31T23:59:59Z', 394 '1973-12-31T23:59:59Z', 395 '1973-12-31T23:59:60Z', 396 '1973-12-31T23:59:60Z', 397 '1974-01-01T00:00:00Z', 398 '1974-01-01T00:00:00Z', 399 '1974-01-01T00:00:01Z', 400 '1974-01-01T00:00:01Z', 401 '1974-01-01T00:00:02Z', 402 ] 403 404def test_iso_of_array_showing_fractions(ts): 405 t = ts.utc(1973, 12, 31, 23, 59, np.arange(58.75, 63.1, 0.5)) 406 assert t.utc_iso(places=2) == [ 407 '1973-12-31T23:59:58.75Z', 408 '1973-12-31T23:59:59.25Z', 409 '1973-12-31T23:59:59.75Z', 410 '1973-12-31T23:59:60.25Z', 411 '1973-12-31T23:59:60.75Z', 412 '1974-01-01T00:00:00.25Z', 413 '1974-01-01T00:00:00.75Z', 414 '1974-01-01T00:00:01.25Z', 415 '1974-01-01T00:00:01.75Z', 416 ] 417 418def test_jpl_format(ts): 419 t = ts.utc(range(-300, 301, 100), 7, 1) 420 assert t.utc_jpl() == [ 421 'B.C. 0301-Jul-01 00:00:00.0000 UTC', 422 'B.C. 0201-Jul-01 00:00:00.0000 UTC', 423 'B.C. 0101-Jul-01 00:00:00.0000 UTC', 424 'B.C. 0001-Jul-01 00:00:00.0000 UTC', 425 'A.D. 0100-Jul-01 00:00:00.0000 UTC', 426 'A.D. 0200-Jul-01 00:00:00.0000 UTC', 427 'A.D. 0300-Jul-01 00:00:00.0000 UTC', 428 ] 429 430def test_strftime_of_a_leap_second(ts): 431 t = ts.utc(1973, 12, 31, 23, 59, 60) 432 assert t.utc_strftime('%Y %m %d %H %M %S') == '1973 12 31 23 59 60' 433 434def test_strftime_of_date_array_over_a_leap_second(ts): 435 t = ts.utc(1973, 12, 31, 23, 59, np.arange(59.0, 61.1, 1.0)) 436 assert t.utc_strftime('%a %Y %m %d %H %M %S') == [ 437 'Mon 1973 12 31 23 59 59', 438 'Mon 1973 12 31 23 59 60', 439 'Tue 1974 01 01 00 00 00', 440 ] 441 442def test_strftime_day_of_year(ts): 443 # Based on example date at https://strftime.org/ 444 assert ts.utc(2013, 9, 29).utc_strftime('%j') == '272' 445 assert ts.utc(2013, 9, 30).utc_strftime('%j') == '273' 446 assert ts.utc(2013, 9, 30, 23, 59).utc_strftime('%j') == '273' 447 assert ts.utc(2013, 9, 30, 23, 60).utc_strftime('%j') == '274' 448 449 assert ts.utc(2013, 9, [29, 30]).utc_strftime('%j') == ['272', '273'] 450 451def test_leap_second(ts): 452 453 # During 1973 the offset between UTC and TAI was 12.0 seconds, so 454 # TAI should reach the first moment of 1974 while the UTC clock is 455 # still reading 12s before midnight (60 - 12 = 48). Happily, the 456 # fraction 0.5 can be precisely represented in floating point, so we 457 # can use a bare `==` in this assert: 458 459 t0 = ts.utc(1973, 12, 31, 23, 59, 48.0).tai 460 assert t0 == 2442048.5 461 462 # Here are some more interesting values: 463 464 t1 = ts.utc(1973, 12, 31, 23, 59, 58.0).tai 465 t2 = ts.utc(1973, 12, 31, 23, 59, 59.0).tai 466 t3 = ts.utc(1973, 12, 31, 23, 59, 60.0).tai 467 t4 = ts.utc(1974, 1, 1, 0, 0, 0.0).tai 468 t5 = ts.utc(1974, 1, 1, 0, 0, 1.0).tai 469 470 # The step from 23:59:59 to 0:00:00 is here a two-second step, 471 # because of the leap second 23:59:60 that falls in between: 472 473 assert abs(t4 - t2 - 2.0 * one_second) < epsilon 474 475 # Otherwise, the five dates given above are all one second apart: 476 477 assert abs(t2 - t1 - one_second) < epsilon 478 assert abs(t3 - t2 - one_second) < epsilon 479 assert abs(t4 - t3 - one_second) < epsilon 480 assert abs(t5 - t4 - one_second) < epsilon 481 482 # And all these dates can be converted back to UTC. 483 484 assert ts.tai(jd=t0).utc_iso() == '1973-12-31T23:59:48Z' 485 assert ts.tai(jd=t1).utc_iso() == '1973-12-31T23:59:58Z' 486 assert ts.tai(jd=t2).utc_iso() == '1973-12-31T23:59:59Z' 487 assert ts.tai(jd=t3).utc_iso() == '1973-12-31T23:59:60Z' 488 assert ts.tai(jd=t4).utc_iso() == '1974-01-01T00:00:00Z' 489 assert ts.tai(jd=t5).utc_iso() == '1974-01-01T00:00:01Z' 490 491def test_leap_second_sensitivity(ts): 492 t = ts.utc(2017, 1, 1, 0, 0, 0) 493 a, b = t.whole, t.tai_fraction 494 495 # First, make sure the UTC time round-trips. 496 t2 = ts.tai_jd(a, b) 497 tup = t2._utc_tuple(0.0) 498 assert tup == (2017, 1, 1, 0, 0, 0.0) 499 500 # Second, bump back infinitesimally into the previous leap second 501 # and make sure the UTC time is just shy of second 61.0. 502 b = np.nextafter(b, -1) 503 t3 = ts.tai_jd(a, b) 504 tup = t3._utc_tuple(0.0) 505 assert tup[:5] == (2016, 12, 31, 23, 59) 506 assert tup[5] > 60.99999999999 507 508def test_delta_t(ts): 509 # The IERS "finals2000A.all" for 2000 Jan 1 gives DUT1 = 0.3554779, 510 # and 0.3554779 - 0.184 - 1.0 = -0.8285221. 511 t = ts.utc(2000, 1, 1, 0, 0, 0) 512 assert t.delta_t == 63.8285221 513 514 # Check historic value. Compare to the table in Morrison and 515 # Stephenson 2004, the tolerance is 2 sigma 516 t = ts.utc(year=1000) 517 assert abs(t.delta_t - 1570.0) < 110.0 518 519 # Check far-future value against the long-term parabola formula. 520 centuries = 20 521 t = ts.J(1825 + centuries * 100) 522 assert t.delta_t == -320 + 32.5 * centuries**2 523 524def test_dut1(ts): 525 # Roughly agreeing with tables on NIST website 526 t = ts.utc(2017, 3, 30) 527 assert str(t.dut1)[:3] == '0.4' 528 529 t = ts.utc(2018, 9, 21) 530 assert str(t.dut1)[:3] == '0.0' 531 532 t = ts.utc(2019, 5, 2) 533 assert str(t.dut1)[:4] == '-0.1' # table says -0.2 534 535def test_polar_motion_table(): 536 with api.load.open('finals2000A.all') as f: 537 finals_data = iers.parse_x_y_dut1_from_finals_all(f) 538 539 ts = api.load.timescale() 540 iers.install_polar_motion_table(ts, finals_data) 541 542 bump = 1e-4 # TODO: can this be improved? 543 544 t = ts.utc(1973, 1, 3, 0, 0, -bump) 545 sprime, x, y = t.polar_motion_angles() 546 assert x > 0.118980 547 assert y > 0.135656 548 549 t = ts.utc(1973, 1, 3, 0, 0, +bump) 550 sprime, x, y = t.polar_motion_angles() 551 assert x < 0.118980 552 assert y < 0.135656 553 554def test_J(ts): 555 assert ts.J(2000).tt == T0 556 assert ts.J(1900).tt == T0 - 36525.0 557 assert (ts.J([1900, 2000]).tt == [T0 - 36525.0, T0]).all() 558 assert ts.tt(2000, 1, 1.5).J == 2000.0 559 assert ts.tt(1900, 1, 0.5).J == 1900.0 560 561def test_time_repr(ts): 562 563 # Check that repr return is a str (this is required on Python 2, 564 # unicode is not allowed) 565 assert isinstance(repr(ts.utc(year=2000)), str) 566 567 # Check array conversion 568 assert isinstance(repr(ts.utc(year=range(2000, 2010))), str) 569 570 assert repr(ts.tt_jd(1)) == '<Time tt=1.0>' 571 assert repr(ts.tt_jd([])) == '<Time tt=[]>' 572 assert repr(ts.tt_jd([1])) == '<Time tt=[1.]>' 573 assert repr(ts.tt_jd([1, 2])) == '<Time tt=[1. 2.]>' 574 assert repr(ts.tt_jd([1, 2, 3])) == '<Time tt=[1. 2. 3.]>' 575 assert repr(ts.tt_jd([1, 2, 3, 4])) == '<Time tt=[1.0 ... 4.0] len=4>' 576 577def test_jd_calendar(): 578 # Check a specific instance (using UNIX epoch here, though that's 579 # an arbitrary choice) 580 jd_unix = 2440587.5 581 cal_unix = (1970, 1, 1, 0, 0, 0.0) 582 583 cal = calendar_tuple(jd_unix) 584 585 # Check that the returned value is correct 586 assert cal == cal_unix 587 588 # Check that all the return types are correct 589 assert isinstance(cal[0], numbers.Integral) # Year 590 assert isinstance(cal[1], numbers.Integral) # Month 591 assert isinstance(cal[2], numbers.Integral) # Day 592 assert isinstance(cal[3], numbers.Integral) # Hour 593 assert isinstance(cal[4], numbers.Integral) # Minute 594 assert isinstance(cal[5], numbers.Real) # Second 595 596 # Check backward conversion 597 assert julian_date(*cal) == jd_unix 598 599 # Check array conversion components 600 jd_array = jd_unix + np.arange(5.0) 601 cal_array = calendar_tuple(jd_array) 602 603 assert (cal_array[0] == 1970).all() 604 assert (cal_array[1] == 1).all() 605 assert (cal_array[2] == np.arange(1, 6)).all() 606 assert (cal_array[3] == 0).all() 607 assert (cal_array[4] == 0).all() 608 assert (cal_array[5] == 0.0).all() 609 610 # Check reversal of array 611 assert (julian_date(*cal_array) == jd_array).all() 612 613def test_raw_julian_gregorian_cutover(): 614 gregory = 2299161 615 assert compute_calendar_date(gregory - 2, gregory) == (1582, 10, 3) 616 assert compute_calendar_date(gregory - 1, gregory) == (1582, 10, 4) 617 assert compute_calendar_date(gregory + 0, gregory) == (1582, 10, 15) 618 assert compute_calendar_date(gregory + 1, gregory) == (1582, 10, 16) 619 620 jd = np.arange(gregory - 2, gregory + 2) 621 assert [list(a) for a in compute_calendar_date(jd, gregory)] == [ 622 [1582, 1582, 1582, 1582], 623 [10, 10, 10, 10], 624 [3, 4, 15, 16], 625 ] 626 627 assert julian_day(1582, 10, 3, gregory) == (gregory - 2) 628 assert julian_day(1582, 10, 4, gregory) == (gregory - 1) 629 assert julian_day(1582, 10, 15, gregory) == (gregory + 0) 630 assert julian_day(1582, 10, 16, gregory) == (gregory + 1) 631 632 days = [3, 4, 15, 16] 633 assert list(julian_day(1582, 10, np.array(days), gregory)) == [ 634 2299159, 2299160, 2299161, 2299162, 635 ] 636 637def test_constructor_julian_gregorian_cutover(time_scale_name): 638 if sys.version_info <= (3,): 639 return # Python 2 time.strftime() complains about the year 1582 640 641 def jd(y, m, d): 642 t = getattr(ts, time_scale_name)(y, m, d) 643 if time_scale_name == 'utc': 644 return sum(t._utc_seconds(0.0)) / DAY_S 645 return getattr(t, time_scale_name) 646 647 ts = api.load.timescale() 648 649 assert jd(1582, 10, 4) == 2299149.5 650 assert jd(1582, 10, 15) == 2299160.5 651 assert jd(1752, 9, 2) == 2361209.5 652 assert jd(1752, 9, 14) == 2361221.5 653 654 ts.julian_calendar_cutoff = GREGORIAN_START 655 656 assert jd(1582, 10, 4) == 2299159.5 657 assert jd(1582, 10, 15) == 2299160.5 658 assert jd(1752, 9, 2) == 2361209.5 659 assert jd(1752, 9, 14) == 2361221.5 660 661 ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND 662 663 assert jd(1582, 10, 4) == 2299159.5 664 assert jd(1582, 10, 15) == 2299170.5 665 assert jd(1752, 9, 2) == 2361220.5 666 assert jd(1752, 9, 14) == 2361221.5 667 668def test_calendar_tuple_julian_gregorian_cutover(time_scale_name): 669 if sys.version_info <= (3,): 670 return # Python 2 time.strftime() complains about the year 1582 671 672 def ymd(jd): 673 t = ts.tt_jd(jd, 0.1) 674 if time_scale_name == 'utc': 675 return t.utc[:3] 676 return getattr(t, time_scale_name + '_calendar')()[:3] 677 678 ts = api.load.timescale() 679 680 assert ymd(2299149.5) == (1582, 10, 4) 681 assert ymd(2299160.5) == (1582, 10, 15) 682 assert ymd(2361209.5) == (1752, 9, 2) 683 assert ymd(2361221.5) == (1752, 9, 14) 684 685 ts.julian_calendar_cutoff = GREGORIAN_START 686 687 assert ymd(2299159.5) == (1582, 10, 4) 688 assert ymd(2299160.5) == (1582, 10, 15) 689 assert ymd(2361209.5) == (1752, 9, 2) 690 assert ymd(2361221.5) == (1752, 9, 14) 691 692 ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND 693 694 assert ymd(2299159.5) == (1582, 10, 4) 695 assert ymd(2299170.5) == (1582, 10, 15) 696 assert ymd(2361220.5) == (1752, 9, 2) 697 assert ymd(2361221.5) == (1752, 9, 14) 698 699def test_strftime_julian_gregorian_cutover(time_scale_name): 700 if sys.version_info <= (3,): 701 return # Python 2 time.strftime() complains about the year 1582 702 703 def ymd(jd): 704 t = ts.tt_jd(jd, 0.1) 705 return getattr(t, time_scale_name + '_strftime')('%Y %m %d') 706 707 ts = api.load.timescale() 708 709 assert ymd(2299149.5) == '1582 10 04' 710 assert ymd(2299160.5) == '1582 10 15' 711 assert ymd(2361209.5) == '1752 09 02' 712 assert ymd(2361221.5) == '1752 09 14' 713 714 ts.julian_calendar_cutoff = GREGORIAN_START 715 716 assert ymd(2299159.5) == '1582 10 04' 717 assert ymd(2299160.5) == '1582 10 15' 718 assert ymd(2361209.5) == '1752 09 02' 719 assert ymd(2361221.5) == '1752 09 14' 720 721 ts.julian_calendar_cutoff = GREGORIAN_START_ENGLAND 722 723 assert ymd(2299159.5) == '1582 10 04' 724 assert ymd(2299170.5) == '1582 10 15' 725 assert ymd(2361220.5) == '1752 09 02' 726 assert ymd(2361221.5) == '1752 09 14' 727 728def test_time_equality(ts): 729 t0 = ts.tt_jd(2459008.5, 0.125) 730 t1 = ts.tt_jd(2459008.0, 0.625) 731 assert t0 == t1 732 assert t1 - t0 == 0.0 733 assert hash(t0) == hash(t1) 734 735 t2 = ts.tt_jd(2459008.0, 0.6251) 736 assert t2 != t0 737 assert t2 - t0 > 0 738 assert hash(t0) != hash(t2) 739 740def test_time_math(ts): 741 t = ts.tt_jd(2459008.5, 0.125) 742 743 assert (t - 1).tt_strftime() == '2020-06-07 03:00:00 TT' 744 assert (t + 1).tt_strftime() == '2020-06-09 03:00:00 TT' 745 746 assert (t - 1.25).tt_strftime() == '2020-06-06 21:00:00 TT' 747 assert (t + 1.25).tt_strftime() == '2020-06-09 09:00:00 TT' 748 749 bump = dt_module.timedelta(days=1, seconds=1) 750 assert (t - bump).tt_strftime() == '2020-06-07 02:59:59 TT' 751 assert (t + bump).tt_strftime() == '2020-06-09 03:00:01 TT' 752 753 bump = dt_module.timedelta(microseconds=300) 754 assert (t - bump).utc_jpl() == 'A.D. 2020-Jun-08 02:58:50.8157 UTC' 755 assert (t + bump).utc_jpl() == 'A.D. 2020-Jun-08 02:58:50.8163 UTC' 756