ai-content-maker/.venv/Lib/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py

1121 lines
38 KiB
Python
Raw Permalink Normal View History

2024-05-03 04:18:51 +03:00
""" test the scalar Timestamp """
import calendar
from datetime import (
datetime,
timedelta,
)
import locale
import pickle
import unicodedata
from dateutil.tz import tzutc
import numpy as np
import pytest
import pytz
from pytz import (
timezone,
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.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
class TestTimestampProperties:
def test_freq_deprecation(self):
# GH#41586
msg = "The 'freq' argument in Timestamp is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
# warning issued at construction
ts = Timestamp("2021-06-01", freq="D")
ts2 = Timestamp("2021-06-01", freq="B")
msg = "Timestamp.freq is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
# warning issued at attribute lookup
ts.freq
for per in ["month", "quarter", "year"]:
for side in ["start", "end"]:
attr = f"is_{per}_{side}"
with tm.assert_produces_warning(FutureWarning, match=msg):
getattr(ts2, attr)
# is_(month|quarter|year)_(start|end) does _not_ issue a warning
# with freq="D" bc the result will be unaffected by the deprecation
with tm.assert_produces_warning(None):
getattr(ts, attr)
@pytest.mark.filterwarnings("ignore:The 'freq' argument:FutureWarning")
@pytest.mark.filterwarnings("ignore:Timestamp.freq is deprecated:FutureWarning")
def test_properties_business(self):
ts = Timestamp("2017-10-01", freq="B")
control = Timestamp("2017-10-01")
assert ts.dayofweek == 6
assert ts.day_of_week == 6
assert not ts.is_month_start # not a weekday
assert not ts.freq.is_month_start(ts)
assert ts.freq.is_month_start(ts + Timedelta(days=1))
assert not ts.is_quarter_start # not a weekday
assert not ts.freq.is_quarter_start(ts)
assert ts.freq.is_quarter_start(ts + Timedelta(days=1))
# Control case: non-business is month/qtr start
assert control.is_month_start
assert control.is_quarter_start
ts = Timestamp("2017-09-30", freq="B")
control = Timestamp("2017-09-30")
assert ts.dayofweek == 5
assert ts.day_of_week == 5
assert not ts.is_month_end # not a weekday
assert not ts.freq.is_month_end(ts)
assert ts.freq.is_month_end(ts - Timedelta(days=1))
assert not ts.is_quarter_end # not a weekday
assert not ts.freq.is_quarter_end(ts)
assert ts.freq.is_quarter_end(ts - Timedelta(days=1))
# Control case: non-business is month/qtr start
assert control.is_month_end
assert control.is_quarter_end
@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() or []) # 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
# 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")
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)
class TestTimestamp:
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 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(timezone("UTC")))
compare(Timestamp.utcnow(), datetime.utcnow())
compare(Timestamp.today(), datetime.today())
current_time = calendar.timegm(datetime.now().utctimetuple())
msg = "timezone-aware Timestamp with UTC"
with tm.assert_produces_warning(FutureWarning, match=msg):
# GH#22451
ts_utc = Timestamp.utcfromtimestamp(current_time)
compare(
ts_utc,
datetime.utcfromtimestamp(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())
msg = "timezone-aware Timestamp with UTC"
with tm.assert_produces_warning(FutureWarning, match=msg):
# GH#22451
ts_utc = Timestamp.utcfromtimestamp(current_time)
compare(
ts_utc,
datetime.utcfromtimestamp(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")
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)
def test_tz_conversion_freq(self, tz_naive_fixture):
# GH25241
with tm.assert_produces_warning(FutureWarning, match="freq"):
t1 = Timestamp("2019-01-01 10:00", freq="H")
assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq
with tm.assert_produces_warning(FutureWarning, match="freq"):
t2 = Timestamp("2019-01-02 12:00", tz="UTC", freq="T")
assert t2.tz_convert(tz="UTC").freq == t2.freq
def test_pickle_freq_no_warning(self):
# GH#41949 we don't want a warning on unpickling
with tm.assert_produces_warning(FutureWarning, match="freq"):
ts = Timestamp("2019-01-01 10:00", freq="H")
out = pickle.dumps(ts)
with tm.assert_produces_warning(None):
res = pickle.loads(out)
assert res._freq == ts._freq
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")
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).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).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._reso, tz)
def test_non_nano_construction(self, dt64, ts, reso):
assert ts.value == dt64.view("i8")
if reso == "s":
assert ts._reso == NpyDatetimeUnit.NPY_FR_s.value
elif reso == "ms":
assert ts._reso == NpyDatetimeUnit.NPY_FR_ms.value
elif reso == "us":
assert ts._reso == 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
freq = offsets.BDay()
ts._set_freq(freq)
# 2016-01-01 is a Friday, so is year/quarter/month start with this freq
msg = "Timestamp.freq is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
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._reso, utc)
tz = pytz.timezone("US/Pacific")
result = ts.tz_convert(tz)
assert isinstance(result, Timestamp)
assert result._reso == ts._reso
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)
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._reso, tz)
rt = tm.round_trip_pickle(ts)
assert rt._reso == ts._reso
assert rt == ts
def test_normalize(self, dt64, ts):
alt = Timestamp(dt64)
result = ts.normalize()
assert result._reso == ts._reso
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):
result = ts - td
expected = Timestamp(dt64) - td
assert isinstance(result, Timestamp)
assert result._reso == ts._reso
assert result == expected
result = ts + td
expected = Timestamp(dt64) + td
assert isinstance(result, Timestamp)
assert result._reso == ts._reso
assert result == expected
result = td + ts
expected = td + Timestamp(dt64)
assert isinstance(result, Timestamp)
assert result._reso == ts._reso
assert result == expected
@pytest.mark.xfail(reason="tz_localize not yet implemented for non-nano")
def test_addsub_offset(self, ts_tz):
# specifically non-Tick offset
off = offsets.YearBegin(1)
result = ts_tz + off
assert isinstance(result, Timestamp)
assert result._reso == ts_tz._reso
# If ts_tz is ever on the last day of the year, the year would be
# incremented by one
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._reso == ts_tz._reso
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._reso < ts._reso
# and cases with other._reso > ts._reso
unit = {
NpyDatetimeUnit.NPY_FR_us.value: "ms",
NpyDatetimeUnit.NPY_FR_ms.value: "s",
NpyDatetimeUnit.NPY_FR_s.value: "us",
}[ts._reso]
other = ts._as_unit(unit)
assert other._reso != ts._reso
result = ts - other
assert isinstance(result, Timedelta)
assert result.value == 0
assert result._reso == min(ts._reso, other._reso)
result = other - ts
assert isinstance(result, Timedelta)
assert result.value == 0
assert result._reso == min(ts._reso, other._reso)
msg = "Timestamp subtraction with mismatched resolutions"
if ts._reso < other._reso:
# Case where rounding is lossy
other2 = other + Timedelta._from_value_and_reso(1, other._reso)
with pytest.raises(ValueError, match=msg):
ts - other2
with pytest.raises(ValueError, match=msg):
other2 - ts
else:
ts2 = ts + Timedelta._from_value_and_reso(1, ts._reso)
with pytest.raises(ValueError, match=msg):
ts2 - other
with pytest.raises(ValueError, match=msg):
other - ts2
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._reso < ts._reso
# and cases with other._reso > ts._reso
unit = {
NpyDatetimeUnit.NPY_FR_us.value: "ms",
NpyDatetimeUnit.NPY_FR_ms.value: "s",
NpyDatetimeUnit.NPY_FR_s.value: "us",
}[ts._reso]
other = Timedelta(0)._as_unit(unit)
assert other._reso != ts._reso
result = ts + other
assert isinstance(result, Timestamp)
assert result == ts
assert result._reso == min(ts._reso, other._reso)
result = other + ts
assert isinstance(result, Timestamp)
assert result == ts
assert result._reso == min(ts._reso, other._reso)
msg = "Timestamp addition with mismatched resolutions"
if ts._reso < other._reso:
# Case where rounding is lossy
other2 = other + Timedelta._from_value_and_reso(1, other._reso)
with pytest.raises(ValueError, match=msg):
ts + other2
with pytest.raises(ValueError, match=msg):
other2 + ts
else:
ts2 = ts + Timedelta._from_value_and_reso(1, ts._reso)
with pytest.raises(ValueError, match=msg):
ts2 + other
with pytest.raises(ValueError, match=msg):
other + ts2
msg = "Addition between Timestamp and Timedelta with mismatched resolutions"
with pytest.raises(ValueError, match=msg):
# With a mismatched td64 as opposed to Timedelta
ts + np.timedelta64(1, "ns")
def test_min(self, ts):
assert ts.min <= ts
assert ts.min._reso == ts._reso
assert ts.min.value == NaT.value + 1
def test_max(self, ts):
assert ts.max >= ts
assert ts.max._reso == ts._reso
assert ts.max.value == np.iinfo(np.int64).max
def test_resolution(self, ts):
expected = Timedelta._from_value_and_reso(1, ts._reso)
result = ts.resolution
assert result == expected
assert result._reso == expected._reso
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._reso == NpyDatetimeUnit.NPY_FR_ns.value
assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
assert Timestamp.max._reso == NpyDatetimeUnit.NPY_FR_ns.value
assert Timestamp.resolution == Timedelta(1)
assert Timestamp.resolution._reso == NpyDatetimeUnit.NPY_FR_ns.value
class TestAsUnit:
def test_as_unit(self):
ts = Timestamp("1970-01-01")
assert ts._as_unit("ns") is ts
res = ts._as_unit("us")
assert res.value == ts.value // 1000
assert res._reso == NpyDatetimeUnit.NPY_FR_us.value
rt = res._as_unit("ns")
assert rt.value == ts.value
assert rt._reso == ts._reso
res = ts._as_unit("ms")
assert res.value == ts.value // 1_000_000
assert res._reso == NpyDatetimeUnit.NPY_FR_ms.value
rt = res._as_unit("ns")
assert rt.value == ts.value
assert rt._reso == ts._reso
res = ts._as_unit("s")
assert res.value == ts.value // 1_000_000_000
assert res._reso == NpyDatetimeUnit.NPY_FR_s.value
rt = res._as_unit("ns")
assert rt.value == ts.value
assert rt._reso == ts._reso
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._reso == 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._reso == 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
)