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