import numpy import numpy as np import datetime import pytest from numpy.testing import ( IS_WASM, assert_, assert_equal, assert_raises, assert_warns, suppress_warnings, assert_raises_regex, assert_array_equal, ) from numpy.compat import pickle # Use pytz to test out various time zones if available try: from pytz import timezone as tz _has_pytz = True except ImportError: _has_pytz = False try: RecursionError except NameError: RecursionError = RuntimeError # python < 3.5 class TestDateTime: def test_datetime_dtype_creation(self): for unit in ['Y', 'M', 'W', 'D', 'h', 'm', 's', 'ms', 'us', 'μs', # alias for us 'ns', 'ps', 'fs', 'as']: dt1 = np.dtype('M8[750%s]' % unit) assert_(dt1 == np.dtype('datetime64[750%s]' % unit)) dt2 = np.dtype('m8[%s]' % unit) assert_(dt2 == np.dtype('timedelta64[%s]' % unit)) # Generic units shouldn't add [] to the end assert_equal(str(np.dtype("M8")), "datetime64") # Should be possible to specify the endianness assert_equal(np.dtype("=M8"), np.dtype("M8")) assert_equal(np.dtype("=M8[s]"), np.dtype("M8[s]")) assert_(np.dtype(">M8") == np.dtype("M8") or np.dtype("M8[D]") == np.dtype("M8[D]") or np.dtype("M8") != np.dtype("m8") == np.dtype("m8") or np.dtype("m8[D]") == np.dtype("m8[D]") or np.dtype("m8") != np.dtype(" Scalars assert_equal(np.datetime64(b, '[s]'), np.datetime64('NaT', '[s]')) assert_equal(np.datetime64(b, '[ms]'), np.datetime64('NaT', '[ms]')) assert_equal(np.datetime64(b, '[M]'), np.datetime64('NaT', '[M]')) assert_equal(np.datetime64(b, '[Y]'), np.datetime64('NaT', '[Y]')) assert_equal(np.datetime64(b, '[W]'), np.datetime64('NaT', '[W]')) # Arrays -> Scalars assert_equal(np.datetime64(a, '[s]'), np.datetime64('NaT', '[s]')) assert_equal(np.datetime64(a, '[ms]'), np.datetime64('NaT', '[ms]')) assert_equal(np.datetime64(a, '[M]'), np.datetime64('NaT', '[M]')) assert_equal(np.datetime64(a, '[Y]'), np.datetime64('NaT', '[Y]')) assert_equal(np.datetime64(a, '[W]'), np.datetime64('NaT', '[W]')) # NaN -> NaT nan = np.array([np.nan] * 8) fnan = nan.astype('f') lnan = nan.astype('g') cnan = nan.astype('D') cfnan = nan.astype('F') clnan = nan.astype('G') nat = np.array([np.datetime64('NaT')] * 8) assert_equal(nan.astype('M8[ns]'), nat) assert_equal(fnan.astype('M8[ns]'), nat) assert_equal(lnan.astype('M8[ns]'), nat) assert_equal(cnan.astype('M8[ns]'), nat) assert_equal(cfnan.astype('M8[ns]'), nat) assert_equal(clnan.astype('M8[ns]'), nat) nat = np.array([np.timedelta64('NaT')] * 8) assert_equal(nan.astype('timedelta64[ns]'), nat) assert_equal(fnan.astype('timedelta64[ns]'), nat) assert_equal(lnan.astype('timedelta64[ns]'), nat) assert_equal(cnan.astype('timedelta64[ns]'), nat) assert_equal(cfnan.astype('timedelta64[ns]'), nat) assert_equal(clnan.astype('timedelta64[ns]'), nat) def test_days_creation(self): assert_equal(np.array('1599', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)/4 + 3 - 365) assert_equal(np.array('1600', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)/4 + 3) assert_equal(np.array('1601', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)/4 + 3 + 366) assert_equal(np.array('1900', dtype='M8[D]').astype('i8'), (1900-1970)*365 - (1970-1900)//4) assert_equal(np.array('1901', dtype='M8[D]').astype('i8'), (1900-1970)*365 - (1970-1900)//4 + 365) assert_equal(np.array('1967', dtype='M8[D]').astype('i8'), -3*365 - 1) assert_equal(np.array('1968', dtype='M8[D]').astype('i8'), -2*365 - 1) assert_equal(np.array('1969', dtype='M8[D]').astype('i8'), -1*365) assert_equal(np.array('1970', dtype='M8[D]').astype('i8'), 0*365) assert_equal(np.array('1971', dtype='M8[D]').astype('i8'), 1*365) assert_equal(np.array('1972', dtype='M8[D]').astype('i8'), 2*365) assert_equal(np.array('1973', dtype='M8[D]').astype('i8'), 3*365 + 1) assert_equal(np.array('1974', dtype='M8[D]').astype('i8'), 4*365 + 1) assert_equal(np.array('2000', dtype='M8[D]').astype('i8'), (2000 - 1970)*365 + (2000 - 1972)//4) assert_equal(np.array('2001', dtype='M8[D]').astype('i8'), (2000 - 1970)*365 + (2000 - 1972)//4 + 366) assert_equal(np.array('2400', dtype='M8[D]').astype('i8'), (2400 - 1970)*365 + (2400 - 1972)//4 - 3) assert_equal(np.array('2401', dtype='M8[D]').astype('i8'), (2400 - 1970)*365 + (2400 - 1972)//4 - 3 + 366) assert_equal(np.array('1600-02-29', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)//4 + 3 + 31 + 28) assert_equal(np.array('1600-03-01', dtype='M8[D]').astype('i8'), (1600-1970)*365 - (1972-1600)//4 + 3 + 31 + 29) assert_equal(np.array('2000-02-29', dtype='M8[D]').astype('i8'), (2000 - 1970)*365 + (2000 - 1972)//4 + 31 + 28) assert_equal(np.array('2000-03-01', dtype='M8[D]').astype('i8'), (2000 - 1970)*365 + (2000 - 1972)//4 + 31 + 29) assert_equal(np.array('2001-03-22', dtype='M8[D]').astype('i8'), (2000 - 1970)*365 + (2000 - 1972)//4 + 366 + 31 + 28 + 21) def test_days_to_pydate(self): assert_equal(np.array('1599', dtype='M8[D]').astype('O'), datetime.date(1599, 1, 1)) assert_equal(np.array('1600', dtype='M8[D]').astype('O'), datetime.date(1600, 1, 1)) assert_equal(np.array('1601', dtype='M8[D]').astype('O'), datetime.date(1601, 1, 1)) assert_equal(np.array('1900', dtype='M8[D]').astype('O'), datetime.date(1900, 1, 1)) assert_equal(np.array('1901', dtype='M8[D]').astype('O'), datetime.date(1901, 1, 1)) assert_equal(np.array('2000', dtype='M8[D]').astype('O'), datetime.date(2000, 1, 1)) assert_equal(np.array('2001', dtype='M8[D]').astype('O'), datetime.date(2001, 1, 1)) assert_equal(np.array('1600-02-29', dtype='M8[D]').astype('O'), datetime.date(1600, 2, 29)) assert_equal(np.array('1600-03-01', dtype='M8[D]').astype('O'), datetime.date(1600, 3, 1)) assert_equal(np.array('2001-03-22', dtype='M8[D]').astype('O'), datetime.date(2001, 3, 22)) def test_dtype_comparison(self): assert_(not (np.dtype('M8[us]') == np.dtype('M8[ms]'))) assert_(np.dtype('M8[us]') != np.dtype('M8[ms]')) assert_(np.dtype('M8[2D]') != np.dtype('M8[D]')) assert_(np.dtype('M8[D]') != np.dtype('M8[2D]')) def test_pydatetime_creation(self): a = np.array(['1960-03-12', datetime.date(1960, 3, 12)], dtype='M8[D]') assert_equal(a[0], a[1]) a = np.array(['1999-12-31', datetime.date(1999, 12, 31)], dtype='M8[D]') assert_equal(a[0], a[1]) a = np.array(['2000-01-01', datetime.date(2000, 1, 1)], dtype='M8[D]') assert_equal(a[0], a[1]) # Will fail if the date changes during the exact right moment a = np.array(['today', datetime.date.today()], dtype='M8[D]') assert_equal(a[0], a[1]) # datetime.datetime.now() returns local time, not UTC #a = np.array(['now', datetime.datetime.now()], dtype='M8[s]') #assert_equal(a[0], a[1]) # we can give a datetime.date time units assert_equal(np.array(datetime.date(1960, 3, 12), dtype='M8[s]'), np.array(np.datetime64('1960-03-12T00:00:00'))) def test_datetime_string_conversion(self): a = ['2011-03-16', '1920-01-01', '2013-05-19'] str_a = np.array(a, dtype='S') uni_a = np.array(a, dtype='U') dt_a = np.array(a, dtype='M') # String to datetime assert_equal(dt_a, str_a.astype('M')) assert_equal(dt_a.dtype, str_a.astype('M').dtype) dt_b = np.empty_like(dt_a) dt_b[...] = str_a assert_equal(dt_a, dt_b) # Datetime to string assert_equal(str_a, dt_a.astype('S0')) str_b = np.empty_like(str_a) str_b[...] = dt_a assert_equal(str_a, str_b) # Unicode to datetime assert_equal(dt_a, uni_a.astype('M')) assert_equal(dt_a.dtype, uni_a.astype('M').dtype) dt_b = np.empty_like(dt_a) dt_b[...] = uni_a assert_equal(dt_a, dt_b) # Datetime to unicode assert_equal(uni_a, dt_a.astype('U')) uni_b = np.empty_like(uni_a) uni_b[...] = dt_a assert_equal(uni_a, uni_b) # Datetime to long string - gh-9712 assert_equal(str_a, dt_a.astype((np.bytes_, 128))) str_b = np.empty(str_a.shape, dtype=(np.bytes_, 128)) str_b[...] = dt_a assert_equal(str_a, str_b) @pytest.mark.parametrize("time_dtype", ["m8[D]", "M8[Y]"]) def test_time_byteswapping(self, time_dtype): times = np.array(["2017", "NaT"], dtype=time_dtype) times_swapped = times.astype(times.dtype.newbyteorder()) assert_array_equal(times, times_swapped) unswapped = times_swapped.view(np.int64).newbyteorder() assert_array_equal(unswapped, times.view(np.int64)) @pytest.mark.parametrize(["time1", "time2"], [("M8[s]", "M8[D]"), ("m8[s]", "m8[ns]")]) def test_time_byteswapped_cast(self, time1, time2): dtype1 = np.dtype(time1) dtype2 = np.dtype(time2) times = np.array(["2017", "NaT"], dtype=dtype1) expected = times.astype(dtype2) # Test that every byte-swapping combination also returns the same # results (previous tests check that this comparison works fine). res = times.astype(dtype1.newbyteorder()).astype(dtype2) assert_array_equal(res, expected) res = times.astype(dtype2.newbyteorder()) assert_array_equal(res, expected) res = times.astype(dtype1.newbyteorder()).astype(dtype2.newbyteorder()) assert_array_equal(res, expected) @pytest.mark.parametrize("time_dtype", ["m8[D]", "M8[Y]"]) @pytest.mark.parametrize("str_dtype", ["U", "S"]) def test_datetime_conversions_byteorders(self, str_dtype, time_dtype): times = np.array(["2017", "NaT"], dtype=time_dtype) # Unfortunately, timedelta does not roundtrip: from_strings = np.array(["2017", "NaT"], dtype=str_dtype) to_strings = times.astype(str_dtype) # assume this is correct # Check that conversion from times to string works if src is swapped: times_swapped = times.astype(times.dtype.newbyteorder()) res = times_swapped.astype(str_dtype) assert_array_equal(res, to_strings) # And also if both are swapped: res = times_swapped.astype(to_strings.dtype.newbyteorder()) assert_array_equal(res, to_strings) # only destination is swapped: res = times.astype(to_strings.dtype.newbyteorder()) assert_array_equal(res, to_strings) # Check that conversion from string to times works if src is swapped: from_strings_swapped = from_strings.astype( from_strings.dtype.newbyteorder()) res = from_strings_swapped.astype(time_dtype) assert_array_equal(res, times) # And if both are swapped: res = from_strings_swapped.astype(times.dtype.newbyteorder()) assert_array_equal(res, times) # Only destination is swapped: res = from_strings.astype(times.dtype.newbyteorder()) assert_array_equal(res, times) def test_datetime_array_str(self): a = np.array(['2011-03-16', '1920-01-01', '2013-05-19'], dtype='M') assert_equal(str(a), "['2011-03-16' '1920-01-01' '2013-05-19']") a = np.array(['2011-03-16T13:55', '1920-01-01T03:12'], dtype='M') assert_equal(np.array2string(a, separator=', ', formatter={'datetime': lambda x: "'%s'" % np.datetime_as_string(x, timezone='UTC')}), "['2011-03-16T13:55Z', '1920-01-01T03:12Z']") # Check that one NaT doesn't corrupt subsequent entries a = np.array(['2010', 'NaT', '2030']).astype('M') assert_equal(str(a), "['2010' 'NaT' '2030']") def test_timedelta_array_str(self): a = np.array([-1, 0, 100], dtype='m') assert_equal(str(a), "[ -1 0 100]") a = np.array(['NaT', 'NaT'], dtype='m') assert_equal(str(a), "['NaT' 'NaT']") # Check right-alignment with NaTs a = np.array([-1, 'NaT', 0], dtype='m') assert_equal(str(a), "[ -1 'NaT' 0]") a = np.array([-1, 'NaT', 1234567], dtype='m') assert_equal(str(a), "[ -1 'NaT' 1234567]") # Test with other byteorder: a = np.array([-1, 'NaT', 1234567], dtype='>m') assert_equal(str(a), "[ -1 'NaT' 1234567]") a = np.array([-1, 'NaT', 1234567], dtype=''\np4\nNNNI-1\nI-1\nI0\n((dp5\n(S'us'\np6\n" + \ b"I1\nI1\nI1\ntp7\ntp8\ntp9\nb." assert_equal(pickle.loads(pkl), np.dtype('>M8[us]')) def test_setstate(self): "Verify that datetime dtype __setstate__ can handle bad arguments" dt = np.dtype('>M8[us]') assert_raises(ValueError, dt.__setstate__, (4, '>', None, None, None, -1, -1, 0, 1)) assert_(dt.__reduce__()[2] == np.dtype('>M8[us]').__reduce__()[2]) assert_raises(TypeError, dt.__setstate__, (4, '>', None, None, None, -1, -1, 0, ({}, 'xxx'))) assert_(dt.__reduce__()[2] == np.dtype('>M8[us]').__reduce__()[2]) def test_dtype_promotion(self): # datetime datetime computes the metadata gcd # timedelta timedelta computes the metadata gcd for mM in ['m', 'M']: assert_equal( np.promote_types(np.dtype(mM+'8[2Y]'), np.dtype(mM+'8[2Y]')), np.dtype(mM+'8[2Y]')) assert_equal( np.promote_types(np.dtype(mM+'8[12Y]'), np.dtype(mM+'8[15Y]')), np.dtype(mM+'8[3Y]')) assert_equal( np.promote_types(np.dtype(mM+'8[62M]'), np.dtype(mM+'8[24M]')), np.dtype(mM+'8[2M]')) assert_equal( np.promote_types(np.dtype(mM+'8[1W]'), np.dtype(mM+'8[2D]')), np.dtype(mM+'8[1D]')) assert_equal( np.promote_types(np.dtype(mM+'8[W]'), np.dtype(mM+'8[13s]')), np.dtype(mM+'8[s]')) assert_equal( np.promote_types(np.dtype(mM+'8[13W]'), np.dtype(mM+'8[49s]')), np.dtype(mM+'8[7s]')) # timedelta timedelta raises when there is no reasonable gcd assert_raises(TypeError, np.promote_types, np.dtype('m8[Y]'), np.dtype('m8[D]')) assert_raises(TypeError, np.promote_types, np.dtype('m8[M]'), np.dtype('m8[W]')) # timedelta and float cannot be safely cast with each other assert_raises(TypeError, np.promote_types, "float32", "m8") assert_raises(TypeError, np.promote_types, "m8", "float32") assert_raises(TypeError, np.promote_types, "uint64", "m8") assert_raises(TypeError, np.promote_types, "m8", "uint64") # timedelta timedelta may overflow with big unit ranges assert_raises(OverflowError, np.promote_types, np.dtype('m8[W]'), np.dtype('m8[fs]')) assert_raises(OverflowError, np.promote_types, np.dtype('m8[s]'), np.dtype('m8[as]')) def test_cast_overflow(self): # gh-4486 def cast(): numpy.datetime64("1971-01-01 00:00:00.000000000000000").astype("datetime64[%s]', 'timedelta64[%s]']) def test_isfinite_isinf_isnan_units(self, unit, dstr): '''check isfinite, isinf, isnan for all units of M, m dtypes ''' arr_val = [123, -321, "NaT"] arr = np.array(arr_val, dtype= dstr % unit) pos = np.array([True, True, False]) neg = np.array([False, False, True]) false = np.array([False, False, False]) assert_equal(np.isfinite(arr), pos) assert_equal(np.isinf(arr), false) assert_equal(np.isnan(arr), neg) def test_assert_equal(self): assert_raises(AssertionError, assert_equal, np.datetime64('nat'), np.timedelta64('nat')) def test_corecursive_input(self): # construct a co-recursive list a, b = [], [] a.append(b) b.append(a) obj_arr = np.array([None]) obj_arr[0] = a # At some point this caused a stack overflow (gh-11154). Now raises # ValueError since the nested list cannot be converted to a datetime. assert_raises(ValueError, obj_arr.astype, 'M8') assert_raises(ValueError, obj_arr.astype, 'm8') @pytest.mark.parametrize("shape", [(), (1,)]) def test_discovery_from_object_array(self, shape): arr = np.array("2020-10-10", dtype=object).reshape(shape) res = np.array("2020-10-10", dtype="M8").reshape(shape) assert res.dtype == np.dtype("M8[D]") assert_equal(arr.astype("M8"), res) arr[...] = np.bytes_("2020-10-10") # try a numpy string type assert_equal(arr.astype("M8"), res) arr = arr.astype("S") assert_equal(arr.astype("S").astype("M8"), res) @pytest.mark.parametrize("time_unit", [ "Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as", # compound units "10D", "2M", ]) def test_limit_symmetry(self, time_unit): """ Dates should have symmetric limits around the unix epoch at +/-np.int64 """ epoch = np.datetime64(0, time_unit) latest = np.datetime64(np.iinfo(np.int64).max, time_unit) earliest = np.datetime64(-np.iinfo(np.int64).max, time_unit) # above should not have overflowed assert earliest < epoch < latest @pytest.mark.parametrize("time_unit", [ "Y", "M", pytest.param("W", marks=pytest.mark.xfail(reason="gh-13197")), "D", "h", "m", "s", "ms", "us", "ns", "ps", "fs", "as", pytest.param("10D", marks=pytest.mark.xfail(reason="similar to gh-13197")), ]) @pytest.mark.parametrize("sign", [-1, 1]) def test_limit_str_roundtrip(self, time_unit, sign): """ Limits should roundtrip when converted to strings. This tests the conversion to and from npy_datetimestruct. """ # TODO: add absolute (gold standard) time span limit strings limit = np.datetime64(np.iinfo(np.int64).max * sign, time_unit) # Convert to string and back. Explicit unit needed since the day and # week reprs are not distinguishable. limit_via_str = np.datetime64(str(limit), time_unit) assert limit_via_str == limit class TestDateTimeData: def test_basic(self): a = np.array(['1980-03-23'], dtype=np.datetime64) assert_equal(np.datetime_data(a.dtype), ('D', 1)) def test_bytes(self): # byte units are converted to unicode dt = np.datetime64('2000', (b'ms', 5)) assert np.datetime_data(dt.dtype) == ('ms', 5) dt = np.datetime64('2000', b'5ms') assert np.datetime_data(dt.dtype) == ('ms', 5) def test_non_ascii(self): # μs is normalized to μ dt = np.datetime64('2000', ('μs', 5)) assert np.datetime_data(dt.dtype) == ('us', 5) dt = np.datetime64('2000', '5μs') assert np.datetime_data(dt.dtype) == ('us', 5) def test_comparisons_return_not_implemented(): # GH#17017 class custom: __array_priority__ = 10000 obj = custom() dt = np.datetime64('2000', 'ns') td = dt - dt for item in [dt, td]: assert item.__eq__(obj) is NotImplemented assert item.__ne__(obj) is NotImplemented assert item.__le__(obj) is NotImplemented assert item.__lt__(obj) is NotImplemented assert item.__ge__(obj) is NotImplemented assert item.__gt__(obj) is NotImplemented