""" test the scalar Timestamp """ import calendar from datetime import ( datetime, timedelta, timezone, ) import locale import time import unicodedata from dateutil.tz import ( tzlocal, tzutc, ) import numpy as np import pytest import pytz from pytz import utc from pandas._libs.tslibs.dtypes import NpyDatetimeUnit from pandas._libs.tslibs.timezones import ( dateutil_gettz as gettz, get_timezone, maybe_get_tz, tz_compare, ) from pandas.compat import IS64 from pandas.errors import OutOfBoundsDatetime import pandas.util._test_decorators as td from pandas import ( NaT, Timedelta, Timestamp, ) import pandas._testing as tm from pandas.tseries import offsets from pandas.tseries.frequencies import to_offset class TestTimestampProperties: def test_properties_business(self): freq = to_offset("B") ts = Timestamp("2017-10-01") assert ts.dayofweek == 6 assert ts.day_of_week == 6 assert ts.is_month_start # not a weekday assert not freq.is_month_start(ts) assert freq.is_month_start(ts + Timedelta(days=1)) assert not freq.is_quarter_start(ts) assert freq.is_quarter_start(ts + Timedelta(days=1)) ts = Timestamp("2017-09-30") assert ts.dayofweek == 5 assert ts.day_of_week == 5 assert ts.is_month_end assert not freq.is_month_end(ts) assert freq.is_month_end(ts - Timedelta(days=1)) assert ts.is_quarter_end assert not freq.is_quarter_end(ts) assert freq.is_quarter_end(ts - Timedelta(days=1)) @pytest.mark.parametrize( "attr, expected", [ ["year", 2014], ["month", 12], ["day", 31], ["hour", 23], ["minute", 59], ["second", 0], ["microsecond", 0], ["nanosecond", 0], ["dayofweek", 2], ["day_of_week", 2], ["quarter", 4], ["dayofyear", 365], ["day_of_year", 365], ["week", 1], ["daysinmonth", 31], ], ) @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_fields(self, attr, expected, tz): # GH 10050 # GH 13303 ts = Timestamp("2014-12-31 23:59:00", tz=tz) result = getattr(ts, attr) # that we are int like assert isinstance(result, int) assert result == expected @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_millisecond_raises(self, tz): ts = Timestamp("2014-12-31 23:59:00", tz=tz) msg = "'Timestamp' object has no attribute 'millisecond'" with pytest.raises(AttributeError, match=msg): ts.millisecond @pytest.mark.parametrize( "start", ["is_month_start", "is_quarter_start", "is_year_start"] ) @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_is_start(self, start, tz): ts = Timestamp("2014-01-01 00:00:00", tz=tz) assert getattr(ts, start) @pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"]) @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_is_end(self, end, tz): ts = Timestamp("2014-12-31 23:59:59", tz=tz) assert getattr(ts, end) # GH 12806 @pytest.mark.parametrize( "data", [Timestamp("2017-08-28 23:00:00"), Timestamp("2017-08-28 23:00:00", tz="EST")], ) # error: Unsupported operand types for + ("List[None]" and "List[str]") @pytest.mark.parametrize( "time_locale", [None] + tm.get_locales() # type: ignore[operator] ) def test_names(self, data, time_locale): # GH 17354 # Test .day_name(), .month_name if time_locale is None: expected_day = "Monday" expected_month = "August" else: with tm.set_locale(time_locale, locale.LC_TIME): expected_day = calendar.day_name[0].capitalize() expected_month = calendar.month_name[8].capitalize() result_day = data.day_name(time_locale) result_month = data.month_name(time_locale) # Work around https://github.com/pandas-dev/pandas/issues/22342 # different normalizations expected_day = unicodedata.normalize("NFD", expected_day) expected_month = unicodedata.normalize("NFD", expected_month) result_day = unicodedata.normalize("NFD", result_day) result_month = unicodedata.normalize("NFD", result_month) assert result_day == expected_day assert result_month == expected_month # Test NaT nan_ts = Timestamp(NaT) assert np.isnan(nan_ts.day_name(time_locale)) assert np.isnan(nan_ts.month_name(time_locale)) def test_is_leap_year(self, tz_naive_fixture): tz = tz_naive_fixture if not IS64 and tz == tzlocal(): # https://github.com/dateutil/dateutil/issues/197 pytest.skip( "tzlocal() on a 32 bit platform causes internal overflow errors" ) # GH 13727 dt = Timestamp("2000-01-01 00:00:00", tz=tz) assert dt.is_leap_year assert isinstance(dt.is_leap_year, bool) dt = Timestamp("1999-01-01 00:00:00", tz=tz) assert not dt.is_leap_year dt = Timestamp("2004-01-01 00:00:00", tz=tz) assert dt.is_leap_year dt = Timestamp("2100-01-01 00:00:00", tz=tz) assert not dt.is_leap_year def test_woy_boundary(self): # make sure weeks at year boundaries are correct d = datetime(2013, 12, 31) result = Timestamp(d).week expected = 1 # ISO standard assert result == expected d = datetime(2008, 12, 28) result = Timestamp(d).week expected = 52 # ISO standard assert result == expected d = datetime(2009, 12, 31) result = Timestamp(d).week expected = 53 # ISO standard assert result == expected d = datetime(2010, 1, 1) result = Timestamp(d).week expected = 53 # ISO standard assert result == expected d = datetime(2010, 1, 3) result = Timestamp(d).week expected = 53 # ISO standard assert result == expected result = np.array( [ Timestamp(datetime(*args)).week for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)] ] ) assert (result == [52, 52, 53, 53]).all() def test_resolution(self): # GH#21336, GH#21365 dt = Timestamp("2100-01-01 00:00:00.000000000") assert dt.resolution == Timedelta(nanoseconds=1) # Check that the attribute is available on the class, mirroring # the stdlib datetime behavior assert Timestamp.resolution == Timedelta(nanoseconds=1) assert dt.as_unit("us").resolution == Timedelta(microseconds=1) assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1) assert dt.as_unit("s").resolution == Timedelta(seconds=1) class TestTimestamp: def test_default_to_stdlib_utc(self): assert Timestamp.utcnow().tz is timezone.utc assert Timestamp.now("UTC").tz is timezone.utc assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc def test_tz(self): tstr = "2014-02-01 09:00" ts = Timestamp(tstr) local = ts.tz_localize("Asia/Tokyo") assert local.hour == 9 assert local == Timestamp(tstr, tz="Asia/Tokyo") conv = local.tz_convert("US/Eastern") assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern") assert conv.hour == 19 # preserves nanosecond ts = Timestamp(tstr) + offsets.Nano(5) local = ts.tz_localize("Asia/Tokyo") assert local.hour == 9 assert local.nanosecond == 5 conv = local.tz_convert("US/Eastern") assert conv.nanosecond == 5 assert conv.hour == 19 def test_utc_z_designator(self): assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is timezone.utc def test_asm8(self): np.random.seed(7_960_929) ns = [Timestamp.min._value, Timestamp.max._value, 1000] for n in ns: assert ( Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n ) assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8") def test_class_ops_pytz(self): def compare(x, y): assert int((Timestamp(x)._value - Timestamp(y)._value) / 1e9) == 0 compare(Timestamp.now(), datetime.now()) compare(Timestamp.now("UTC"), datetime.now(pytz.timezone("UTC"))) compare(Timestamp.utcnow(), datetime.utcnow()) compare(Timestamp.today(), datetime.today()) current_time = calendar.timegm(datetime.now().utctimetuple()) ts_utc = Timestamp.utcfromtimestamp(current_time) assert ts_utc.timestamp() == current_time compare( Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time) ) compare( # Support tz kwarg in Timestamp.fromtimestamp Timestamp.fromtimestamp(current_time, "UTC"), datetime.fromtimestamp(current_time, utc), ) compare( # Support tz kwarg in Timestamp.fromtimestamp Timestamp.fromtimestamp(current_time, tz="UTC"), datetime.fromtimestamp(current_time, utc), ) date_component = datetime.utcnow() time_component = (date_component + timedelta(minutes=10)).time() compare( Timestamp.combine(date_component, time_component), datetime.combine(date_component, time_component), ) def test_class_ops_dateutil(self): def compare(x, y): assert ( int( np.round(Timestamp(x)._value / 1e9) - np.round(Timestamp(y)._value / 1e9) ) == 0 ) compare(Timestamp.now(), datetime.now()) compare(Timestamp.now("UTC"), datetime.now(tzutc())) compare(Timestamp.utcnow(), datetime.utcnow()) compare(Timestamp.today(), datetime.today()) current_time = calendar.timegm(datetime.now().utctimetuple()) ts_utc = Timestamp.utcfromtimestamp(current_time) assert ts_utc.timestamp() == current_time compare( Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time) ) date_component = datetime.utcnow() time_component = (date_component + timedelta(minutes=10)).time() compare( Timestamp.combine(date_component, time_component), datetime.combine(date_component, time_component), ) def test_basics_nanos(self): val = np.int64(946_684_800_000_000_000).view("M8[ns]") stamp = Timestamp(val.view("i8") + 500) assert stamp.year == 2000 assert stamp.month == 1 assert stamp.microsecond == 0 assert stamp.nanosecond == 500 # GH 14415 val = np.iinfo(np.int64).min + 80_000_000_000_000 stamp = Timestamp(val) assert stamp.year == 1677 assert stamp.month == 9 assert stamp.day == 21 assert stamp.microsecond == 145224 assert stamp.nanosecond == 192 @pytest.mark.parametrize( "value, check_kwargs", [ [946688461000000000, {}], [946688461000000000 / 1000, {"unit": "us"}], [946688461000000000 / 1_000_000, {"unit": "ms"}], [946688461000000000 / 1_000_000_000, {"unit": "s"}], [10957, {"unit": "D", "h": 0}], [ (946688461000000000 + 500000) / 1000000000, {"unit": "s", "us": 499, "ns": 964}, ], [ (946688461000000000 + 500000000) / 1000000000, {"unit": "s", "us": 500000}, ], [(946688461000000000 + 500000) / 1000000, {"unit": "ms", "us": 500}], [(946688461000000000 + 500000) / 1000, {"unit": "us", "us": 500}], [(946688461000000000 + 500000000) / 1000000, {"unit": "ms", "us": 500000}], [946688461000000000 / 1000.0 + 5, {"unit": "us", "us": 5}], [946688461000000000 / 1000.0 + 5000, {"unit": "us", "us": 5000}], [946688461000000000 / 1000000.0 + 0.5, {"unit": "ms", "us": 500}], [946688461000000000 / 1000000.0 + 0.005, {"unit": "ms", "us": 5, "ns": 5}], [946688461000000000 / 1000000000.0 + 0.5, {"unit": "s", "us": 500000}], [10957 + 0.5, {"unit": "D", "h": 12}], ], ) def test_unit(self, value, check_kwargs): def check(value, unit=None, h=1, s=1, us=0, ns=0): stamp = Timestamp(value, unit=unit) assert stamp.year == 2000 assert stamp.month == 1 assert stamp.day == 1 assert stamp.hour == h if unit != "D": assert stamp.minute == 1 assert stamp.second == s assert stamp.microsecond == us else: assert stamp.minute == 0 assert stamp.second == 0 assert stamp.microsecond == 0 assert stamp.nanosecond == ns check(value, **check_kwargs) def test_roundtrip(self): # test value to string and back conversions # further test accessors base = Timestamp("20140101 00:00:00").as_unit("ns") result = Timestamp(base._value + Timedelta("5ms")._value) assert result == Timestamp(f"{base}.005000") assert result.microsecond == 5000 result = Timestamp(base._value + Timedelta("5us")._value) assert result == Timestamp(f"{base}.000005") assert result.microsecond == 5 result = Timestamp(base._value + Timedelta("5ns")._value) assert result == Timestamp(f"{base}.000000005") assert result.nanosecond == 5 assert result.microsecond == 0 result = Timestamp(base._value + Timedelta("6ms 5us")._value) assert result == Timestamp(f"{base}.006005") assert result.microsecond == 5 + 6 * 1000 result = Timestamp(base._value + Timedelta("200ms 5us")._value) assert result == Timestamp(f"{base}.200005") assert result.microsecond == 5 + 200 * 1000 def test_hash_equivalent(self): d = {datetime(2011, 1, 1): 5} stamp = Timestamp(datetime(2011, 1, 1)) assert d[stamp] == 5 @pytest.mark.parametrize( "timezone, year, month, day, hour", [["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]], ) def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour): # see gh-33931 test_timezone = gettz(timezone) transition_1 = Timestamp( year=year, month=month, day=day, hour=hour, minute=0, fold=0, tzinfo=test_timezone, ) transition_2 = Timestamp( year=year, month=month, day=day, hour=hour, minute=0, fold=1, tzinfo=test_timezone, ) assert hash(transition_1) == hash(transition_2) class TestTimestampNsOperations: def test_nanosecond_string_parsing(self): ts = Timestamp("2013-05-01 07:15:45.123456789") # GH 7878 expected_repr = "2013-05-01 07:15:45.123456789" expected_value = 1_367_392_545_123_456_789 assert ts._value == expected_value assert expected_repr in repr(ts) ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo") assert ts._value == expected_value - 9 * 3600 * 1_000_000_000 assert expected_repr in repr(ts) ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC") assert ts._value == expected_value assert expected_repr in repr(ts) ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern") assert ts._value == expected_value + 4 * 3600 * 1_000_000_000 assert expected_repr in repr(ts) # GH 10041 ts = Timestamp("20130501T071545.123456789") assert ts._value == expected_value assert expected_repr in repr(ts) def test_nanosecond_timestamp(self): # GH 7610 expected = 1_293_840_000_000_000_005 t = Timestamp("2011-01-01") + offsets.Nano(5) assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')" assert t._value == expected assert t.nanosecond == 5 t = Timestamp(t) assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')" assert t._value == expected assert t.nanosecond == 5 t = Timestamp("2011-01-01 00:00:00.000000005") assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')" assert t._value == expected assert t.nanosecond == 5 expected = 1_293_840_000_000_000_010 t = t + offsets.Nano(5) assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')" assert t._value == expected assert t.nanosecond == 10 t = Timestamp(t) assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')" assert t._value == expected assert t.nanosecond == 10 t = Timestamp("2011-01-01 00:00:00.000000010") assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')" assert t._value == expected assert t.nanosecond == 10 class TestTimestampToJulianDate: def test_compare_1700(self): r = Timestamp("1700-06-23").to_julian_date() assert r == 2_342_145.5 def test_compare_2000(self): r = Timestamp("2000-04-12").to_julian_date() assert r == 2_451_646.5 def test_compare_2100(self): r = Timestamp("2100-08-12").to_julian_date() assert r == 2_488_292.5 def test_compare_hour01(self): r = Timestamp("2000-08-12T01:00:00").to_julian_date() assert r == 2_451_768.5416666666666666 def test_compare_hour13(self): r = Timestamp("2000-08-12T13:00:00").to_julian_date() assert r == 2_451_769.0416666666666666 class TestTimestampConversion: def test_conversion(self): # GH#9255 ts = Timestamp("2000-01-01").as_unit("ns") result = ts.to_pydatetime() expected = datetime(2000, 1, 1) assert result == expected assert type(result) == type(expected) result = ts.to_datetime64() expected = np.datetime64(ts._value, "ns") assert result == expected assert type(result) == type(expected) assert result.dtype == expected.dtype def test_to_pydatetime_fold(self): # GH#45087 tzstr = "dateutil/usr/share/zoneinfo/America/Chicago" ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, tz=tzstr) dt = ts.to_pydatetime() assert dt.fold == 1 def test_to_pydatetime_nonzero_nano(self): ts = Timestamp("2011-01-01 9:00:00.123456789") # Warn the user of data loss (nanoseconds). with tm.assert_produces_warning(UserWarning): expected = datetime(2011, 1, 1, 9, 0, 0, 123456) result = ts.to_pydatetime() assert result == expected def test_timestamp_to_datetime(self): stamp = Timestamp("20090415", tz="US/Eastern") dtval = stamp.to_pydatetime() assert stamp == dtval assert stamp.tzinfo == dtval.tzinfo def test_timestamp_to_datetime_dateutil(self): stamp = Timestamp("20090415", tz="dateutil/US/Eastern") dtval = stamp.to_pydatetime() assert stamp == dtval assert stamp.tzinfo == dtval.tzinfo def test_timestamp_to_datetime_explicit_pytz(self): stamp = Timestamp("20090415", tz=pytz.timezone("US/Eastern")) dtval = stamp.to_pydatetime() assert stamp == dtval assert stamp.tzinfo == dtval.tzinfo @td.skip_if_windows def test_timestamp_to_datetime_explicit_dateutil(self): stamp = Timestamp("20090415", tz=gettz("US/Eastern")) dtval = stamp.to_pydatetime() assert stamp == dtval assert stamp.tzinfo == dtval.tzinfo def test_to_datetime_bijective(self): # Ensure that converting to datetime and back only loses precision # by going from nanoseconds to microseconds. exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning with tm.assert_produces_warning(exp_warning): pydt_max = Timestamp.max.to_pydatetime() assert ( Timestamp(pydt_max).as_unit("ns")._value / 1000 == Timestamp.max._value / 1000 ) exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning with tm.assert_produces_warning(exp_warning): pydt_min = Timestamp.min.to_pydatetime() # The next assertion can be enabled once GH#39221 is merged # assert pydt_min < Timestamp.min # this is bc nanos are dropped tdus = timedelta(microseconds=1) assert pydt_min + tdus > Timestamp.min assert ( Timestamp(pydt_min + tdus).as_unit("ns")._value / 1000 == Timestamp.min._value / 1000 ) def test_to_period_tz_warning(self): # GH#21333 make sure a warning is issued when timezone # info is lost ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern") with tm.assert_produces_warning(UserWarning): # warning that timezone info will be lost ts.to_period("D") def test_to_numpy_alias(self): # GH 24653: alias .to_numpy() for scalars ts = Timestamp(datetime.now()) assert ts.to_datetime64() == ts.to_numpy() # GH#44460 msg = "dtype and copy arguments are ignored" with pytest.raises(ValueError, match=msg): ts.to_numpy("M8[s]") with pytest.raises(ValueError, match=msg): ts.to_numpy(copy=True) class SubDatetime(datetime): pass @pytest.mark.parametrize( "lh,rh", [ (SubDatetime(2000, 1, 1), Timedelta(hours=1)), (Timedelta(hours=1), SubDatetime(2000, 1, 1)), ], ) def test_dt_subclass_add_timedelta(lh, rh): # GH#25851 # ensure that subclassed datetime works for # Timedelta operations result = lh + rh expected = SubDatetime(2000, 1, 1, 1) assert result == expected class TestNonNano: @pytest.fixture(params=["s", "ms", "us"]) def reso(self, request): return request.param @pytest.fixture def dt64(self, reso): # cases that are in-bounds for nanosecond, so we can compare against # the existing implementation. return np.datetime64("2016-01-01", reso) @pytest.fixture def ts(self, dt64): return Timestamp._from_dt64(dt64) @pytest.fixture def ts_tz(self, ts, tz_aware_fixture): tz = maybe_get_tz(tz_aware_fixture) return Timestamp._from_value_and_reso(ts._value, ts._creso, tz) def test_non_nano_construction(self, dt64, ts, reso): assert ts._value == dt64.view("i8") if reso == "s": assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value elif reso == "ms": assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value elif reso == "us": assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value def test_non_nano_fields(self, dt64, ts): alt = Timestamp(dt64) assert ts.year == alt.year assert ts.month == alt.month assert ts.day == alt.day assert ts.hour == ts.minute == ts.second == ts.microsecond == 0 assert ts.nanosecond == 0 assert ts.to_julian_date() == alt.to_julian_date() assert ts.weekday() == alt.weekday() assert ts.isoweekday() == alt.isoweekday() def test_start_end_fields(self, ts): assert ts.is_year_start assert ts.is_quarter_start assert ts.is_month_start assert not ts.is_year_end assert not ts.is_month_end assert not ts.is_month_end # 2016-01-01 is a Friday, so is year/quarter/month start with this freq assert ts.is_year_start assert ts.is_quarter_start assert ts.is_month_start assert not ts.is_year_end assert not ts.is_month_end assert not ts.is_month_end def test_day_name(self, dt64, ts): alt = Timestamp(dt64) assert ts.day_name() == alt.day_name() def test_month_name(self, dt64, ts): alt = Timestamp(dt64) assert ts.month_name() == alt.month_name() def test_tz_convert(self, ts): ts = Timestamp._from_value_and_reso(ts._value, ts._creso, utc) tz = pytz.timezone("US/Pacific") result = ts.tz_convert(tz) assert isinstance(result, Timestamp) assert result._creso == ts._creso assert tz_compare(result.tz, tz) def test_repr(self, dt64, ts): alt = Timestamp(dt64) assert str(ts) == str(alt) assert repr(ts) == repr(alt) def test_comparison(self, dt64, ts): alt = Timestamp(dt64) assert ts == dt64 assert dt64 == ts assert ts == alt assert alt == ts assert not ts != dt64 assert not dt64 != ts assert not ts != alt assert not alt != ts assert not ts < dt64 assert not dt64 < ts assert not ts < alt assert not alt < ts assert not ts > dt64 assert not dt64 > ts assert not ts > alt assert not alt > ts assert ts >= dt64 assert dt64 >= ts assert ts >= alt assert alt >= ts assert ts <= dt64 assert dt64 <= ts assert ts <= alt assert alt <= ts def test_cmp_cross_reso(self): # numpy gets this wrong because of silent overflow dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns] ts = Timestamp._from_dt64(dt64) # subtracting 3600*24 gives a datetime64 that _can_ fit inside the # nanosecond implementation bounds. other = Timestamp(dt64 - 3600 * 24).as_unit("ns") assert other < ts assert other.asm8 > ts.asm8 # <- numpy gets this wrong assert ts > other assert ts.asm8 < other.asm8 # <- numpy gets this wrong assert not other == ts assert ts != other @pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong") def test_cmp_cross_reso_reversed_dt64(self): dt64 = np.datetime64(106752, "D") # won't fit in M8[ns] ts = Timestamp._from_dt64(dt64) other = Timestamp(dt64 - 1) assert other.asm8 < ts def test_pickle(self, ts, tz_aware_fixture): tz = tz_aware_fixture tz = maybe_get_tz(tz) ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz) rt = tm.round_trip_pickle(ts) assert rt._creso == ts._creso assert rt == ts def test_normalize(self, dt64, ts): alt = Timestamp(dt64) result = ts.normalize() assert result._creso == ts._creso assert result == alt.normalize() def test_asm8(self, dt64, ts): rt = ts.asm8 assert rt == dt64 assert rt.dtype == dt64.dtype def test_to_numpy(self, dt64, ts): res = ts.to_numpy() assert res == dt64 assert res.dtype == dt64.dtype def test_to_datetime64(self, dt64, ts): res = ts.to_datetime64() assert res == dt64 assert res.dtype == dt64.dtype def test_timestamp(self, dt64, ts): alt = Timestamp(dt64) assert ts.timestamp() == alt.timestamp() def test_to_period(self, dt64, ts): alt = Timestamp(dt64) assert ts.to_period("D") == alt.to_period("D") @pytest.mark.parametrize( "td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")] ) def test_addsub_timedeltalike_non_nano(self, dt64, ts, td): exp_reso = max(ts._creso, Timedelta(td)._creso) result = ts - td expected = Timestamp(dt64) - td assert isinstance(result, Timestamp) assert result._creso == exp_reso assert result == expected result = ts + td expected = Timestamp(dt64) + td assert isinstance(result, Timestamp) assert result._creso == exp_reso assert result == expected result = td + ts expected = td + Timestamp(dt64) assert isinstance(result, Timestamp) assert result._creso == exp_reso assert result == expected def test_addsub_offset(self, ts_tz): # specifically non-Tick offset off = offsets.YearEnd(1) result = ts_tz + off assert isinstance(result, Timestamp) assert result._creso == ts_tz._creso if ts_tz.month == 12 and ts_tz.day == 31: assert result.year == ts_tz.year + 1 else: assert result.year == ts_tz.year assert result.day == 31 assert result.month == 12 assert tz_compare(result.tz, ts_tz.tz) result = ts_tz - off assert isinstance(result, Timestamp) assert result._creso == ts_tz._creso assert result.year == ts_tz.year - 1 assert result.day == 31 assert result.month == 12 assert tz_compare(result.tz, ts_tz.tz) def test_sub_datetimelike_mismatched_reso(self, ts_tz): # case with non-lossy rounding ts = ts_tz # choose a unit for `other` that doesn't match ts_tz's; # this construction ensures we get cases with other._creso < ts._creso # and cases with other._creso > ts._creso unit = { NpyDatetimeUnit.NPY_FR_us.value: "ms", NpyDatetimeUnit.NPY_FR_ms.value: "s", NpyDatetimeUnit.NPY_FR_s.value: "us", }[ts._creso] other = ts.as_unit(unit) assert other._creso != ts._creso result = ts - other assert isinstance(result, Timedelta) assert result._value == 0 assert result._creso == max(ts._creso, other._creso) result = other - ts assert isinstance(result, Timedelta) assert result._value == 0 assert result._creso == max(ts._creso, other._creso) if ts._creso < other._creso: # Case where rounding is lossy other2 = other + Timedelta._from_value_and_reso(1, other._creso) exp = ts.as_unit(other.unit) - other2 res = ts - other2 assert res == exp assert res._creso == max(ts._creso, other._creso) res = other2 - ts assert res == -exp assert res._creso == max(ts._creso, other._creso) else: ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso) exp = ts2 - other.as_unit(ts2.unit) res = ts2 - other assert res == exp assert res._creso == max(ts._creso, other._creso) res = other - ts2 assert res == -exp assert res._creso == max(ts._creso, other._creso) def test_sub_timedeltalike_mismatched_reso(self, ts_tz): # case with non-lossy rounding ts = ts_tz # choose a unit for `other` that doesn't match ts_tz's; # this construction ensures we get cases with other._creso < ts._creso # and cases with other._creso > ts._creso unit = { NpyDatetimeUnit.NPY_FR_us.value: "ms", NpyDatetimeUnit.NPY_FR_ms.value: "s", NpyDatetimeUnit.NPY_FR_s.value: "us", }[ts._creso] other = Timedelta(0).as_unit(unit) assert other._creso != ts._creso result = ts + other assert isinstance(result, Timestamp) assert result == ts assert result._creso == max(ts._creso, other._creso) result = other + ts assert isinstance(result, Timestamp) assert result == ts assert result._creso == max(ts._creso, other._creso) if ts._creso < other._creso: # Case where rounding is lossy other2 = other + Timedelta._from_value_and_reso(1, other._creso) exp = ts.as_unit(other.unit) + other2 res = ts + other2 assert res == exp assert res._creso == max(ts._creso, other._creso) res = other2 + ts assert res == exp assert res._creso == max(ts._creso, other._creso) else: ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso) exp = ts2 + other.as_unit(ts2.unit) res = ts2 + other assert res == exp assert res._creso == max(ts._creso, other._creso) res = other + ts2 assert res == exp assert res._creso == max(ts._creso, other._creso) def test_addition_doesnt_downcast_reso(self): # https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413 ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us") td = Timedelta(microseconds=1).as_unit("us") res = ts + td assert res._creso == ts._creso def test_sub_timedelta64_mismatched_reso(self, ts_tz): ts = ts_tz res = ts + np.timedelta64(1, "ns") exp = ts.as_unit("ns") + np.timedelta64(1, "ns") assert exp == res assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value def test_min(self, ts): assert ts.min <= ts assert ts.min._creso == ts._creso assert ts.min._value == NaT._value + 1 def test_max(self, ts): assert ts.max >= ts assert ts.max._creso == ts._creso assert ts.max._value == np.iinfo(np.int64).max def test_resolution(self, ts): expected = Timedelta._from_value_and_reso(1, ts._creso) result = ts.resolution assert result == expected assert result._creso == expected._creso def test_out_of_ns_bounds(self): # https://github.com/pandas-dev/pandas/issues/51060 result = Timestamp(-52700112000, unit="s") assert result == Timestamp("0300-01-01") assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s") def test_timestamp_class_min_max_resolution(): # when accessed on the class (as opposed to an instance), we default # to nanoseconds assert Timestamp.min == Timestamp(NaT._value + 1) assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value assert Timestamp.max == Timestamp(np.iinfo(np.int64).max) assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value assert Timestamp.resolution == Timedelta(1) assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value class TestAsUnit: def test_as_unit(self): ts = Timestamp("1970-01-01").as_unit("ns") assert ts.unit == "ns" assert ts.as_unit("ns") is ts res = ts.as_unit("us") assert res._value == ts._value // 1000 assert res._creso == NpyDatetimeUnit.NPY_FR_us.value rt = res.as_unit("ns") assert rt._value == ts._value assert rt._creso == ts._creso res = ts.as_unit("ms") assert res._value == ts._value // 1_000_000 assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value rt = res.as_unit("ns") assert rt._value == ts._value assert rt._creso == ts._creso res = ts.as_unit("s") assert res._value == ts._value // 1_000_000_000 assert res._creso == NpyDatetimeUnit.NPY_FR_s.value rt = res.as_unit("ns") assert rt._value == ts._value assert rt._creso == ts._creso def test_as_unit_overflows(self): # microsecond that would be just out of bounds for nano us = 9223372800000000 ts = Timestamp._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value, None) msg = "Cannot cast 2262-04-12 00:00:00 to unit='ns' without overflow" with pytest.raises(OutOfBoundsDatetime, match=msg): ts.as_unit("ns") res = ts.as_unit("ms") assert res._value == us // 1000 assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value def test_as_unit_rounding(self): ts = Timestamp(1_500_000) # i.e. 1500 microseconds res = ts.as_unit("ms") expected = Timestamp(1_000_000) # i.e. 1 millisecond assert res == expected assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value assert res._value == 1 with pytest.raises(ValueError, match="Cannot losslessly convert units"): ts.as_unit("ms", round_ok=False) def test_as_unit_non_nano(self): # case where we are going neither to nor from nano ts = Timestamp("1970-01-02").as_unit("ms") assert ts.year == 1970 assert ts.month == 1 assert ts.day == 2 assert ts.hour == ts.minute == ts.second == ts.microsecond == ts.nanosecond == 0 res = ts.as_unit("s") assert res._value == 24 * 3600 assert res.year == 1970 assert res.month == 1 assert res.day == 2 assert ( res.hour == res.minute == res.second == res.microsecond == res.nanosecond == 0 ) def test_delimited_date(): # https://github.com/pandas-dev/pandas/issues/50231 with tm.assert_produces_warning(None): result = Timestamp("13-01-2000") expected = Timestamp(2000, 1, 13) assert result == expected def test_utctimetuple(): # GH 32174 ts = Timestamp("2000-01-01", tz="UTC") result = ts.utctimetuple() expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0)) assert result == expected def test_negative_dates(): # https://github.com/pandas-dev/pandas/issues/50787 ts = Timestamp("-2000-01-01") msg = ( "^strftime not yet supported on Timestamps which are outside the range of " "Python's standard library. For now, please call the components you need " r"\(such as `.year` and `.month`\) and construct your string from there.$" ) with pytest.raises(NotImplementedError, match=msg): ts.strftime("%Y")