1# Licensed under a 3-clause BSD style license - see LICENSE.rst 2 3import os 4import copy 5import functools 6import datetime 7from copy import deepcopy 8from decimal import Decimal, localcontext 9 10import numpy as np 11import pytest 12from numpy.testing import assert_allclose 13import erfa 14from erfa import ErfaWarning 15 16from astropy.utils.exceptions import AstropyDeprecationWarning 17from astropy.utils import isiterable, iers 18from astropy.time import (Time, TimeDelta, ScaleValueError, STANDARD_TIME_SCALES, 19 TimeString, TimezoneInfo, TIME_FORMATS) 20from astropy.coordinates import EarthLocation 21from astropy import units as u 22from astropy.table import Column, Table 23from astropy.utils.compat.optional_deps import HAS_PYTZ # noqa 24 25 26allclose_jd = functools.partial(np.allclose, rtol=np.finfo(float).eps, atol=0) 27allclose_jd2 = functools.partial(np.allclose, rtol=np.finfo(float).eps, 28 atol=np.finfo(float).eps) # 20 ps atol 29allclose_sec = functools.partial(np.allclose, rtol=np.finfo(float).eps, 30 atol=np.finfo(float).eps * 24 * 3600) 31allclose_year = functools.partial(np.allclose, rtol=np.finfo(float).eps, 32 atol=0.) # 14 microsec at current epoch 33 34 35def setup_function(func): 36 func.FORMATS_ORIG = deepcopy(Time.FORMATS) 37 38 39def teardown_function(func): 40 Time.FORMATS.clear() 41 Time.FORMATS.update(func.FORMATS_ORIG) 42 43 44class TestBasic: 45 """Basic tests stemming from initial example and API reference""" 46 47 def test_simple(self): 48 times = ['1999-01-01 00:00:00.123456789', '2010-01-01 00:00:00'] 49 t = Time(times, format='iso', scale='utc') 50 assert (repr(t) == "<Time object: scale='utc' format='iso' " 51 "value=['1999-01-01 00:00:00.123' '2010-01-01 00:00:00.000']>") 52 assert allclose_jd(t.jd1, np.array([2451180., 2455198.])) 53 assert allclose_jd2(t.jd2, np.array([-0.5 + 1.4288980208333335e-06, 54 -0.50000000e+00])) 55 56 # Set scale to TAI 57 t = t.tai 58 assert (repr(t) == "<Time object: scale='tai' format='iso' " 59 "value=['1999-01-01 00:00:32.123' '2010-01-01 00:00:34.000']>") 60 assert allclose_jd(t.jd1, np.array([2451180., 2455198.])) 61 assert allclose_jd2(t.jd2, np.array([-0.5 + 0.00037179926839122024, 62 -0.5 + 0.00039351851851851852])) 63 64 # Get a new ``Time`` object which is referenced to the TT scale 65 # (internal JD1 and JD1 are now with respect to TT scale)""" 66 67 assert (repr(t.tt) == "<Time object: scale='tt' format='iso' " 68 "value=['1999-01-01 00:01:04.307' '2010-01-01 00:01:06.184']>") 69 70 # Get the representation of the ``Time`` object in a particular format 71 # (in this case seconds since 1998.0). This returns either a scalar or 72 # array, depending on whether the input was a scalar or array""" 73 74 assert allclose_sec(t.cxcsec, np.array([31536064.307456788, 378691266.18400002])) 75 76 def test_different_dimensions(self): 77 """Test scalars, vector, and higher-dimensions""" 78 # scalar 79 val, val1 = 2450000.0, 0.125 80 t1 = Time(val, val1, format='jd') 81 assert t1.isscalar is True and t1.shape == () 82 # vector 83 val = np.arange(2450000., 2450010.) 84 t2 = Time(val, format='jd') 85 assert t2.isscalar is False and t2.shape == val.shape 86 # explicitly check broadcasting for mixed vector, scalar. 87 val2 = 0. 88 t3 = Time(val, val2, format='jd') 89 assert t3.isscalar is False and t3.shape == val.shape 90 val2 = (np.arange(5.) / 10.).reshape(5, 1) 91 # now see if broadcasting to two-dimensional works 92 t4 = Time(val, val2, format='jd') 93 assert t4.isscalar is False 94 assert t4.shape == np.broadcast(val, val2).shape 95 96 @pytest.mark.parametrize('format_', Time.FORMATS) 97 def test_empty_value(self, format_): 98 t = Time([], format=format_) 99 assert t.size == 0 100 assert t.shape == (0,) 101 assert t.format == format_ 102 t_value = t.value 103 assert t_value.size == 0 104 assert t_value.shape == (0,) 105 t2 = Time(t_value, format=format_) 106 assert t2.size == 0 107 assert t2.shape == (0,) 108 assert t2.format == format_ 109 t3 = t2.tai 110 assert t3.size == 0 111 assert t3.shape == (0,) 112 assert t3.format == format_ 113 assert t3.scale == 'tai' 114 115 @pytest.mark.parametrize('value', [2455197.5, [2455197.5]]) 116 def test_copy_time(self, value): 117 """Test copying the values of a Time object by passing it into the 118 Time initializer. 119 """ 120 t = Time(value, format='jd', scale='utc') 121 122 t2 = Time(t, copy=False) 123 assert np.all(t.jd - t2.jd == 0) 124 assert np.all((t - t2).jd == 0) 125 assert t._time.jd1 is t2._time.jd1 126 assert t._time.jd2 is t2._time.jd2 127 128 t2 = Time(t, copy=True) 129 assert np.all(t.jd - t2.jd == 0) 130 assert np.all((t - t2).jd == 0) 131 assert t._time.jd1 is not t2._time.jd1 132 assert t._time.jd2 is not t2._time.jd2 133 134 # Include initializers 135 t2 = Time(t, format='iso', scale='tai', precision=1) 136 assert t2.value == '2010-01-01 00:00:34.0' 137 t2 = Time(t, format='iso', scale='tai', out_subfmt='date') 138 assert t2.value == '2010-01-01' 139 140 def test_getitem(self): 141 """Test that Time objects holding arrays are properly subscriptable, 142 set isscalar as appropriate, and also subscript delta_ut1_utc, etc.""" 143 144 mjd = np.arange(50000, 50010) 145 t = Time(mjd, format='mjd', scale='utc', location=('45d', '50d')) 146 t1 = t[3] 147 assert t1.isscalar is True 148 assert t1._time.jd1 == t._time.jd1[3] 149 assert t1.location is t.location 150 t1a = Time(mjd[3], format='mjd', scale='utc') 151 assert t1a.isscalar is True 152 assert np.all(t1._time.jd1 == t1a._time.jd1) 153 t1b = Time(t[3]) 154 assert t1b.isscalar is True 155 assert np.all(t1._time.jd1 == t1b._time.jd1) 156 t2 = t[4:6] 157 assert t2.isscalar is False 158 assert np.all(t2._time.jd1 == t._time.jd1[4:6]) 159 assert t2.location is t.location 160 t2a = Time(t[4:6]) 161 assert t2a.isscalar is False 162 assert np.all(t2a._time.jd1 == t._time.jd1[4:6]) 163 t2b = Time([t[4], t[5]]) 164 assert t2b.isscalar is False 165 assert np.all(t2b._time.jd1 == t._time.jd1[4:6]) 166 t2c = Time((t[4], t[5])) 167 assert t2c.isscalar is False 168 assert np.all(t2c._time.jd1 == t._time.jd1[4:6]) 169 t.delta_tdb_tt = np.arange(len(t)) # Explicitly set (not testing .tdb) 170 t3 = t[4:6] 171 assert np.all(t3._delta_tdb_tt == t._delta_tdb_tt[4:6]) 172 t4 = Time(mjd, format='mjd', scale='utc', 173 location=(np.arange(len(mjd)), np.arange(len(mjd)))) 174 t5a = t4[3] 175 assert t5a.location == t4.location[3] 176 assert t5a.location.shape == () 177 t5b = t4[3:4] 178 assert t5b.location.shape == (1,) 179 # Check that indexing a size-1 array returns a scalar location as well; 180 # see gh-10113. 181 t5c = t5b[0] 182 assert t5c.location.shape == () 183 t6 = t4[4:6] 184 assert np.all(t6.location == t4.location[4:6]) 185 # check it is a view 186 # (via ndarray, since quantity setter problematic for structured array) 187 allzeros = np.array((0., 0., 0.), dtype=t4.location.dtype) 188 assert t6.location.view(np.ndarray)[-1] != allzeros 189 assert t4.location.view(np.ndarray)[5] != allzeros 190 t6.location.view(np.ndarray)[-1] = allzeros 191 assert t4.location.view(np.ndarray)[5] == allzeros 192 # Test subscription also works for two-dimensional arrays. 193 frac = np.arange(0., 0.999, 0.2) 194 t7 = Time(mjd[:, np.newaxis] + frac, format='mjd', scale='utc', 195 location=('45d', '50d')) 196 assert t7[0, 0]._time.jd1 == t7._time.jd1[0, 0] 197 assert t7[0, 0].isscalar is True 198 assert np.all(t7[5]._time.jd1 == t7._time.jd1[5]) 199 assert np.all(t7[5]._time.jd2 == t7._time.jd2[5]) 200 assert np.all(t7[:, 2]._time.jd1 == t7._time.jd1[:, 2]) 201 assert np.all(t7[:, 2]._time.jd2 == t7._time.jd2[:, 2]) 202 assert np.all(t7[:, 0]._time.jd1 == t._time.jd1) 203 assert np.all(t7[:, 0]._time.jd2 == t._time.jd2) 204 # Get tdb to check that delta_tdb_tt attribute is sliced properly. 205 t7_tdb = t7.tdb 206 assert t7_tdb[0, 0].delta_tdb_tt == t7_tdb.delta_tdb_tt[0, 0] 207 assert np.all(t7_tdb[5].delta_tdb_tt == t7_tdb.delta_tdb_tt[5]) 208 assert np.all(t7_tdb[:, 2].delta_tdb_tt == t7_tdb.delta_tdb_tt[:, 2]) 209 # Explicitly set delta_tdb_tt attribute. Now it should not be sliced. 210 t7.delta_tdb_tt = 0.1 211 t7_tdb2 = t7.tdb 212 assert t7_tdb2[0, 0].delta_tdb_tt == 0.1 213 assert t7_tdb2[5].delta_tdb_tt == 0.1 214 assert t7_tdb2[:, 2].delta_tdb_tt == 0.1 215 # Check broadcasting of location. 216 t8 = Time(mjd[:, np.newaxis] + frac, format='mjd', scale='utc', 217 location=(np.arange(len(frac)), np.arange(len(frac)))) 218 assert t8[0, 0].location == t8.location[0, 0] 219 assert np.all(t8[5].location == t8.location[5]) 220 assert np.all(t8[:, 2].location == t8.location[:, 2]) 221 # Finally check empty array. 222 t9 = t[:0] 223 assert t9.isscalar is False 224 assert t9.shape == (0,) 225 assert t9.size == 0 226 227 def test_properties(self): 228 """Use properties to convert scales and formats. Note that the UT1 to 229 UTC transformation requires a supplementary value (``delta_ut1_utc``) 230 that can be obtained by interpolating from a table supplied by IERS. 231 This is tested separately.""" 232 233 t = Time('2010-01-01 00:00:00', format='iso', scale='utc') 234 t.delta_ut1_utc = 0.3341 # Explicitly set one part of the xform 235 assert allclose_jd(t.jd, 2455197.5) 236 assert t.iso == '2010-01-01 00:00:00.000' 237 assert t.tt.iso == '2010-01-01 00:01:06.184' 238 assert t.tai.fits == '2010-01-01T00:00:34.000' 239 assert allclose_jd(t.utc.jd, 2455197.5) 240 assert allclose_jd(t.ut1.jd, 2455197.500003867) 241 assert t.tcg.isot == '2010-01-01T00:01:06.910' 242 assert allclose_sec(t.unix, 1262304000.0) 243 assert allclose_sec(t.cxcsec, 378691266.184) 244 assert allclose_sec(t.gps, 946339215.0) 245 assert t.datetime == datetime.datetime(2010, 1, 1) 246 247 def test_precision(self): 248 """Set the output precision which is used for some formats. This is 249 also a test of the code that provides a dict for global and instance 250 options.""" 251 252 t = Time('2010-01-01 00:00:00', format='iso', scale='utc') 253 # Uses initial class-defined precision=3 254 assert t.iso == '2010-01-01 00:00:00.000' 255 256 # Set instance precision to 9 257 t.precision = 9 258 assert t.iso == '2010-01-01 00:00:00.000000000' 259 assert t.tai.utc.iso == '2010-01-01 00:00:00.000000000' 260 261 def test_transforms(self): 262 """Transform from UTC to all supported time scales (TAI, TCB, TCG, 263 TDB, TT, UT1, UTC). This requires auxiliary information (latitude and 264 longitude).""" 265 266 lat = 19.48125 267 lon = -155.933222 268 t = Time('2006-01-15 21:24:37.5', format='iso', scale='utc', 269 precision=7, location=(lon, lat)) 270 t.delta_ut1_utc = 0.3341 # Explicitly set one part of the xform 271 assert t.utc.iso == '2006-01-15 21:24:37.5000000' 272 assert t.ut1.iso == '2006-01-15 21:24:37.8341000' 273 assert t.tai.iso == '2006-01-15 21:25:10.5000000' 274 assert t.tt.iso == '2006-01-15 21:25:42.6840000' 275 assert t.tcg.iso == '2006-01-15 21:25:43.3226905' 276 assert t.tdb.iso == '2006-01-15 21:25:42.6843728' 277 assert t.tcb.iso == '2006-01-15 21:25:56.8939523' 278 279 def test_transforms_no_location(self): 280 """Location should default to geocenter (relevant for TDB, TCB).""" 281 t = Time('2006-01-15 21:24:37.5', format='iso', scale='utc', 282 precision=7) 283 t.delta_ut1_utc = 0.3341 # Explicitly set one part of the xform 284 assert t.utc.iso == '2006-01-15 21:24:37.5000000' 285 assert t.ut1.iso == '2006-01-15 21:24:37.8341000' 286 assert t.tai.iso == '2006-01-15 21:25:10.5000000' 287 assert t.tt.iso == '2006-01-15 21:25:42.6840000' 288 assert t.tcg.iso == '2006-01-15 21:25:43.3226905' 289 assert t.tdb.iso == '2006-01-15 21:25:42.6843725' 290 assert t.tcb.iso == '2006-01-15 21:25:56.8939519' 291 # Check we get the same result 292 t2 = Time('2006-01-15 21:24:37.5', format='iso', scale='utc', 293 location=(0*u.m, 0*u.m, 0*u.m)) 294 assert t == t2 295 assert t.tdb == t2.tdb 296 297 def test_location(self): 298 """Check that location creates an EarthLocation object, and that 299 such objects can be used as arguments. 300 """ 301 lat = 19.48125 302 lon = -155.933222 303 t = Time(['2006-01-15 21:24:37.5'], format='iso', scale='utc', 304 precision=6, location=(lon, lat)) 305 assert isinstance(t.location, EarthLocation) 306 location = EarthLocation(lon, lat) 307 t2 = Time(['2006-01-15 21:24:37.5'], format='iso', scale='utc', 308 precision=6, location=location) 309 assert isinstance(t2.location, EarthLocation) 310 assert t2.location == t.location 311 t3 = Time(['2006-01-15 21:24:37.5'], format='iso', scale='utc', 312 precision=6, location=(location.x, location.y, location.z)) 313 assert isinstance(t3.location, EarthLocation) 314 assert t3.location == t.location 315 316 def test_location_array(self): 317 """Check that location arrays are checked for size and used 318 for the corresponding times. Also checks that erfa 319 can handle array-valued locations, and can broadcast these if needed. 320 """ 321 322 lat = 19.48125 323 lon = -155.933222 324 t = Time(['2006-01-15 21:24:37.5'] * 2, format='iso', scale='utc', 325 precision=6, location=(lon, lat)) 326 assert np.all(t.utc.iso == '2006-01-15 21:24:37.500000') 327 assert np.all(t.tdb.iso[0] == '2006-01-15 21:25:42.684373') 328 t2 = Time(['2006-01-15 21:24:37.5'] * 2, format='iso', scale='utc', 329 precision=6, location=(np.array([lon, 0]), 330 np.array([lat, 0]))) 331 assert np.all(t2.utc.iso == '2006-01-15 21:24:37.500000') 332 assert t2.tdb.iso[0] == '2006-01-15 21:25:42.684373' 333 assert t2.tdb.iso[1] != '2006-01-15 21:25:42.684373' 334 with pytest.raises(ValueError): # 1 time, but two locations 335 Time('2006-01-15 21:24:37.5', format='iso', scale='utc', 336 precision=6, location=(np.array([lon, 0]), 337 np.array([lat, 0]))) 338 with pytest.raises(ValueError): # 3 times, but two locations 339 Time(['2006-01-15 21:24:37.5'] * 3, format='iso', scale='utc', 340 precision=6, location=(np.array([lon, 0]), 341 np.array([lat, 0]))) 342 # multidimensional 343 mjd = np.arange(50000., 50008.).reshape(4, 2) 344 t3 = Time(mjd, format='mjd', scale='utc', location=(lon, lat)) 345 assert t3.shape == (4, 2) 346 assert t3.location.shape == () 347 assert t3.tdb.shape == t3.shape 348 t4 = Time(mjd, format='mjd', scale='utc', 349 location=(np.array([lon, 0]), np.array([lat, 0]))) 350 assert t4.shape == (4, 2) 351 assert t4.location.shape == t4.shape 352 assert t4.tdb.shape == t4.shape 353 t5 = Time(mjd, format='mjd', scale='utc', 354 location=(np.array([[lon], [0], [0], [0]]), 355 np.array([[lat], [0], [0], [0]]))) 356 assert t5.shape == (4, 2) 357 assert t5.location.shape == t5.shape 358 assert t5.tdb.shape == t5.shape 359 360 def test_all_scale_transforms(self): 361 """Test that standard scale transforms work. Does not test correctness, 362 except reversibility [#2074]. Also tests that standard scales can't be 363 converted to local scales""" 364 lat = 19.48125 365 lon = -155.933222 366 with iers.conf.set_temp('auto_download', False): 367 for scale1 in STANDARD_TIME_SCALES: 368 t1 = Time('2006-01-15 21:24:37.5', format='iso', scale=scale1, 369 location=(lon, lat)) 370 for scale2 in STANDARD_TIME_SCALES: 371 t2 = getattr(t1, scale2) 372 t21 = getattr(t2, scale1) 373 assert allclose_jd(t21.jd, t1.jd) 374 375 # test for conversion to local scale 376 scale3 = 'local' 377 with pytest.raises(ScaleValueError): 378 t2 = getattr(t1, scale3) 379 380 def test_creating_all_formats(self): 381 """Create a time object using each defined format""" 382 Time(2000.5, format='decimalyear') 383 Time(100.0, format='cxcsec') 384 Time(100.0, format='unix') 385 Time(100.0, format='gps') 386 Time(1950.0, format='byear', scale='tai') 387 Time(2000.0, format='jyear', scale='tai') 388 Time('B1950.0', format='byear_str', scale='tai') 389 Time('J2000.0', format='jyear_str', scale='tai') 390 Time('2000-01-01 12:23:34.0', format='iso', scale='tai') 391 Time('2000-01-01 12:23:34.0Z', format='iso', scale='utc') 392 Time('2000-01-01T12:23:34.0', format='isot', scale='tai') 393 Time('2000-01-01T12:23:34.0Z', format='isot', scale='utc') 394 Time('2000-01-01T12:23:34.0', format='fits') 395 Time('2000-01-01T12:23:34.0', format='fits', scale='tdb') 396 Time(2400000.5, 51544.0333981, format='jd', scale='tai') 397 Time(0.0, 51544.0333981, format='mjd', scale='tai') 398 Time('2000:001:12:23:34.0', format='yday', scale='tai') 399 Time('2000:001:12:23:34.0Z', format='yday', scale='utc') 400 dt = datetime.datetime(2000, 1, 2, 3, 4, 5, 123456) 401 Time(dt, format='datetime', scale='tai') 402 Time([dt, dt], format='datetime', scale='tai') 403 dt64 = np.datetime64('2012-06-18T02:00:05.453000000') 404 Time(dt64, format='datetime64', scale='tai') 405 Time([dt64, dt64], format='datetime64', scale='tai') 406 407 def test_local_format_transforms(self): 408 """ 409 Test transformation of local time to different formats 410 Transformation to formats with reference time should give 411 ScalevalueError 412 """ 413 t = Time('2006-01-15 21:24:37.5', scale='local') 414 assert_allclose(t.jd, 2453751.3921006946, atol=0.001 / 3600. / 24., rtol=0.) 415 assert_allclose(t.mjd, 53750.892100694444, atol=0.001 / 3600. / 24., rtol=0.) 416 assert_allclose(t.decimalyear, 2006.0408002758752, atol=0.001 / 3600. / 24. / 365., rtol=0.) 417 assert t.datetime == datetime.datetime(2006, 1, 15, 21, 24, 37, 500000) 418 assert t.isot == '2006-01-15T21:24:37.500' 419 assert t.yday == '2006:015:21:24:37.500' 420 assert t.fits == '2006-01-15T21:24:37.500' 421 assert_allclose(t.byear, 2006.04217888831, atol=0.001 / 3600. / 24. / 365., rtol=0.) 422 assert_allclose(t.jyear, 2006.0407723496082, atol=0.001 / 3600. / 24. / 365., rtol=0.) 423 assert t.byear_str == 'B2006.042' 424 assert t.jyear_str == 'J2006.041' 425 426 # epochTimeFormats 427 with pytest.raises(ScaleValueError): 428 t.gps 429 with pytest.raises(ScaleValueError): 430 t.unix 431 with pytest.raises(ScaleValueError): 432 t.cxcsec 433 with pytest.raises(ScaleValueError): 434 t.plot_date 435 436 def test_datetime(self): 437 """ 438 Test datetime format, including guessing the format from the input type 439 by not providing the format keyword to Time. 440 """ 441 dt = datetime.datetime(2000, 1, 2, 3, 4, 5, 123456) 442 dt2 = datetime.datetime(2001, 1, 1) 443 t = Time(dt, scale='utc', precision=9) 444 assert t.iso == '2000-01-02 03:04:05.123456000' 445 assert t.datetime == dt 446 assert t.value == dt 447 t2 = Time(t.iso, scale='utc') 448 assert t2.datetime == dt 449 450 t = Time([dt, dt2], scale='utc') 451 assert np.all(t.value == [dt, dt2]) 452 453 t = Time('2000-01-01 01:01:01.123456789', scale='tai') 454 assert t.datetime == datetime.datetime(2000, 1, 1, 1, 1, 1, 123457) 455 456 # broadcasting 457 dt3 = (dt + (dt2-dt) * np.arange(12)).reshape(4, 3) 458 t3 = Time(dt3, scale='utc') 459 assert t3.shape == (4, 3) 460 assert t3[2, 1].value == dt3[2, 1] 461 assert t3[2, 1] == Time(dt3[2, 1]) 462 assert np.all(t3.value == dt3) 463 assert np.all(t3[1].value == dt3[1]) 464 assert np.all(t3[:, 2] == Time(dt3[:, 2])) 465 assert Time(t3[2, 0]) == t3[2, 0] 466 467 def test_datetime64(self): 468 dt64 = np.datetime64('2000-01-02T03:04:05.123456789') 469 dt64_2 = np.datetime64('2000-01-02') 470 t = Time(dt64, scale='utc', precision=9, format='datetime64') 471 assert t.iso == '2000-01-02 03:04:05.123456789' 472 assert t.datetime64 == dt64 473 assert t.value == dt64 474 t2 = Time(t.iso, scale='utc') 475 assert t2.datetime64 == dt64 476 477 t = Time(dt64_2, scale='utc', precision=3, format='datetime64') 478 assert t.iso == '2000-01-02 00:00:00.000' 479 assert t.datetime64 == dt64_2 480 assert t.value == dt64_2 481 t2 = Time(t.iso, scale='utc') 482 assert t2.datetime64 == dt64_2 483 484 t = Time([dt64, dt64_2], scale='utc', format='datetime64') 485 assert np.all(t.value == [dt64, dt64_2]) 486 487 t = Time('2000-01-01 01:01:01.123456789', scale='tai') 488 assert t.datetime64 == np.datetime64('2000-01-01T01:01:01.123456789') 489 490 # broadcasting 491 dt3 = (dt64 + (dt64_2-dt64) * np.arange(12)).reshape(4, 3) 492 t3 = Time(dt3, scale='utc', format='datetime64') 493 assert t3.shape == (4, 3) 494 assert t3[2, 1].value == dt3[2, 1] 495 assert t3[2, 1] == Time(dt3[2, 1], format='datetime64') 496 assert np.all(t3.value == dt3) 497 assert np.all(t3[1].value == dt3[1]) 498 assert np.all(t3[:, 2] == Time(dt3[:, 2], format='datetime64')) 499 assert Time(t3[2, 0], format='datetime64') == t3[2, 0] 500 501 def test_epoch_transform(self): 502 """Besselian and julian epoch transforms""" 503 jd = 2457073.05631 504 t = Time(jd, format='jd', scale='tai', precision=6) 505 assert allclose_year(t.byear, 2015.1365941020817) 506 assert allclose_year(t.jyear, 2015.1349933196439) 507 assert t.byear_str == 'B2015.136594' 508 assert t.jyear_str == 'J2015.134993' 509 t2 = Time(t.byear, format='byear', scale='tai') 510 assert allclose_jd(t2.jd, jd) 511 t2 = Time(t.jyear, format='jyear', scale='tai') 512 assert allclose_jd(t2.jd, jd) 513 514 t = Time('J2015.134993', scale='tai', precision=6) 515 assert np.allclose(t.jd, jd, rtol=1e-10, atol=0) # J2015.134993 has 10 digit precision 516 assert t.byear_str == 'B2015.136594' 517 518 def test_input_validation(self): 519 """Wrong input type raises error""" 520 times = [10, 20] 521 with pytest.raises(ValueError): 522 Time(times, format='iso', scale='utc') 523 with pytest.raises(ValueError): 524 Time('2000:001', format='jd', scale='utc') 525 with pytest.raises(ValueError): # unguessable 526 Time([]) 527 with pytest.raises(ValueError): 528 Time([50000.0], ['bad'], format='mjd', scale='tai') 529 with pytest.raises(ValueError): 530 Time(50000.0, 'bad', format='mjd', scale='tai') 531 with pytest.raises(ValueError): 532 Time('2005-08-04T00:01:02.000Z', scale='tai') 533 # regression test against #3396 534 with pytest.raises(ValueError): 535 Time(np.nan, format='jd', scale='utc') 536 with pytest.raises(ValueError): 537 with pytest.warns(AstropyDeprecationWarning): 538 Time('2000-01-02T03:04:05(TAI)', scale='utc') 539 with pytest.raises(ValueError): 540 Time('2000-01-02T03:04:05(TAI') 541 with pytest.raises(ValueError): 542 Time('2000-01-02T03:04:05(UT(NIST)') 543 544 def test_utc_leap_sec(self): 545 """Time behaves properly near or in UTC leap second. This 546 uses the 2012-06-30 leap second for testing.""" 547 for year, month, day in ((2012, 6, 30), (2016, 12, 31)): 548 # Start with a day without a leap second and note rollover 549 yyyy_mm = f'{year:04d}-{month:02d}' 550 yyyy_mm_dd = f'{year:04d}-{month:02d}-{day:02d}' 551 with pytest.warns(ErfaWarning): 552 t1 = Time(yyyy_mm + '-01 23:59:60.0', scale='utc') 553 assert t1.iso == yyyy_mm + '-02 00:00:00.000' 554 555 # Leap second is different 556 t1 = Time(yyyy_mm_dd + ' 23:59:59.900', scale='utc') 557 assert t1.iso == yyyy_mm_dd + ' 23:59:59.900' 558 559 t1 = Time(yyyy_mm_dd + ' 23:59:60.000', scale='utc') 560 assert t1.iso == yyyy_mm_dd + ' 23:59:60.000' 561 562 t1 = Time(yyyy_mm_dd + ' 23:59:60.999', scale='utc') 563 assert t1.iso == yyyy_mm_dd + ' 23:59:60.999' 564 565 if month == 6: 566 yyyy_mm_dd_plus1 = f'{year:04d}-07-01' 567 else: 568 yyyy_mm_dd_plus1 = f'{year + 1:04d}-01-01' 569 570 with pytest.warns(ErfaWarning): 571 t1 = Time(yyyy_mm_dd + ' 23:59:61.0', scale='utc') 572 assert t1.iso == yyyy_mm_dd_plus1 + ' 00:00:00.000' 573 574 # Delta time gives 2 seconds here as expected 575 t0 = Time(yyyy_mm_dd + ' 23:59:59', scale='utc') 576 t1 = Time(yyyy_mm_dd_plus1 + ' 00:00:00', scale='utc') 577 assert allclose_sec((t1 - t0).sec, 2.0) 578 579 def test_init_from_time_objects(self): 580 """Initialize from one or more Time objects""" 581 t1 = Time('2007:001', scale='tai') 582 t2 = Time(['2007-01-02', '2007-01-03'], scale='utc') 583 # Init from a list of Time objects without an explicit scale 584 t3 = Time([t1, t2]) 585 # Test that init appropriately combines a scalar (t1) and list (t2) 586 # and that scale and format are same as first element. 587 assert len(t3) == 3 588 assert t3.scale == t1.scale 589 assert t3.format == t1.format # t1 format is yday 590 assert np.all(t3.value == np.concatenate([[t1.yday], t2.tai.yday])) 591 592 # Init from a single Time object without a scale 593 t3 = Time(t1) 594 assert t3.isscalar 595 assert t3.scale == t1.scale 596 assert t3.format == t1.format 597 assert np.all(t3.value == t1.value) 598 599 # Init from a single Time object with scale specified 600 t3 = Time(t1, scale='utc') 601 assert t3.scale == 'utc' 602 assert np.all(t3.value == t1.utc.value) 603 604 # Init from a list of Time object with scale specified 605 t3 = Time([t1, t2], scale='tt') 606 assert t3.scale == 'tt' 607 assert t3.format == t1.format # yday 608 assert np.all(t3.value == np.concatenate([[t1.tt.yday], t2.tt.yday])) 609 610 # OK, how likely is this... but might as well test. 611 mjd = np.arange(50000., 50006.) 612 frac = np.arange(0., 0.999, 0.2) 613 t4 = Time(mjd[:, np.newaxis] + frac, format='mjd', scale='utc') 614 t5 = Time([t4[:2], t4[4:5]]) 615 assert t5.shape == (3, 5) 616 617 # throw error when deriving local scale time 618 # from non local time scale 619 with pytest.raises(ValueError): 620 Time(t1, scale='local') 621 622 623class TestVal2: 624 """Tests related to val2""" 625 626 @pytest.mark.parametrize("d", [ 627 dict(val="2001:001", val2="ignored", scale="utc"), 628 dict(val={'year': 2015, 'month': 2, 'day': 3, 629 'hour': 12, 'minute': 13, 'second': 14.567}, 630 val2="ignored", scale="utc"), 631 dict(val=np.datetime64('2005-02-25'), val2="ignored", scale="utc"), 632 dict(val=datetime.datetime(2000, 1, 2, 12, 0, 0), 633 val2="ignored", scale="utc"), 634 ]) 635 def test_unused_val2_raises(self, d): 636 """Test that providing val2 is for string input lets user know we won't use it""" 637 with pytest.raises(ValueError): 638 Time(**d) 639 640 def test_val2(self): 641 """Various tests of the val2 input""" 642 t = Time([0.0, 50000.0], [50000.0, 0.0], format='mjd', scale='tai') 643 assert t.mjd[0] == t.mjd[1] 644 assert t.jd[0] == t.jd[1] 645 646 def test_val_broadcasts_against_val2(self): 647 mjd = np.arange(50000., 50007.) 648 frac = np.arange(0., 0.999, 0.2) 649 t = Time(mjd[:, np.newaxis], frac, format='mjd', scale='utc') 650 assert t.shape == (7, 5) 651 with pytest.raises(ValueError): 652 Time([0.0, 50000.0], [0.0, 1.0, 2.0], format='mjd', scale='tai') 653 654 def test_broadcast_not_writable(self): 655 val = (2458000 + np.arange(3))[:, None] 656 val2 = np.linspace(0, 1, 4, endpoint=False) 657 t = Time(val=val, val2=val2, format="jd", scale="tai") 658 t_b = Time(val=val + 0 * val2, val2=0 * val + val2, format="jd", scale="tai") 659 t_i = Time(val=57990, val2=0.3, format="jd", scale="tai") 660 t_b[1, 2] = t_i 661 t[1, 2] = t_i 662 assert t_b[1, 2] == t[1, 2], "writing worked" 663 assert t_b[0, 2] == t[0, 2], "broadcasting didn't cause problems" 664 assert t_b[1, 1] == t[1, 1], "broadcasting didn't cause problems" 665 assert np.all(t_b == t), "behaved as expected" 666 667 def test_broadcast_one_not_writable(self): 668 val = (2458000 + np.arange(3)) 669 val2 = np.arange(1) 670 t = Time(val=val, val2=val2, format="jd", scale="tai") 671 t_b = Time(val=val + 0 * val2, val2=0 * val + val2, format="jd", scale="tai") 672 t_i = Time(val=57990, val2=0.3, format="jd", scale="tai") 673 t_b[1] = t_i 674 t[1] = t_i 675 assert t_b[1] == t[1], "writing worked" 676 assert t_b[0] == t[0], "broadcasting didn't cause problems" 677 assert np.all(t_b == t), "behaved as expected" 678 679 680class TestSubFormat: 681 """Test input and output subformat functionality""" 682 683 def test_input_subformat(self): 684 """Input subformat selection""" 685 # Heterogeneous input formats with in_subfmt='*' (default) 686 times = ['2000-01-01', '2000-01-01 01:01', 687 '2000-01-01 01:01:01', '2000-01-01 01:01:01.123'] 688 t = Time(times, format='iso', scale='tai') 689 assert np.all(t.iso == np.array(['2000-01-01 00:00:00.000', 690 '2000-01-01 01:01:00.000', 691 '2000-01-01 01:01:01.000', 692 '2000-01-01 01:01:01.123'])) 693 694 # Heterogeneous input formats with in_subfmt='date_*' 695 times = ['2000-01-01 01:01', 696 '2000-01-01 01:01:01', '2000-01-01 01:01:01.123'] 697 t = Time(times, format='iso', scale='tai', 698 in_subfmt='date_*') 699 assert np.all(t.iso == np.array(['2000-01-01 01:01:00.000', 700 '2000-01-01 01:01:01.000', 701 '2000-01-01 01:01:01.123'])) 702 703 def test_input_subformat_fail(self): 704 """Failed format matching""" 705 with pytest.raises(ValueError): 706 Time('2000-01-01 01:01', format='iso', scale='tai', 707 in_subfmt='date') 708 709 def test_bad_input_subformat(self): 710 """Non-existent input subformat""" 711 with pytest.raises(ValueError): 712 Time('2000-01-01 01:01', format='iso', scale='tai', 713 in_subfmt='doesnt exist') 714 715 def test_output_subformat(self): 716 """Input subformat selection""" 717 # Heterogeneous input formats with in_subfmt='*' (default) 718 times = ['2000-01-01', '2000-01-01 01:01', 719 '2000-01-01 01:01:01', '2000-01-01 01:01:01.123'] 720 t = Time(times, format='iso', scale='tai', 721 out_subfmt='date_hm') 722 assert np.all(t.iso == np.array(['2000-01-01 00:00', 723 '2000-01-01 01:01', 724 '2000-01-01 01:01', 725 '2000-01-01 01:01'])) 726 727 def test_fits_format(self): 728 """FITS format includes bigger years.""" 729 # Heterogeneous input formats with in_subfmt='*' (default) 730 times = ['2000-01-01', '2000-01-01T01:01:01', '2000-01-01T01:01:01.123'] 731 t = Time(times, format='fits', scale='tai') 732 assert np.all(t.fits == np.array(['2000-01-01T00:00:00.000', 733 '2000-01-01T01:01:01.000', 734 '2000-01-01T01:01:01.123'])) 735 # Explicit long format for output, default scale is UTC. 736 t2 = Time(times, format='fits', out_subfmt='long*') 737 assert np.all(t2.fits == np.array(['+02000-01-01T00:00:00.000', 738 '+02000-01-01T01:01:01.000', 739 '+02000-01-01T01:01:01.123'])) 740 # Implicit long format for output, because of negative year. 741 times[2] = '-00594-01-01' 742 t3 = Time(times, format='fits', scale='tai') 743 assert np.all(t3.fits == np.array(['+02000-01-01T00:00:00.000', 744 '+02000-01-01T01:01:01.000', 745 '-00594-01-01T00:00:00.000'])) 746 # Implicit long format for output, because of large positive year. 747 times[2] = '+10594-01-01' 748 t4 = Time(times, format='fits', scale='tai') 749 assert np.all(t4.fits == np.array(['+02000-01-01T00:00:00.000', 750 '+02000-01-01T01:01:01.000', 751 '+10594-01-01T00:00:00.000'])) 752 753 def test_yday_format(self): 754 """Year:Day_of_year format""" 755 # Heterogeneous input formats with in_subfmt='*' (default) 756 times = ['2000-12-01', '2001-12-01 01:01:01.123'] 757 t = Time(times, format='iso', scale='tai') 758 t.out_subfmt = 'date_hm' 759 assert np.all(t.yday == np.array(['2000:336:00:00', 760 '2001:335:01:01'])) 761 t.out_subfmt = '*' 762 assert np.all(t.yday == np.array(['2000:336:00:00:00.000', 763 '2001:335:01:01:01.123'])) 764 765 def test_scale_input(self): 766 """Test for issues related to scale input""" 767 # Check case where required scale is defined by the TimeFormat. 768 # All three should work. 769 t = Time(100.0, format='cxcsec', scale='utc') 770 assert t.scale == 'utc' 771 t = Time(100.0, format='unix', scale='tai') 772 assert t.scale == 'tai' 773 t = Time(100.0, format='gps', scale='utc') 774 assert t.scale == 'utc' 775 776 # Check that bad scale is caught when format is specified 777 with pytest.raises(ScaleValueError): 778 Time(1950.0, format='byear', scale='bad scale') 779 780 # Check that bad scale is caught when format is auto-determined 781 with pytest.raises(ScaleValueError): 782 Time('2000:001:00:00:00', scale='bad scale') 783 784 def test_fits_scale(self): 785 """Test that the previous FITS-string formatting can still be handled 786 but with a DeprecationWarning.""" 787 for inputs in (("2000-01-02(TAI)", "tai"), 788 ("1999-01-01T00:00:00.123(ET(NIST))", "tt"), 789 ("2014-12-12T01:00:44.1(UTC)", "utc")): 790 with pytest.warns(AstropyDeprecationWarning): 791 t = Time(inputs[0]) 792 assert t.scale == inputs[1] 793 794 # Create Time using normal ISOT syntax and compare with FITS 795 t2 = Time(inputs[0][:inputs[0].index("(")], format="isot", 796 scale=inputs[1]) 797 assert t == t2 798 799 # Explicit check that conversions still work despite warning 800 with pytest.warns(AstropyDeprecationWarning): 801 t = Time('1999-01-01T00:00:00.123456789(UTC)') 802 t = t.tai 803 assert t.isot == '1999-01-01T00:00:32.123' 804 805 with pytest.warns(AstropyDeprecationWarning): 806 t = Time('1999-01-01T00:00:32.123456789(TAI)') 807 t = t.utc 808 assert t.isot == '1999-01-01T00:00:00.123' 809 810 # Check scale consistency 811 with pytest.warns(AstropyDeprecationWarning): 812 t = Time('1999-01-01T00:00:32.123456789(TAI)', scale="tai") 813 assert t.scale == "tai" 814 with pytest.warns(AstropyDeprecationWarning): 815 t = Time('1999-01-01T00:00:32.123456789(ET)', scale="tt") 816 assert t.scale == "tt" 817 with pytest.raises(ValueError), pytest.warns(AstropyDeprecationWarning): 818 t = Time('1999-01-01T00:00:32.123456789(TAI)', scale="utc") 819 820 def test_scale_default(self): 821 """Test behavior when no scale is provided""" 822 # These first three are TimeFromEpoch and have an intrinsic time scale 823 t = Time(100.0, format='cxcsec') 824 assert t.scale == 'tt' 825 t = Time(100.0, format='unix') 826 assert t.scale == 'utc' 827 t = Time(100.0, format='gps') 828 assert t.scale == 'tai' 829 830 for date in ('2000:001', '2000-01-01T00:00:00'): 831 t = Time(date) 832 assert t.scale == 'utc' 833 834 t = Time(2000.1, format='byear') 835 assert t.scale == 'tt' 836 t = Time('J2000') 837 assert t.scale == 'tt' 838 839 def test_epoch_times(self): 840 """Test time formats derived from EpochFromTime""" 841 t = Time(0.0, format='cxcsec', scale='tai') 842 assert t.tt.iso == '1998-01-01 00:00:00.000' 843 844 # Create new time object from this one and change scale, format 845 t2 = Time(t, scale='tt', format='iso') 846 assert t2.value == '1998-01-01 00:00:00.000' 847 848 # Value take from Chandra.Time.DateTime('2010:001:00:00:00').secs 849 t_cxcsec = 378691266.184 850 t = Time(t_cxcsec, format='cxcsec', scale='utc') 851 assert allclose_sec(t.value, t_cxcsec) 852 assert allclose_sec(t.cxcsec, t_cxcsec) 853 assert allclose_sec(t.tt.value, t_cxcsec) 854 assert allclose_sec(t.tt.cxcsec, t_cxcsec) 855 assert t.yday == '2010:001:00:00:00.000' 856 t = Time('2010:001:00:00:00.000', scale='utc') 857 assert allclose_sec(t.cxcsec, t_cxcsec) 858 assert allclose_sec(t.tt.cxcsec, t_cxcsec) 859 860 # Round trip through epoch time 861 for scale in ('utc', 'tt'): 862 t = Time('2000:001', scale=scale) 863 t2 = Time(t.unix, scale=scale, format='unix') 864 assert getattr(t2, scale).iso == '2000-01-01 00:00:00.000' 865 866 # Test unix time. Values taken from http://en.wikipedia.org/wiki/Unix_time 867 t = Time('2013-05-20 21:18:46', scale='utc') 868 assert allclose_sec(t.unix, 1369084726.0) 869 assert allclose_sec(t.tt.unix, 1369084726.0) 870 871 # Values from issue #1118 872 t = Time('2004-09-16T23:59:59', scale='utc') 873 assert allclose_sec(t.unix, 1095379199.0) 874 875 def test_plot_date(self): 876 """Test the plot_date format. 877 878 Depending on the situation with matplotlib, this can give different 879 results because the plot date epoch time changed in matplotlib 3.3. This 880 test tries to use the matplotlib date2num function to make the test 881 independent of version, but if matplotlib isn't available then the code 882 (and test) use the pre-3.3 epoch. 883 """ 884 try: 885 from matplotlib.dates import date2num 886 except ImportError: 887 # No matplotlib, in which case this uses the epoch 0000-12-31 888 # as per matplotlib < 3.3. 889 # Value from: 890 # matplotlib.dates.set_epoch('0000-12-31') 891 # val = matplotlib.dates.date2num('2000-01-01') 892 val = 730120.0 893 else: 894 val = date2num(datetime.datetime(2000, 1, 1)) 895 t = Time('2000-01-01 00:00:00', scale='utc') 896 assert np.allclose(t.plot_date, val, atol=1e-5, rtol=0) 897 898 899class TestNumericalSubFormat: 900 def test_explicit_example(self): 901 t = Time('54321.000000000001', format='mjd') 902 assert t == Time(54321, 1e-12, format='mjd') 903 assert t.mjd == 54321. # Lost precision! 904 assert t.value == 54321. # Lost precision! 905 assert t.to_value('mjd') == 54321. # Lost precision! 906 assert t.to_value('mjd', subfmt='str') == '54321.000000000001' 907 assert t.to_value('mjd', 'bytes') == b'54321.000000000001' 908 expected_long = np.longdouble(54321.) + np.longdouble(1e-12) 909 # Check we're the same to within the double holding jd2 910 # (which is less precise than longdouble on arm64). 911 assert np.allclose(t.to_value('mjd', subfmt='long'), 912 expected_long, rtol=0, atol=np.finfo(float).eps) 913 t.out_subfmt = 'str' 914 assert t.value == '54321.000000000001' 915 assert t.to_value('mjd') == 54321. # Lost precision! 916 assert t.mjd == '54321.000000000001' 917 assert t.to_value('mjd', subfmt='bytes') == b'54321.000000000001' 918 assert t.to_value('mjd', subfmt='float') == 54321. # Lost precision! 919 t.out_subfmt = 'long' 920 assert np.allclose(t.value, expected_long, 921 rtol=0., atol=np.finfo(float).eps) 922 assert np.allclose(t.to_value('mjd', subfmt=None), expected_long, 923 rtol=0., atol=np.finfo(float).eps) 924 assert np.allclose(t.mjd, expected_long, 925 rtol=0., atol=np.finfo(float).eps) 926 assert t.to_value('mjd', subfmt='str') == '54321.000000000001' 927 assert t.to_value('mjd', subfmt='float') == 54321. # Lost precision! 928 929 @pytest.mark.skipif(np.finfo(np.longdouble).eps >= np.finfo(float).eps, 930 reason="long double is the same as float") 931 def test_explicit_longdouble(self): 932 i = 54321 933 # Create a different long double (which will give a different jd2 934 # even when long doubles are more precise than Time, as on arm64). 935 f = max(2.**(-np.finfo(np.longdouble).nmant) * 65536, 936 np.finfo(float).eps) 937 mjd_long = np.longdouble(i) + np.longdouble(f) 938 assert mjd_long != i, "longdouble failure!" 939 t = Time(mjd_long, format='mjd') 940 expected = Time(i, f, format='mjd') 941 assert abs(t - expected) <= 20. * u.ps 942 t_float = Time(i + f, format='mjd') 943 assert t_float == Time(i, format='mjd') 944 assert t_float != t 945 assert t.value == 54321. # Lost precision! 946 assert np.allclose(t.to_value('mjd', subfmt='long'), mjd_long, 947 rtol=0., atol=np.finfo(float).eps) 948 t2 = Time(mjd_long, format='mjd', out_subfmt='long') 949 assert np.allclose(t2.value, mjd_long, 950 rtol=0., atol=np.finfo(float).eps) 951 952 @pytest.mark.skipif(np.finfo(np.longdouble).eps >= np.finfo(float).eps, 953 reason="long double is the same as float") 954 def test_explicit_longdouble_one_val(self): 955 """Ensure either val1 or val2 being longdouble is possible. 956 957 Regression test for issue gh-10033. 958 """ 959 i = 54321 960 f = max(2.**(-np.finfo(np.longdouble).nmant) * 65536, 961 np.finfo(float).eps) 962 t1 = Time(i, f, format='mjd') 963 t2 = Time(np.longdouble(i), f, format='mjd') 964 t3 = Time(i, np.longdouble(f), format='mjd') 965 t4 = Time(np.longdouble(i), np.longdouble(f), format='mjd') 966 assert t1 == t2 == t3 == t4 967 968 @pytest.mark.skipif(np.finfo(np.longdouble).eps >= np.finfo(float).eps, 969 reason="long double is the same as float") 970 @pytest.mark.parametrize("fmt", ["mjd", "unix", "cxcsec"]) 971 def test_longdouble_for_other_types(self, fmt): 972 t_fmt = getattr(Time(58000, format="mjd"), fmt) # Get regular float 973 t_fmt_long = np.longdouble(t_fmt) 974 # Create a different long double (ensuring it will give a different jd2 975 # even when long doubles are more precise than Time, as on arm64). 976 atol = np.finfo(float).eps * (1. if fmt == 'mjd' else 24. * 3600.) 977 t_fmt_long2 = t_fmt_long + max( 978 t_fmt_long * np.finfo(np.longdouble).eps * 2, atol) 979 assert t_fmt_long != t_fmt_long2, "longdouble weird!" 980 tm = Time(t_fmt_long, format=fmt) 981 tm2 = Time(t_fmt_long2, format=fmt) 982 assert tm != tm2 983 tm_long2 = tm2.to_value(fmt, subfmt='long') 984 assert np.allclose(tm_long2, t_fmt_long2, rtol=0., atol=atol) 985 986 def test_subformat_input(self): 987 s = '54321.01234567890123456789' 988 i, f = s.split('.') # Note, OK only for fraction < 0.5 989 t = Time(float(i), float('.' + f), format='mjd') 990 t_str = Time(s, format='mjd') 991 t_bytes = Time(s.encode('ascii'), format='mjd') 992 t_decimal = Time(Decimal(s), format='mjd') 993 assert t_str == t 994 assert t_bytes == t 995 assert t_decimal == t 996 997 @pytest.mark.parametrize('out_subfmt', ('str', 'bytes')) 998 def test_subformat_output(self, out_subfmt): 999 i = 54321 1000 f = np.array([0., 1e-9, 1e-12]) 1001 t = Time(i, f, format='mjd', out_subfmt=out_subfmt) 1002 t_value = t.value 1003 expected = np.array(['54321.0', 1004 '54321.000000001', 1005 '54321.000000000001'], dtype=out_subfmt) 1006 assert np.all(t_value == expected) 1007 assert np.all(Time(expected, format='mjd') == t) 1008 1009 # Explicit sub-format. 1010 t = Time(i, f, format='mjd') 1011 t_mjd_subfmt = t.to_value('mjd', subfmt=out_subfmt) 1012 assert np.all(t_mjd_subfmt == expected) 1013 1014 @pytest.mark.parametrize('fmt,string,val1,val2', [ 1015 ('jd', '2451544.5333981', 2451544.5, .0333981), 1016 ('decimalyear', '2000.54321', 2000., .54321), 1017 ('cxcsec', '100.0123456', 100.0123456, None), 1018 ('unix', '100.0123456', 100.0123456, None), 1019 ('gps', '100.0123456', 100.0123456, None), 1020 ('byear', '1950.1', 1950.1, None), 1021 ('jyear', '2000.1', 2000.1, None)]) 1022 def test_explicit_string_other_formats(self, fmt, string, val1, val2): 1023 t = Time(string, format=fmt) 1024 assert t == Time(val1, val2, format=fmt) 1025 assert t.to_value(fmt, subfmt='str') == string 1026 1027 def test_basic_subformat_setting(self): 1028 t = Time('2001', format='jyear', scale='tai') 1029 t.format = "mjd" 1030 t.out_subfmt = "str" 1031 assert t.value.startswith("5") 1032 1033 def test_basic_subformat_cache_does_not_crash(self): 1034 t = Time('2001', format='jyear', scale='tai') 1035 t.to_value('mjd', subfmt='str') 1036 assert ('mjd', 'str') in t.cache['format'] 1037 t.to_value('mjd', 'str') 1038 1039 @pytest.mark.parametrize("fmt", ["jd", "mjd", "cxcsec", "unix", "gps", "jyear"]) 1040 def test_decimal_context_does_not_affect_string(self, fmt): 1041 t = Time('2001', format='jyear', scale='tai') 1042 t.format = fmt 1043 with localcontext() as ctx: 1044 ctx.prec = 2 1045 t_s_2 = t.to_value(fmt, "str") 1046 t2 = Time('2001', format='jyear', scale='tai') 1047 t2.format = fmt 1048 with localcontext() as ctx: 1049 ctx.prec = 40 1050 t2_s_40 = t.to_value(fmt, "str") 1051 assert t_s_2 == t2_s_40, "String representation should not depend on Decimal context" 1052 1053 def test_decimal_context_caching(self): 1054 t = Time(val=58000, val2=1e-14, format='mjd', scale='tai') 1055 with localcontext() as ctx: 1056 ctx.prec = 2 1057 t_s_2 = t.to_value('mjd', subfmt='decimal') 1058 t2 = Time(val=58000, val2=1e-14, format='mjd', scale='tai') 1059 with localcontext() as ctx: 1060 ctx.prec = 40 1061 t_s_40 = t.to_value('mjd', subfmt='decimal') 1062 t2_s_40 = t2.to_value('mjd', subfmt='decimal') 1063 assert t_s_2 == t_s_40, "Should be the same but cache might make this automatic" 1064 assert t_s_2 == t2_s_40, "Different precision should produce the same results" 1065 1066 @pytest.mark.parametrize("f, s, t", [("sec", "long", np.longdouble), 1067 ("sec", "decimal", Decimal), 1068 ("sec", "str", str)]) 1069 def test_timedelta_basic(self, f, s, t): 1070 dt = (Time("58000", format="mjd", scale="tai") 1071 - Time("58001", format="mjd", scale="tai")) 1072 1073 value = dt.to_value(f, s) 1074 assert isinstance(value, t) 1075 dt.format = f 1076 dt.out_subfmt = s 1077 assert isinstance(dt.value, t) 1078 assert isinstance(dt.to_value(f, None), t) 1079 1080 def test_need_format_argument(self): 1081 t = Time('J2000') 1082 with pytest.raises(TypeError, match="missing.*required.*'format'"): 1083 t.to_value() 1084 with pytest.raises(ValueError, match='format must be one of'): 1085 t.to_value('julian') 1086 1087 def test_wrong_in_subfmt(self): 1088 with pytest.raises(ValueError, match='not among selected'): 1089 Time("58000", format='mjd', in_subfmt='float') 1090 1091 with pytest.raises(ValueError, match='not among selected'): 1092 Time(np.longdouble(58000), format='mjd', in_subfmt='float') 1093 1094 with pytest.raises(ValueError, match='not among selected'): 1095 Time(58000., format='mjd', in_subfmt='str') 1096 1097 with pytest.raises(ValueError, match='not among selected'): 1098 Time(58000., format='mjd', in_subfmt='long') 1099 1100 def test_wrong_subfmt(self): 1101 t = Time(58000., format='mjd') 1102 with pytest.raises(ValueError, match='must match one'): 1103 t.to_value('mjd', subfmt='parrot') 1104 1105 with pytest.raises(ValueError, match='must match one'): 1106 t.out_subfmt = 'parrot' 1107 1108 with pytest.raises(ValueError, match='must match one'): 1109 t.in_subfmt = 'parrot' 1110 1111 def test_not_allowed_subfmt(self): 1112 """Test case where format has no defined subfmts""" 1113 t = Time('J2000') 1114 match = 'subformat not allowed for format jyear_str' 1115 with pytest.raises(ValueError, match=match): 1116 t.to_value('jyear_str', subfmt='parrot') 1117 1118 with pytest.raises(ValueError, match=match): 1119 t.out_subfmt = 'parrot' 1120 1121 with pytest.raises(ValueError, match=match): 1122 Time('J2000', out_subfmt='parrot') 1123 1124 with pytest.raises(ValueError, match=match): 1125 t.in_subfmt = 'parrot' 1126 1127 with pytest.raises(ValueError, match=match): 1128 Time('J2000', format='jyear_str', in_subfmt='parrot') 1129 1130 def test_switch_to_format_with_no_out_subfmt(self): 1131 t = Time('2001-01-01', out_subfmt='date_hm') 1132 assert t.out_subfmt == 'date_hm' 1133 1134 # Now do an in-place switch to format 'jyear_str' that has no subfmts 1135 # where out_subfmt is changed to '*'. 1136 t.format = 'jyear_str' 1137 assert t.out_subfmt == '*' 1138 assert t.value == 'J2001.001' 1139 1140 1141class TestSofaErrors: 1142 """Test that erfa status return values are handled correctly""" 1143 1144 def test_bad_time(self): 1145 iy = np.array([2000], dtype=np.intc) 1146 im = np.array([2000], dtype=np.intc) # bad month 1147 id = np.array([2000], dtype=np.intc) # bad day 1148 with pytest.raises(ValueError): # bad month, fatal error 1149 djm0, djm = erfa.cal2jd(iy, im, id) 1150 1151 iy[0] = -5000 1152 im[0] = 2 1153 with pytest.raises(ValueError): # bad year, fatal error 1154 djm0, djm = erfa.cal2jd(iy, im, id) 1155 1156 iy[0] = 2000 1157 with pytest.warns(ErfaWarning, match=r'bad day \(JD computed\)') as w: 1158 djm0, djm = erfa.cal2jd(iy, im, id) 1159 assert len(w) == 1 1160 1161 assert allclose_jd(djm0, [2400000.5]) 1162 assert allclose_jd(djm, [53574.]) 1163 1164 1165class TestCopyReplicate: 1166 """Test issues related to copying and replicating data""" 1167 1168 def test_immutable_input(self): 1169 """Internals are never mutable.""" 1170 jds = np.array([2450000.5], dtype=np.double) 1171 t = Time(jds, format='jd', scale='tai') 1172 assert allclose_jd(t.jd, jds) 1173 jds[0] = 2458654 1174 assert not allclose_jd(t.jd, jds) 1175 1176 mjds = np.array([50000.0], dtype=np.double) 1177 t = Time(mjds, format='mjd', scale='tai') 1178 assert allclose_jd(t.jd, [2450000.5]) 1179 mjds[0] = 0.0 1180 assert allclose_jd(t.jd, [2450000.5]) 1181 1182 def test_replicate(self): 1183 """Test replicate method""" 1184 t = Time(['2000:001'], format='yday', scale='tai', 1185 location=('45d', '45d')) 1186 t_yday = t.yday 1187 t_loc_x = t.location.x.copy() 1188 t2 = t.replicate() 1189 assert t.yday == t2.yday 1190 assert t.format == t2.format 1191 assert t.scale == t2.scale 1192 assert t.location == t2.location 1193 # This is not allowed publicly, but here we hack the internal time 1194 # and location values to show that t and t2 are sharing references. 1195 t2._time.jd1 += 100.0 1196 1197 # Need to delete the cached yday attributes (only an issue because 1198 # of the internal _time hack). 1199 del t.cache 1200 del t2.cache 1201 1202 assert t.yday == t2.yday 1203 assert t.yday != t_yday # prove that it changed 1204 t2_loc_x_view = t2.location.x 1205 t2_loc_x_view[()] = 0 # use 0 to avoid having to give units 1206 assert t2.location.x == t2_loc_x_view 1207 assert t.location.x == t2.location.x 1208 assert t.location.x != t_loc_x # prove that it changed 1209 1210 def test_copy(self): 1211 """Test copy method""" 1212 t = Time('2000:001', format='yday', scale='tai', 1213 location=('45d', '45d')) 1214 t_yday = t.yday 1215 t_loc_x = t.location.x.copy() 1216 t2 = t.copy() 1217 assert t.yday == t2.yday 1218 # This is not allowed publicly, but here we hack the internal time 1219 # and location values to show that t and t2 are not sharing references. 1220 t2._time.jd1 += 100.0 1221 1222 # Need to delete the cached yday attributes (only an issue because 1223 # of the internal _time hack). 1224 del t.cache 1225 del t2.cache 1226 1227 assert t.yday != t2.yday 1228 assert t.yday == t_yday # prove that it did not change 1229 t2_loc_x_view = t2.location.x 1230 t2_loc_x_view[()] = 0 # use 0 to avoid having to give units 1231 assert t2.location.x == t2_loc_x_view 1232 assert t.location.x != t2.location.x 1233 assert t.location.x == t_loc_x # prove that it changed 1234 1235 1236class TestStardate: 1237 """Sync chronometers with Starfleet Command""" 1238 1239 def test_iso_to_stardate(self): 1240 assert str(Time('2320-01-01', scale='tai').stardate)[:7] == '1368.99' 1241 assert str(Time('2330-01-01', scale='tai').stardate)[:8] == '10552.76' 1242 assert str(Time('2340-01-01', scale='tai').stardate)[:8] == '19734.02' 1243 1244 @pytest.mark.parametrize('dates', 1245 [(10000, '2329-05-26 03:02'), 1246 (20000, '2340-04-15 19:05'), 1247 (30000, '2351-03-07 11:08')]) 1248 def test_stardate_to_iso(self, dates): 1249 stardate, iso = dates 1250 t_star = Time(stardate, format='stardate') 1251 t_iso = Time(t_star, format='iso', out_subfmt='date_hm') 1252 assert t_iso.value == iso 1253 1254 1255def test_python_builtin_copy(): 1256 t = Time('2000:001', format='yday', scale='tai') 1257 t2 = copy.copy(t) 1258 t3 = copy.deepcopy(t) 1259 1260 assert t.jd == t2.jd 1261 assert t.jd == t3.jd 1262 1263 1264def test_now(): 1265 """ 1266 Tests creating a Time object with the `now` class method. 1267 """ 1268 1269 now = datetime.datetime.utcnow() 1270 t = Time.now() 1271 1272 assert t.format == 'datetime' 1273 assert t.scale == 'utc' 1274 1275 dt = t.datetime - now # a datetime.timedelta object 1276 1277 # this gives a .1 second margin between the `utcnow` call and the `Time` 1278 # initializer, which is really way more generous than necessary - typical 1279 # times are more like microseconds. But it seems safer in case some 1280 # platforms have slow clock calls or something. 1281 1282 assert dt.total_seconds() < 0.1 1283 1284 1285def test_decimalyear(): 1286 t = Time('2001:001', format='yday') 1287 assert t.decimalyear == 2001.0 1288 1289 t = Time(2000.0, [0.5, 0.75], format='decimalyear') 1290 assert np.all(t.value == [2000.5, 2000.75]) 1291 1292 jd0 = Time('2000:001').jd 1293 jd1 = Time('2001:001').jd 1294 d_jd = jd1 - jd0 1295 assert np.all(t.jd == [jd0 + 0.5 * d_jd, 1296 jd0 + 0.75 * d_jd]) 1297 1298 1299def test_fits_year0(): 1300 t = Time(1721425.5, format='jd', scale='tai') 1301 assert t.fits == '0001-01-01T00:00:00.000' 1302 t = Time(1721425.5 - 366., format='jd', scale='tai') 1303 assert t.fits == '+00000-01-01T00:00:00.000' 1304 t = Time(1721425.5 - 366. - 365., format='jd', scale='tai') 1305 assert t.fits == '-00001-01-01T00:00:00.000' 1306 1307 1308def test_fits_year10000(): 1309 t = Time(5373484.5, format='jd', scale='tai') 1310 assert t.fits == '+10000-01-01T00:00:00.000' 1311 t = Time(5373484.5 - 365., format='jd', scale='tai') 1312 assert t.fits == '9999-01-01T00:00:00.000' 1313 t = Time(5373484.5, -1. / 24. / 3600., format='jd', scale='tai') 1314 assert t.fits == '9999-12-31T23:59:59.000' 1315 1316 1317def test_dir(): 1318 t = Time('2000:001', format='yday', scale='tai') 1319 assert 'utc' in dir(t) 1320 1321 1322def test_time_from_epoch_jds(): 1323 """Test that jd1/jd2 in a TimeFromEpoch format is always well-formed: 1324 jd1 is an integral value and abs(jd2) <= 0.5. 1325 """ 1326 # From 1999:001 00:00 to 1999:002 12:00 by a non-round step. This will 1327 # catch jd2 == 0 and a case of abs(jd2) == 0.5. 1328 cxcsecs = np.linspace(0, 86400 * 1.5, 49) 1329 for cxcsec in cxcsecs: 1330 t = Time(cxcsec, format='cxcsec') 1331 assert np.round(t.jd1) == t.jd1 1332 assert np.abs(t.jd2) <= 0.5 1333 1334 t = Time(cxcsecs, format='cxcsec') 1335 assert np.all(np.round(t.jd1) == t.jd1) 1336 assert np.all(np.abs(t.jd2) <= 0.5) 1337 assert np.any(np.abs(t.jd2) == 0.5) # At least one exactly 0.5 1338 1339 1340def test_bool(): 1341 """Any Time object should evaluate to True unless it is empty [#3520].""" 1342 t = Time(np.arange(50000, 50010), format='mjd', scale='utc') 1343 assert bool(t) is True 1344 assert bool(t[0]) is True 1345 assert bool(t[:0]) is False 1346 1347 1348def test_len_size(): 1349 """Check length of Time objects and that scalar ones do not have one.""" 1350 t = Time(np.arange(50000, 50010), format='mjd', scale='utc') 1351 assert len(t) == 10 and t.size == 10 1352 t1 = Time(np.arange(50000, 50010).reshape(2, 5), format='mjd', scale='utc') 1353 assert len(t1) == 2 and t1.size == 10 1354 # Can have length 1 or length 0 arrays. 1355 t2 = t[:1] 1356 assert len(t2) == 1 and t2.size == 1 1357 t3 = t[:0] 1358 assert len(t3) == 0 and t3.size == 0 1359 # But cannot get length from scalar. 1360 t4 = t[0] 1361 with pytest.raises(TypeError) as err: 1362 len(t4) 1363 # Ensure we're not just getting the old error of 1364 # "object of type 'float' has no len()". 1365 assert 'Time' in str(err.value) 1366 1367 1368def test_TimeFormat_scale(): 1369 """guard against recurrence of #1122, where TimeFormat class looses uses 1370 attributes (delta_ut1_utc here), preventing conversion to unix, cxc""" 1371 t = Time('1900-01-01', scale='ut1') 1372 t.delta_ut1_utc = 0.0 1373 with pytest.warns(ErfaWarning): 1374 t.unix 1375 assert t.unix == t.utc.unix 1376 1377 1378@pytest.mark.remote_data 1379def test_scale_conversion(monkeypatch): 1380 # Check that if we have internet, and downloading is allowed, we 1381 # can get conversion to UT1 for the present, since we will download 1382 # IERS_A in IERS_Auto. 1383 monkeypatch.setattr('astropy.utils.iers.conf.auto_download', True) 1384 Time(Time.now().cxcsec, format='cxcsec', scale='ut1') 1385 1386 1387def test_byteorder(): 1388 """Ensure that bigendian and little-endian both work (closes #2942)""" 1389 mjd = np.array([53000.00, 54000.00]) 1390 big_endian = mjd.astype('>f8') 1391 little_endian = mjd.astype('<f8') 1392 time_mjd = Time(mjd, format='mjd') 1393 time_big = Time(big_endian, format='mjd') 1394 time_little = Time(little_endian, format='mjd') 1395 assert np.all(time_big == time_mjd) 1396 assert np.all(time_little == time_mjd) 1397 1398 1399def test_datetime_tzinfo(): 1400 """ 1401 Test #3160 that time zone info in datetime objects is respected. 1402 """ 1403 class TZm6(datetime.tzinfo): 1404 def utcoffset(self, dt): 1405 return datetime.timedelta(hours=-6) 1406 1407 d = datetime.datetime(2002, 1, 2, 10, 3, 4, tzinfo=TZm6()) 1408 t = Time(d) 1409 assert t.value == datetime.datetime(2002, 1, 2, 16, 3, 4) 1410 1411 1412def test_subfmts_regex(): 1413 """ 1414 Test having a custom subfmts with a regular expression 1415 """ 1416 class TimeLongYear(TimeString): 1417 name = 'longyear' 1418 subfmts = (('date', 1419 r'(?P<year>[+-]\d{5})-%m-%d', # hybrid 1420 '{year:+06d}-{mon:02d}-{day:02d}'),) 1421 t = Time('+02000-02-03', format='longyear') 1422 assert t.value == '+02000-02-03' 1423 assert t.jd == Time('2000-02-03').jd 1424 1425 1426def test_set_format_basic(): 1427 """ 1428 Test basics of setting format attribute. 1429 """ 1430 for format, value in (('jd', 2451577.5), 1431 ('mjd', 51577.0), 1432 ('cxcsec', 65923264.184), # confirmed with Chandra.Time 1433 ('datetime', datetime.datetime(2000, 2, 3, 0, 0)), 1434 ('iso', '2000-02-03 00:00:00.000')): 1435 t = Time('+02000-02-03', format='fits') 1436 t0 = t.replicate() 1437 t.format = format 1438 assert t.value == value 1439 # Internal jd1 and jd2 are preserved 1440 assert t._time.jd1 is t0._time.jd1 1441 assert t._time.jd2 is t0._time.jd2 1442 1443 1444def test_unix_tai_format(): 1445 t = Time('2020-01-01', scale='utc') 1446 assert allclose_sec(t.unix_tai - t.unix, 37.0) 1447 t = Time('1970-01-01', scale='utc') 1448 assert allclose_sec(t.unix_tai - t.unix, 8 + 8.2e-05) 1449 1450 1451def test_set_format_shares_subfmt(): 1452 """ 1453 Set format and round trip through a format that shares out_subfmt 1454 """ 1455 t = Time('+02000-02-03', format='fits', out_subfmt='date_hms', precision=5) 1456 tc = t.copy() 1457 1458 t.format = 'isot' 1459 assert t.precision == 5 1460 assert t.out_subfmt == 'date_hms' 1461 assert t.value == '2000-02-03T00:00:00.00000' 1462 1463 t.format = 'fits' 1464 assert t.value == tc.value 1465 assert t.precision == 5 1466 1467 1468def test_set_format_does_not_share_subfmt(): 1469 """ 1470 Set format and round trip through a format that does not share out_subfmt 1471 """ 1472 t = Time('+02000-02-03', format='fits', out_subfmt='longdate') 1473 1474 t.format = 'isot' 1475 assert t.out_subfmt == '*' # longdate_hms not there, goes to default 1476 assert t.value == '2000-02-03T00:00:00.000' 1477 1478 t.format = 'fits' 1479 assert t.out_subfmt == '*' 1480 assert t.value == '2000-02-03T00:00:00.000' # date_hms 1481 1482 1483def test_replicate_value_error(): 1484 """ 1485 Passing a bad format to replicate should raise ValueError, not KeyError. 1486 PR #3857. 1487 """ 1488 t1 = Time('2007:001', scale='tai') 1489 with pytest.raises(ValueError) as err: 1490 t1.replicate(format='definitely_not_a_valid_format') 1491 assert 'format must be one of' in str(err.value) 1492 1493 1494def test_remove_astropy_time(): 1495 """ 1496 Make sure that 'astropy_time' format is really gone after #3857. Kind of 1497 silly test but just to be sure. 1498 """ 1499 t1 = Time('2007:001', scale='tai') 1500 assert 'astropy_time' not in t1.FORMATS 1501 with pytest.raises(ValueError) as err: 1502 Time(t1, format='astropy_time') 1503 assert 'format must be one of' in str(err.value) 1504 1505 1506def test_isiterable(): 1507 """ 1508 Ensure that scalar `Time` instances are not reported as iterable by the 1509 `isiterable` utility. 1510 1511 Regression test for https://github.com/astropy/astropy/issues/4048 1512 """ 1513 1514 t1 = Time.now() 1515 assert not isiterable(t1) 1516 1517 t2 = Time(['1999-01-01 00:00:00.123456789', '2010-01-01 00:00:00'], 1518 format='iso', scale='utc') 1519 assert isiterable(t2) 1520 1521 1522def test_to_datetime(): 1523 tz = TimezoneInfo(utc_offset=-10 * u.hour, tzname='US/Hawaii') 1524 # The above lines produces a `datetime.tzinfo` object similar to: 1525 # tzinfo = pytz.timezone('US/Hawaii') 1526 time = Time('2010-09-03 00:00:00') 1527 tz_aware_datetime = time.to_datetime(tz) 1528 assert tz_aware_datetime.time() == datetime.time(14, 0) 1529 forced_to_astropy_time = Time(tz_aware_datetime) 1530 assert tz.tzname(time.datetime) == tz_aware_datetime.tzname() 1531 assert time == forced_to_astropy_time 1532 1533 # Test non-scalar time inputs: 1534 time = Time(['2010-09-03 00:00:00', '2005-09-03 06:00:00', 1535 '1990-09-03 06:00:00']) 1536 tz_aware_datetime = time.to_datetime(tz) 1537 forced_to_astropy_time = Time(tz_aware_datetime) 1538 for dt, tz_dt in zip(time.datetime, tz_aware_datetime): 1539 assert tz.tzname(dt) == tz_dt.tzname() 1540 assert np.all(time == forced_to_astropy_time) 1541 1542 with pytest.raises(ValueError, match=r'does not support leap seconds'): 1543 Time('2015-06-30 23:59:60.000').to_datetime() 1544 1545 1546@pytest.mark.skipif('not HAS_PYTZ') 1547def test_to_datetime_pytz(): 1548 import pytz 1549 tz = pytz.timezone('US/Hawaii') 1550 time = Time('2010-09-03 00:00:00') 1551 tz_aware_datetime = time.to_datetime(tz) 1552 forced_to_astropy_time = Time(tz_aware_datetime) 1553 assert tz_aware_datetime.time() == datetime.time(14, 0) 1554 assert tz.tzname(time.datetime) == tz_aware_datetime.tzname() 1555 assert time == forced_to_astropy_time 1556 1557 # Test non-scalar time inputs: 1558 time = Time(['2010-09-03 00:00:00', '2005-09-03 06:00:00', 1559 '1990-09-03 06:00:00']) 1560 tz_aware_datetime = time.to_datetime(tz) 1561 forced_to_astropy_time = Time(tz_aware_datetime) 1562 for dt, tz_dt in zip(time.datetime, tz_aware_datetime): 1563 assert tz.tzname(dt) == tz_dt.tzname() 1564 assert np.all(time == forced_to_astropy_time) 1565 1566 1567def test_cache(): 1568 t = Time('2010-09-03 00:00:00') 1569 t2 = Time('2010-09-03 00:00:00') 1570 1571 # Time starts out without a cache 1572 assert 'cache' not in t._time.__dict__ 1573 1574 # Access the iso format and confirm that the cached version is as expected 1575 t.iso 1576 assert t.cache['format']['iso'] == t2.iso 1577 1578 # Access the TAI scale and confirm that the cached version is as expected 1579 t.tai 1580 assert t.cache['scale']['tai'] == t2.tai 1581 1582 # New Time object after scale transform does not have a cache yet 1583 assert 'cache' not in t.tt._time.__dict__ 1584 1585 # Clear the cache 1586 del t.cache 1587 assert 'cache' not in t._time.__dict__ 1588 # Check accessing the cache creates an empty dictionary 1589 assert not t.cache 1590 assert 'cache' in t._time.__dict__ 1591 1592 1593def test_epoch_date_jd_is_day_fraction(): 1594 """ 1595 Ensure that jd1 and jd2 of an epoch Time are respect the (day, fraction) convention 1596 (see #6638) 1597 """ 1598 t0 = Time("J2000", scale="tdb") 1599 1600 assert t0.jd1 == 2451545.0 1601 assert t0.jd2 == 0.0 1602 1603 t1 = Time(datetime.datetime(2000, 1, 1, 12, 0, 0), scale="tdb") 1604 1605 assert t1.jd1 == 2451545.0 1606 assert t1.jd2 == 0.0 1607 1608 1609def test_sum_is_equivalent(): 1610 """ 1611 Ensure that two equal dates defined in different ways behave equally (#6638) 1612 """ 1613 t0 = Time("J2000", scale="tdb") 1614 t1 = Time("2000-01-01 12:00:00", scale="tdb") 1615 1616 assert t0 == t1 1617 assert (t0 + 1 * u.second) == (t1 + 1 * u.second) 1618 1619 1620def test_string_valued_columns(): 1621 # Columns have a nice shim that translates bytes to string as needed. 1622 # Ensure Time can handle these. Use multi-d array just to be sure. 1623 times = [[[f'{y:04d}-{m:02d}-{d:02d}' for d in range(1, 3)] 1624 for m in range(5, 7)] for y in range(2012, 2014)] 1625 cutf32 = Column(times) 1626 cbytes = cutf32.astype('S') 1627 tutf32 = Time(cutf32) 1628 tbytes = Time(cbytes) 1629 assert np.all(tutf32 == tbytes) 1630 tutf32 = Time(Column(['B1950'])) 1631 tbytes = Time(Column([b'B1950'])) 1632 assert tutf32 == tbytes 1633 # Regression tests for arrays with entries with unequal length. gh-6903. 1634 times = Column([b'2012-01-01', b'2012-01-01T00:00:00']) 1635 assert np.all(Time(times) == Time(['2012-01-01', '2012-01-01T00:00:00'])) 1636 1637 1638def test_bytes_input(): 1639 tstring = '2011-01-02T03:04:05' 1640 tbytes = b'2011-01-02T03:04:05' 1641 assert tbytes.decode('ascii') == tstring 1642 t0 = Time(tstring) 1643 t1 = Time(tbytes) 1644 assert t1 == t0 1645 tarray = np.array(tbytes) 1646 assert tarray.dtype.kind == 'S' 1647 t2 = Time(tarray) 1648 assert t2 == t0 1649 1650 1651def test_writeable_flag(): 1652 t = Time([1, 2, 3], format='cxcsec') 1653 t[1] = 5.0 1654 assert allclose_sec(t[1].value, 5.0) 1655 1656 t.writeable = False 1657 with pytest.raises(ValueError) as err: 1658 t[1] = 5.0 1659 assert 'Time object is read-only. Make a copy()' in str(err.value) 1660 1661 with pytest.raises(ValueError) as err: 1662 t[:] = 5.0 1663 assert 'Time object is read-only. Make a copy()' in str(err.value) 1664 1665 t.writeable = True 1666 t[1] = 10.0 1667 assert allclose_sec(t[1].value, 10.0) 1668 1669 # Scalar is writeable because it gets boxed into a zero-d array 1670 t = Time('2000:001', scale='utc') 1671 t[()] = '2000:002' 1672 assert t.value.startswith('2000:002') 1673 1674 # Transformed attribute is not writeable 1675 t = Time(['2000:001', '2000:002'], scale='utc') 1676 t2 = t.tt # t2 is read-only now because t.tt is cached 1677 with pytest.raises(ValueError) as err: 1678 t2[0] = '2005:001' 1679 assert 'Time object is read-only. Make a copy()' in str(err.value) 1680 1681 1682def test_setitem_location(): 1683 loc = EarthLocation(x=[1, 2] * u.m, y=[3, 4] * u.m, z=[5, 6] * u.m) 1684 t = Time([[1, 2], [3, 4]], format='cxcsec', location=loc) 1685 1686 # Succeeds because the right hand side makes no implication about 1687 # location and just inherits t.location 1688 t[0, 0] = 0 1689 assert allclose_sec(t.value, [[0, 2], [3, 4]]) 1690 1691 # Fails because the right hand side has location=None 1692 with pytest.raises(ValueError) as err: 1693 t[0, 0] = Time(-1, format='cxcsec') 1694 assert ('cannot set to Time with different location: ' 1695 'expected location={} and ' 1696 'got location=None'.format(loc[0])) in str(err.value) 1697 1698 # Succeeds because the right hand side correctly sets location 1699 t[0, 0] = Time(-2, format='cxcsec', location=loc[0]) 1700 assert allclose_sec(t.value, [[-2, 2], [3, 4]]) 1701 1702 # Fails because the right hand side has different location 1703 with pytest.raises(ValueError) as err: 1704 t[0, 0] = Time(-2, format='cxcsec', location=loc[1]) 1705 assert ('cannot set to Time with different location: ' 1706 'expected location={} and ' 1707 'got location={}'.format(loc[0], loc[1])) in str(err.value) 1708 1709 # Fails because the Time has None location and RHS has defined location 1710 t = Time([[1, 2], [3, 4]], format='cxcsec') 1711 with pytest.raises(ValueError) as err: 1712 t[0, 0] = Time(-2, format='cxcsec', location=loc[1]) 1713 assert ('cannot set to Time with different location: ' 1714 'expected location=None and ' 1715 'got location={}'.format(loc[1])) in str(err.value) 1716 1717 # Broadcasting works 1718 t = Time([[1, 2], [3, 4]], format='cxcsec', location=loc) 1719 t[0, :] = Time([-3, -4], format='cxcsec', location=loc) 1720 assert allclose_sec(t.value, [[-3, -4], [3, 4]]) 1721 1722 1723def test_setitem_from_python_objects(): 1724 t = Time([[1, 2], [3, 4]], format='cxcsec') 1725 assert t.cache == {} 1726 t.iso 1727 assert 'iso' in t.cache['format'] 1728 assert np.all(t.iso == [['1998-01-01 00:00:01.000', '1998-01-01 00:00:02.000'], 1729 ['1998-01-01 00:00:03.000', '1998-01-01 00:00:04.000']]) 1730 1731 # Setting item clears cache 1732 t[0, 1] = 100 1733 assert t.cache == {} 1734 assert allclose_sec(t.value, [[1, 100], 1735 [3, 4]]) 1736 assert np.all(t.iso == [['1998-01-01 00:00:01.000', '1998-01-01 00:01:40.000'], 1737 ['1998-01-01 00:00:03.000', '1998-01-01 00:00:04.000']]) 1738 1739 # Set with a float value 1740 t.iso 1741 t[1, :] = 200 1742 assert t.cache == {} 1743 assert allclose_sec(t.value, [[1, 100], 1744 [200, 200]]) 1745 1746 # Array of strings in yday format 1747 t[:, 1] = ['1998:002', '1998:003'] 1748 assert allclose_sec(t.value, [[1, 86400 * 1], 1749 [200, 86400 * 2]]) 1750 1751 # Incompatible numeric value 1752 t = Time(['2000:001', '2000:002']) 1753 t[0] = '2001:001' 1754 with pytest.raises(ValueError) as err: 1755 t[0] = 100 1756 assert 'cannot convert value to a compatible Time object' in str(err.value) 1757 1758 1759def test_setitem_from_time_objects(): 1760 """Set from existing Time object. 1761 """ 1762 # Set from time object with different scale 1763 t = Time(['2000:001', '2000:002'], scale='utc') 1764 t2 = Time(['2000:010'], scale='tai') 1765 t[1] = t2[0] 1766 assert t.value[1] == t2.utc.value[0] 1767 1768 # Time object with different scale and format 1769 t = Time(['2000:001', '2000:002'], scale='utc') 1770 t2.format = 'jyear' 1771 t[1] = t2[0] 1772 assert t.yday[1] == t2.utc.yday[0] 1773 1774 1775def test_setitem_bad_item(): 1776 t = Time([1, 2], format='cxcsec') 1777 with pytest.raises(IndexError): 1778 t['asdf'] = 3 1779 1780 1781def test_setitem_deltas(): 1782 """Setting invalidates any transform deltas""" 1783 t = Time([1, 2], format='cxcsec') 1784 t.delta_tdb_tt = [1, 2] 1785 t.delta_ut1_utc = [3, 4] 1786 t[1] = 3 1787 assert not hasattr(t, '_delta_tdb_tt') 1788 assert not hasattr(t, '_delta_ut1_utc') 1789 1790 1791def test_subclass(): 1792 """Check that we can initialize subclasses with a Time instance.""" 1793 # Ref: Issue gh-#7449 and PR gh-#7453. 1794 1795 class _Time(Time): 1796 pass 1797 1798 t1 = Time('1999-01-01T01:01:01') 1799 t2 = _Time(t1) 1800 1801 assert t2.__class__ == _Time 1802 assert t1 == t2 1803 1804 1805def test_strftime_scalar(): 1806 """Test of Time.strftime 1807 """ 1808 time_string = '2010-09-03 06:00:00' 1809 t = Time(time_string) 1810 1811 for format in t.FORMATS: 1812 t.format = format 1813 assert t.strftime('%Y-%m-%d %H:%M:%S') == time_string 1814 1815 1816def test_strftime_array(): 1817 tstrings = ['2010-09-03 00:00:00', '2005-09-03 06:00:00', 1818 '1995-12-31 23:59:60'] 1819 t = Time(tstrings) 1820 1821 for format in t.FORMATS: 1822 t.format = format 1823 assert t.strftime('%Y-%m-%d %H:%M:%S').tolist() == tstrings 1824 1825 1826def test_strftime_array_2(): 1827 tstrings = [['1998-01-01 00:00:01', '1998-01-01 00:00:02'], 1828 ['1998-01-01 00:00:03', '1995-12-31 23:59:60']] 1829 tstrings = np.array(tstrings) 1830 1831 t = Time(tstrings) 1832 1833 for format in t.FORMATS: 1834 t.format = format 1835 assert np.all(t.strftime('%Y-%m-%d %H:%M:%S') == tstrings) 1836 assert t.strftime('%Y-%m-%d %H:%M:%S').shape == tstrings.shape 1837 1838 1839def test_strftime_leapsecond(): 1840 time_string = '1995-12-31 23:59:60' 1841 t = Time(time_string) 1842 1843 for format in t.FORMATS: 1844 t.format = format 1845 assert t.strftime('%Y-%m-%d %H:%M:%S') == time_string 1846 1847 1848def test_strptime_scalar(): 1849 """Test of Time.strptime 1850 """ 1851 time_string = '2007-May-04 21:08:12' 1852 time_object = Time('2007-05-04 21:08:12') 1853 t = Time.strptime(time_string, '%Y-%b-%d %H:%M:%S') 1854 1855 assert t == time_object 1856 1857 1858def test_strptime_array(): 1859 """Test of Time.strptime 1860 """ 1861 tstrings = [['1998-Jan-01 00:00:01', '1998-Jan-01 00:00:02'], 1862 ['1998-Jan-01 00:00:03', '1998-Jan-01 00:00:04']] 1863 tstrings = np.array(tstrings) 1864 1865 time_object = Time([['1998-01-01 00:00:01', '1998-01-01 00:00:02'], 1866 ['1998-01-01 00:00:03', '1998-01-01 00:00:04']]) 1867 t = Time.strptime(tstrings, '%Y-%b-%d %H:%M:%S') 1868 1869 assert np.all(t == time_object) 1870 assert t.shape == tstrings.shape 1871 1872 1873def test_strptime_badinput(): 1874 tstrings = [1, 2, 3] 1875 with pytest.raises(TypeError): 1876 Time.strptime(tstrings, '%S') 1877 1878 1879def test_strptime_input_bytes_scalar(): 1880 time_string = b'2007-May-04 21:08:12' 1881 time_object = Time('2007-05-04 21:08:12') 1882 t = Time.strptime(time_string, '%Y-%b-%d %H:%M:%S') 1883 1884 assert t == time_object 1885 1886 1887def test_strptime_input_bytes_array(): 1888 tstrings = [[b'1998-Jan-01 00:00:01', b'1998-Jan-01 00:00:02'], 1889 [b'1998-Jan-01 00:00:03', b'1998-Jan-01 00:00:04']] 1890 tstrings = np.array(tstrings) 1891 1892 time_object = Time([['1998-01-01 00:00:01', '1998-01-01 00:00:02'], 1893 ['1998-01-01 00:00:03', '1998-01-01 00:00:04']]) 1894 t = Time.strptime(tstrings, '%Y-%b-%d %H:%M:%S') 1895 1896 assert np.all(t == time_object) 1897 assert t.shape == tstrings.shape 1898 1899 1900def test_strptime_leapsecond(): 1901 time_obj1 = Time('1995-12-31T23:59:60', format='isot') 1902 time_obj2 = Time.strptime('1995-Dec-31 23:59:60', '%Y-%b-%d %H:%M:%S') 1903 1904 assert time_obj1 == time_obj2 1905 1906 1907def test_strptime_3_digit_year(): 1908 time_obj1 = Time('0995-12-31T00:00:00', format='isot', scale='tai') 1909 time_obj2 = Time.strptime('0995-Dec-31 00:00:00', '%Y-%b-%d %H:%M:%S', 1910 scale='tai') 1911 1912 assert time_obj1 == time_obj2 1913 1914 1915def test_strptime_fracsec_scalar(): 1916 time_string = '2007-May-04 21:08:12.123' 1917 time_object = Time('2007-05-04 21:08:12.123') 1918 t = Time.strptime(time_string, '%Y-%b-%d %H:%M:%S.%f') 1919 1920 assert t == time_object 1921 1922 1923def test_strptime_fracsec_array(): 1924 """Test of Time.strptime 1925 """ 1926 tstrings = [['1998-Jan-01 00:00:01.123', '1998-Jan-01 00:00:02.000001'], 1927 ['1998-Jan-01 00:00:03.000900', '1998-Jan-01 00:00:04.123456']] 1928 tstrings = np.array(tstrings) 1929 1930 time_object = Time([['1998-01-01 00:00:01.123', '1998-01-01 00:00:02.000001'], 1931 ['1998-01-01 00:00:03.000900', '1998-01-01 00:00:04.123456']]) 1932 t = Time.strptime(tstrings, '%Y-%b-%d %H:%M:%S.%f') 1933 1934 assert np.all(t == time_object) 1935 assert t.shape == tstrings.shape 1936 1937 1938def test_strftime_scalar_fracsec(): 1939 """Test of Time.strftime 1940 """ 1941 time_string = '2010-09-03 06:00:00.123' 1942 t = Time(time_string) 1943 1944 for format in t.FORMATS: 1945 t.format = format 1946 assert t.strftime('%Y-%m-%d %H:%M:%S.%f') == time_string 1947 1948 1949def test_strftime_scalar_fracsec_precision(): 1950 time_string = '2010-09-03 06:00:00.123123123' 1951 t = Time(time_string) 1952 assert t.strftime('%Y-%m-%d %H:%M:%S.%f') == '2010-09-03 06:00:00.123' 1953 t.precision = 9 1954 assert t.strftime('%Y-%m-%d %H:%M:%S.%f') == '2010-09-03 06:00:00.123123123' 1955 1956 1957def test_strftime_array_fracsec(): 1958 tstrings = ['2010-09-03 00:00:00.123000', '2005-09-03 06:00:00.000001', 1959 '1995-12-31 23:59:60.000900'] 1960 t = Time(tstrings) 1961 t.precision = 6 1962 1963 for format in t.FORMATS: 1964 t.format = format 1965 assert t.strftime('%Y-%m-%d %H:%M:%S.%f').tolist() == tstrings 1966 1967 1968def test_insert_time(): 1969 tm = Time([1, 2], format='unix') 1970 1971 # Insert a scalar using an auto-parsed string 1972 tm2 = tm.insert(1, '1970-01-01 00:01:00') 1973 assert np.all(tm2 == Time([1, 60, 2], format='unix')) 1974 1975 # Insert scalar using a Time value 1976 tm2 = tm.insert(1, Time('1970-01-01 00:01:00')) 1977 assert np.all(tm2 == Time([1, 60, 2], format='unix')) 1978 1979 # Insert length=1 array with a Time value 1980 tm2 = tm.insert(1, [Time('1970-01-01 00:01:00')]) 1981 assert np.all(tm2 == Time([1, 60, 2], format='unix')) 1982 1983 # Insert length=2 list with float values matching unix format. 1984 # Also actually provide axis=0 unlike all other tests. 1985 tm2 = tm.insert(1, [10, 20], axis=0) 1986 assert np.all(tm2 == Time([1, 10, 20, 2], format='unix')) 1987 1988 # Insert length=2 np.array with float values matching unix format 1989 tm2 = tm.insert(1, np.array([10, 20])) 1990 assert np.all(tm2 == Time([1, 10, 20, 2], format='unix')) 1991 1992 # Insert length=2 np.array with float values at the end 1993 tm2 = tm.insert(2, np.array([10, 20])) 1994 assert np.all(tm2 == Time([1, 2, 10, 20], format='unix')) 1995 1996 # Insert length=2 np.array with float values at the beginning 1997 # with a negative index 1998 tm2 = tm.insert(-2, np.array([10, 20])) 1999 assert np.all(tm2 == Time([10, 20, 1, 2], format='unix')) 2000 2001 2002def test_insert_exceptions(): 2003 tm = Time(1, format='unix') 2004 with pytest.raises(TypeError) as err: 2005 tm.insert(0, 50) 2006 assert 'cannot insert into scalar' in str(err.value) 2007 2008 tm = Time([1, 2], format='unix') 2009 with pytest.raises(ValueError) as err: 2010 tm.insert(0, 50, axis=1) 2011 assert 'axis must be 0' in str(err.value) 2012 2013 with pytest.raises(TypeError) as err: 2014 tm.insert(slice(None), 50) 2015 assert 'obj arg must be an integer' in str(err.value) 2016 2017 with pytest.raises(IndexError) as err: 2018 tm.insert(-100, 50) 2019 assert 'index -100 is out of bounds for axis 0 with size 2' in str(err.value) 2020 2021 2022def test_datetime64_no_format(): 2023 dt64 = np.datetime64('2000-01-02T03:04:05.123456789') 2024 t = Time(dt64, scale='utc', precision=9) 2025 assert t.iso == '2000-01-02 03:04:05.123456789' 2026 assert t.datetime64 == dt64 2027 assert t.value == dt64 2028 2029 2030def test_hash_time(): 2031 loc1 = EarthLocation(1 * u.m, 2 * u.m, 3 * u.m) 2032 for loc in None, loc1: 2033 t = Time([1, 1, 2, 3], format='cxcsec', location=loc) 2034 t[3] = np.ma.masked 2035 h1 = hash(t[0]) 2036 h2 = hash(t[1]) 2037 h3 = hash(t[2]) 2038 assert h1 == h2 2039 assert h1 != h3 2040 2041 with pytest.raises(TypeError) as exc: 2042 hash(t) 2043 assert exc.value.args[0] == "unhashable type: 'Time' (must be scalar)" 2044 2045 with pytest.raises(TypeError) as exc: 2046 hash(t[3]) 2047 assert exc.value.args[0] == "unhashable type: 'Time' (value is masked)" 2048 2049 t = Time(1, format='cxcsec', location=loc) 2050 t2 = Time(1, format='cxcsec') 2051 2052 assert hash(t) != hash(t2) 2053 2054 t = Time('2000:180', scale='utc') 2055 t2 = Time(t, scale='tai') 2056 assert t == t2 2057 assert hash(t) != hash(t2) 2058 2059 2060def test_hash_time_delta(): 2061 2062 t = TimeDelta([1, 1, 2, 3], format='sec') 2063 t[3] = np.ma.masked 2064 h1 = hash(t[0]) 2065 h2 = hash(t[1]) 2066 h3 = hash(t[2]) 2067 assert h1 == h2 2068 assert h1 != h3 2069 2070 with pytest.raises(TypeError) as exc: 2071 hash(t) 2072 assert exc.value.args[0] == "unhashable type: 'TimeDelta' (must be scalar)" 2073 2074 with pytest.raises(TypeError) as exc: 2075 hash(t[3]) 2076 assert exc.value.args[0] == "unhashable type: 'TimeDelta' (value is masked)" 2077 2078 2079def test_get_time_fmt_exception_messages(): 2080 with pytest.raises(ValueError) as err: 2081 Time(10) 2082 assert "No time format was given, and the input is" in str(err.value) 2083 2084 with pytest.raises(ValueError) as err: 2085 Time('2000:001', format='not-a-format') 2086 assert "Format 'not-a-format' is not one of the allowed" in str(err.value) 2087 2088 with pytest.raises(ValueError) as err: 2089 Time('200') 2090 assert 'Input values did not match any of the formats where' in str(err.value) 2091 2092 with pytest.raises(ValueError) as err: 2093 Time('200', format='iso') 2094 assert ('Input values did not match the format class iso:' + os.linesep 2095 + 'ValueError: Time 200 does not match iso format') == str(err.value) 2096 2097 with pytest.raises(ValueError) as err: 2098 Time(200, format='iso') 2099 assert ('Input values did not match the format class iso:' + os.linesep 2100 + 'TypeError: Input values for iso class must be strings') == str(err.value) 2101 2102 2103def test_ymdhms_defaults(): 2104 t1 = Time({'year': 2001}, format='ymdhms') 2105 assert t1 == Time('2001-01-01') 2106 2107 2108times_dict_ns = { 2109 'year': [2001, 2002], 2110 'month': [2, 3], 2111 'day': [4, 5], 2112 'hour': [6, 7], 2113 'minute': [8, 9], 2114 'second': [10, 11] 2115} 2116table_ns = Table(times_dict_ns) 2117struct_array_ns = table_ns.as_array() 2118rec_array_ns = struct_array_ns.view(np.recarray) 2119ymdhms_names = ('year', 'month', 'day', 'hour', 'minute', 'second') 2120 2121 2122@pytest.mark.parametrize('tm_input', [table_ns, struct_array_ns, rec_array_ns]) 2123@pytest.mark.parametrize('kwargs', [{}, {'format': 'ymdhms'}]) 2124@pytest.mark.parametrize('as_row', [False, True]) 2125def test_ymdhms_init_from_table_like(tm_input, kwargs, as_row): 2126 time_ns = Time(['2001-02-04 06:08:10', '2002-03-05 07:09:11']) 2127 if as_row: 2128 tm_input = tm_input[0] 2129 time_ns = time_ns[0] 2130 2131 tm = Time(tm_input, **kwargs) 2132 assert np.all(tm == time_ns) 2133 assert tm.value.dtype.names == ymdhms_names 2134 2135 2136def test_ymdhms_init_from_dict_array(): 2137 times_dict_shape = { 2138 'year': [[2001, 2002], 2139 [2003, 2004]], 2140 'month': [2, 3], 2141 'day': 4 2142 } 2143 time_shape = Time( 2144 [['2001-02-04', '2002-03-04'], 2145 ['2003-02-04', '2004-03-04']] 2146 ) 2147 time = Time(times_dict_shape, format='ymdhms') 2148 2149 assert np.all(time == time_shape) 2150 assert time.ymdhms.shape == time_shape.shape 2151 2152 2153@pytest.mark.parametrize('kwargs', [{}, {'format': 'ymdhms'}]) 2154def test_ymdhms_init_from_dict_scalar(kwargs): 2155 """ 2156 Test YMDHMS functionality for a dict input. This includes ensuring that 2157 key and attribute access work. For extra fun use a time within a leap 2158 second. 2159 """ 2160 time_dict = { 2161 'year': 2016, 2162 'month': 12, 2163 'day': 31, 2164 'hour': 23, 2165 'minute': 59, 2166 'second': 60.123456789} 2167 2168 tm = Time(time_dict, **kwargs) 2169 2170 assert tm == Time('2016-12-31T23:59:60.123456789') 2171 for attr in time_dict: 2172 for value in (tm.value[attr], getattr(tm.value, attr)): 2173 if attr == 'second': 2174 assert allclose_sec(time_dict[attr], value) 2175 else: 2176 assert time_dict[attr] == value 2177 2178 # Now test initializing from a YMDHMS format time using the object 2179 tm_rt = Time(tm) 2180 assert tm_rt == tm 2181 assert tm_rt.format == 'ymdhms' 2182 2183 # Test initializing from a YMDHMS value (np.void, i.e. recarray row) 2184 # without specified format. 2185 tm_rt = Time(tm.ymdhms) 2186 assert tm_rt == tm 2187 assert tm_rt.format == 'ymdhms' 2188 2189 2190def test_ymdhms_exceptions(): 2191 with pytest.raises(ValueError, match='input must be dict or table-like'): 2192 Time(10, format='ymdhms') 2193 2194 match = "'wrong' not allowed as YMDHMS key name(s)" 2195 # NB: for reasons unknown, using match=match in pytest.raises() fails, so we 2196 # fall back to old school ``match in str(err.value)``. 2197 with pytest.raises(ValueError) as err: 2198 Time({'year': 2019, 'wrong': 1}, format='ymdhms') 2199 assert match in str(err.value) 2200 2201 match = "for 2 input key names you must supply 'year', 'month'" 2202 with pytest.raises(ValueError, match=match): 2203 Time({'year': 2019, 'minute': 1}, format='ymdhms') 2204 2205 2206def test_ymdhms_masked(): 2207 tm = Time({'year': [2000, 2001]}, format='ymdhms') 2208 tm[0] = np.ma.masked 2209 assert isinstance(tm.value[0], np.ma.core.mvoid) 2210 for name in ymdhms_names: 2211 assert tm.value[0][name] is np.ma.masked 2212 2213 2214# Converted from doctest in astropy/test/formats.py for debugging 2215def test_ymdhms_output(): 2216 t = Time({'year': 2015, 'month': 2, 'day': 3, 2217 'hour': 12, 'minute': 13, 'second': 14.567}, 2218 scale='utc') 2219 # NOTE: actually comes back as np.void for some reason 2220 # NOTE: not necessarily a python int; might be an int32 2221 assert t.ymdhms.year == 2015 2222 2223 2224# There are two stages of validation now - one on input into a format, so that 2225# the format conversion code has tidy matched arrays to work with, and the 2226# other when object construction does not go through a format object. Or at 2227# least, the format object is constructed with "from_jd=True". In this case the 2228# normal input validation does not happen but the new input validation does, 2229# and can ensure that strange broadcasting anomalies can't happen. 2230# This form of construction uses from_jd=True. 2231def test_broadcasting_writeable(): 2232 t = Time('J2015') + np.linspace(-1, 1, 10) * u.day 2233 t[2] = Time(58000, format="mjd") 2234 2235 2236def test_format_subformat_compatibility(): 2237 """Test that changing format with out_subfmt defined is not a problem. 2238 See #9812, #9810.""" 2239 t = Time('2019-12-20', out_subfmt='date_??') 2240 assert t.mjd == 58837.0 2241 assert t.yday == '2019:354:00:00' # Preserves out_subfmt 2242 2243 t2 = t.replicate(format='mjd') 2244 assert t2.out_subfmt == '*' # Changes to default 2245 2246 t2 = t.copy(format='mjd') 2247 assert t2.out_subfmt == '*' 2248 2249 t2 = Time(t, format='mjd') 2250 assert t2.out_subfmt == '*' 2251 2252 t2 = t.copy(format='yday') 2253 assert t2.out_subfmt == 'date_??' 2254 assert t2.value == '2019:354:00:00' 2255 2256 t.format = 'yday' 2257 assert t.value == '2019:354:00:00' 2258 assert t.out_subfmt == 'date_??' 2259 2260 t = Time('2019-12-20', out_subfmt='date') 2261 assert t.mjd == 58837.0 2262 assert t.yday == '2019:354' 2263 2264 2265@pytest.mark.parametrize('fmt_name,fmt_class', TIME_FORMATS.items()) 2266def test_to_value_with_subfmt_for_every_format(fmt_name, fmt_class): 2267 """From a starting Time value, test that every valid combination of 2268 to_value(format, subfmt) works. See #9812, #9361. 2269 """ 2270 t = Time('2000-01-01') 2271 subfmts = list(subfmt[0] for subfmt in fmt_class.subfmts) + [None, '*'] 2272 for subfmt in subfmts: 2273 t.to_value(fmt_name, subfmt) 2274 2275 2276@pytest.mark.parametrize('location', [None, (45, 45)]) 2277def test_location_init(location): 2278 """Test fix in #9969 for issue #9962 where the location attribute is 2279 lost when initializing Time from an existing Time instance of list of 2280 Time instances. 2281 """ 2282 tm = Time('J2010', location=location) 2283 2284 # Init from a scalar Time 2285 tm2 = Time(tm) 2286 assert np.all(tm.location == tm2.location) 2287 assert type(tm.location) is type(tm2.location) # noqa 2288 2289 # From a list of Times 2290 tm2 = Time([tm, tm]) 2291 if location is None: 2292 assert tm2.location is None 2293 else: 2294 for loc in tm2.location: 2295 assert loc == tm.location 2296 assert type(tm.location) is type(tm2.location) # noqa 2297 2298 # Effectively the same as a list of Times, but just to be sure that 2299 # Table mixin inititialization is working as expected. 2300 tm2 = Table([[tm, tm]])['col0'] 2301 if location is None: 2302 assert tm2.location is None 2303 else: 2304 for loc in tm2.location: 2305 assert loc == tm.location 2306 assert type(tm.location) is type(tm2.location) # noqa 2307 2308 2309def test_location_init_fail(): 2310 """Test fix in #9969 for issue #9962 where the location attribute is 2311 lost when initializing Time from an existing Time instance of list of 2312 Time instances. Make sure exception is correct. 2313 """ 2314 tm = Time('J2010', location=(45, 45)) 2315 tm2 = Time('J2010') 2316 2317 with pytest.raises(ValueError, 2318 match='cannot concatenate times unless all locations'): 2319 Time([tm, tm2]) 2320