|
22 | 22 | from pandas._libs.tslibs.timezones import (
|
23 | 23 | dateutil_gettz as gettz,
|
24 | 24 | get_timezone,
|
| 25 | + maybe_get_tz, |
25 | 26 | tz_compare,
|
26 | 27 | )
|
27 | 28 | from pandas.errors import OutOfBoundsDatetime
|
@@ -712,6 +713,11 @@ def dt64(self, reso):
|
712 | 713 | def ts(self, dt64):
|
713 | 714 | return Timestamp._from_dt64(dt64)
|
714 | 715 |
|
| 716 | + @pytest.fixture |
| 717 | + def ts_tz(self, ts, tz_aware_fixture): |
| 718 | + tz = maybe_get_tz(tz_aware_fixture) |
| 719 | + return Timestamp._from_value_and_reso(ts.value, ts._reso, tz) |
| 720 | + |
715 | 721 | def test_non_nano_construction(self, dt64, ts, reso):
|
716 | 722 | assert ts.value == dt64.view("i8")
|
717 | 723 |
|
@@ -893,6 +899,60 @@ def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
|
893 | 899 | assert result._reso == ts._reso
|
894 | 900 | assert result == expected
|
895 | 901 |
|
| 902 | + @pytest.mark.xfail(reason="tz_localize not yet implemented for non-nano") |
| 903 | + def test_addsub_offset(self, ts_tz): |
| 904 | + # specifically non-Tick offset |
| 905 | + off = offsets.YearBegin(1) |
| 906 | + result = ts_tz + off |
| 907 | + |
| 908 | + assert isinstance(result, Timestamp) |
| 909 | + assert result._reso == ts_tz._reso |
| 910 | + # If ts_tz is ever on the last day of the year, the year would be |
| 911 | + # incremented by one |
| 912 | + assert result.year == ts_tz.year |
| 913 | + assert result.day == 31 |
| 914 | + assert result.month == 12 |
| 915 | + assert tz_compare(result.tz, ts_tz.tz) |
| 916 | + |
| 917 | + def test_sub_datetimelike_mismatched_reso(self, ts_tz): |
| 918 | + # case with non-lossy rounding |
| 919 | + ts = ts_tz |
| 920 | + |
| 921 | + # choose a unit for `other` that doesn't match ts_tz's |
| 922 | + unit = { |
| 923 | + NpyDatetimeUnit.NPY_FR_us.value: "ms", |
| 924 | + NpyDatetimeUnit.NPY_FR_ms.value: "s", |
| 925 | + NpyDatetimeUnit.NPY_FR_s.value: "us", |
| 926 | + }[ts._reso] |
| 927 | + other = ts._as_unit(unit) |
| 928 | + assert other._reso != ts._reso |
| 929 | + |
| 930 | + result = ts - other |
| 931 | + assert isinstance(result, Timedelta) |
| 932 | + assert result.value == 0 |
| 933 | + assert result._reso == min(ts._reso, other._reso) |
| 934 | + |
| 935 | + result = other - ts |
| 936 | + assert isinstance(result, Timedelta) |
| 937 | + assert result.value == 0 |
| 938 | + assert result._reso == min(ts._reso, other._reso) |
| 939 | + |
| 940 | + # TODO: clarify in message that add/sub is allowed only when lossless? |
| 941 | + msg = "Cannot losslessly convert units" |
| 942 | + if ts._reso < other._reso: |
| 943 | + # Case where rounding is lossy |
| 944 | + other2 = other + Timedelta._from_value_and_reso(1, other._reso) |
| 945 | + with pytest.raises(ValueError, match=msg): |
| 946 | + ts - other2 |
| 947 | + with pytest.raises(ValueError, match=msg): |
| 948 | + other2 - ts |
| 949 | + else: |
| 950 | + ts2 = ts + Timedelta._from_value_and_reso(1, ts._reso) |
| 951 | + with pytest.raises(ValueError, match=msg): |
| 952 | + ts2 - other |
| 953 | + with pytest.raises(ValueError, match=msg): |
| 954 | + other - ts2 |
| 955 | + |
896 | 956 |
|
897 | 957 | class TestAsUnit:
|
898 | 958 | def test_as_unit(self):
|
|
0 commit comments