Brettapps commited on
Commit
2be5b35
·
verified ·
1 Parent(s): 44d1dff

Upload folder using huggingface_hub (part 14)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_asfreq.py +815 -0
  2. .venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_period.py +1208 -0
  3. .venv/lib/python3.13/site-packages/pandas/tests/scalar/test_na_scalar.py +334 -0
  4. .venv/lib/python3.13/site-packages/pandas/tests/scalar/test_nat.py +684 -0
  5. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/__init__.py +0 -0
  6. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py +0 -0
  7. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py +80 -0
  8. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py +195 -0
  9. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py +1251 -0
  10. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_constructors.py +795 -0
  11. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_formats.py +109 -0
  12. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py +740 -0
  13. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/__init__.py +0 -0
  14. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py +0 -0
  15. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py +86 -0
  16. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py +29 -0
  17. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py +211 -0
  18. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py +372 -0
  19. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py +34 -0
  20. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py +28 -0
  21. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py +83 -0
  22. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py +51 -0
  23. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py +364 -0
  24. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py +346 -0
  25. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py +313 -0
  26. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_constructors.py +1107 -0
  27. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_formats.py +206 -0
  28. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py +938 -0
  29. .venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timezones.py +25 -0
  30. .venv/lib/python3.13/site-packages/pandas/tests/series/__init__.py +0 -0
  31. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/__init__.py +0 -0
  32. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_cat_accessor.py +260 -0
  33. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_dt_accessor.py +832 -0
  34. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_list_accessor.py +146 -0
  35. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_sparse_accessor.py +9 -0
  36. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_str_accessor.py +26 -0
  37. .venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_struct_accessor.py +188 -0
  38. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/__init__.py +0 -0
  39. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_datetime.py +493 -0
  40. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_delitem.py +69 -0
  41. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_get.py +228 -0
  42. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_getitem.py +721 -0
  43. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_indexing.py +510 -0
  44. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_mask.py +69 -0
  45. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_set_value.py +49 -0
  46. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_setitem.py +1843 -0
  47. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_take.py +50 -0
  48. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_where.py +446 -0
  49. .venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_xs.py +82 -0
  50. .venv/lib/python3.13/site-packages/pandas/tests/series/methods/__init__.py +7 -0
.venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_asfreq.py ADDED
@@ -0,0 +1,815 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
4
+
5
+ from pandas import (
6
+ Period,
7
+ Timestamp,
8
+ offsets,
9
+ )
10
+ import pandas._testing as tm
11
+
12
+ bday_msg = "Period with BDay freq is deprecated"
13
+
14
+
15
+ class TestFreqConversion:
16
+ """Test frequency conversion of date objects"""
17
+
18
+ @pytest.mark.filterwarnings("ignore:Period with BDay:FutureWarning")
19
+ @pytest.mark.parametrize("freq", ["Y", "Q", "M", "W", "B", "D"])
20
+ def test_asfreq_near_zero(self, freq):
21
+ # GH#19643, GH#19650
22
+ per = Period("0001-01-01", freq=freq)
23
+ tup1 = (per.year, per.hour, per.day)
24
+
25
+ prev = per - 1
26
+ assert prev.ordinal == per.ordinal - 1
27
+ tup2 = (prev.year, prev.month, prev.day)
28
+ assert tup2 < tup1
29
+
30
+ def test_asfreq_near_zero_weekly(self):
31
+ # GH#19834
32
+ per1 = Period("0001-01-01", "D") + 6
33
+ per2 = Period("0001-01-01", "D") - 6
34
+ week1 = per1.asfreq("W")
35
+ week2 = per2.asfreq("W")
36
+ assert week1 != week2
37
+ assert week1.asfreq("D", "E") >= per1
38
+ assert week2.asfreq("D", "S") <= per2
39
+
40
+ def test_to_timestamp_out_of_bounds(self):
41
+ # GH#19643, used to incorrectly give Timestamp in 1754
42
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
43
+ per = Period("0001-01-01", freq="B")
44
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
45
+ per.to_timestamp()
46
+
47
+ def test_asfreq_corner(self):
48
+ val = Period(freq="Y", year=2007)
49
+ result1 = val.asfreq("5min")
50
+ result2 = val.asfreq("min")
51
+ expected = Period("2007-12-31 23:59", freq="min")
52
+ assert result1.ordinal == expected.ordinal
53
+ assert result1.freqstr == "5min"
54
+ assert result2.ordinal == expected.ordinal
55
+ assert result2.freqstr == "min"
56
+
57
+ def test_conv_annual(self):
58
+ # frequency conversion tests: from Annual Frequency
59
+ msg = INVALID_FREQ_ERR_MSG
60
+
61
+ ival_A = Period(freq="Y", year=2007)
62
+
63
+ ival_AJAN = Period(freq="Y-JAN", year=2007)
64
+ ival_AJUN = Period(freq="Y-JUN", year=2007)
65
+ ival_ANOV = Period(freq="Y-NOV", year=2007)
66
+
67
+ ival_A_to_Q_start = Period(freq="Q", year=2007, quarter=1)
68
+ ival_A_to_Q_end = Period(freq="Q", year=2007, quarter=4)
69
+ ival_A_to_M_start = Period(freq="M", year=2007, month=1)
70
+ ival_A_to_M_end = Period(freq="M", year=2007, month=12)
71
+ ival_A_to_W_start = Period(freq="W", year=2007, month=1, day=1)
72
+ ival_A_to_W_end = Period(freq="W", year=2007, month=12, day=31)
73
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
74
+ ival_A_to_B_start = Period(freq="B", year=2007, month=1, day=1)
75
+ ival_A_to_B_end = Period(freq="B", year=2007, month=12, day=31)
76
+ ival_A_to_D_start = Period(freq="D", year=2007, month=1, day=1)
77
+ ival_A_to_D_end = Period(freq="D", year=2007, month=12, day=31)
78
+ ival_A_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
79
+ ival_A_to_H_end = Period(freq="h", year=2007, month=12, day=31, hour=23)
80
+ ival_A_to_T_start = Period(
81
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
82
+ )
83
+ ival_A_to_T_end = Period(
84
+ freq="Min", year=2007, month=12, day=31, hour=23, minute=59
85
+ )
86
+ ival_A_to_S_start = Period(
87
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
88
+ )
89
+ ival_A_to_S_end = Period(
90
+ freq="s", year=2007, month=12, day=31, hour=23, minute=59, second=59
91
+ )
92
+
93
+ ival_AJAN_to_D_end = Period(freq="D", year=2007, month=1, day=31)
94
+ ival_AJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
95
+ ival_AJUN_to_D_end = Period(freq="D", year=2007, month=6, day=30)
96
+ ival_AJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
97
+ ival_ANOV_to_D_end = Period(freq="D", year=2007, month=11, day=30)
98
+ ival_ANOV_to_D_start = Period(freq="D", year=2006, month=12, day=1)
99
+
100
+ assert ival_A.asfreq("Q", "s") == ival_A_to_Q_start
101
+ assert ival_A.asfreq("Q", "e") == ival_A_to_Q_end
102
+ assert ival_A.asfreq("M", "s") == ival_A_to_M_start
103
+ assert ival_A.asfreq("M", "E") == ival_A_to_M_end
104
+ assert ival_A.asfreq("W", "s") == ival_A_to_W_start
105
+ assert ival_A.asfreq("W", "E") == ival_A_to_W_end
106
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
107
+ assert ival_A.asfreq("B", "s") == ival_A_to_B_start
108
+ assert ival_A.asfreq("B", "E") == ival_A_to_B_end
109
+ assert ival_A.asfreq("D", "s") == ival_A_to_D_start
110
+ assert ival_A.asfreq("D", "E") == ival_A_to_D_end
111
+ with pytest.raises(ValueError, match=msg):
112
+ assert ival_A.asfreq("H", "s") == ival_A_to_H_start
113
+ assert ival_A.asfreq("H", "E") == ival_A_to_H_end
114
+ assert ival_A.asfreq("min", "s") == ival_A_to_T_start
115
+ assert ival_A.asfreq("min", "E") == ival_A_to_T_end
116
+ with pytest.raises(ValueError, match=msg):
117
+ assert ival_A.asfreq("T", "s") == ival_A_to_T_start
118
+ assert ival_A.asfreq("T", "E") == ival_A_to_T_end
119
+ assert ival_A.asfreq("S", "S") == ival_A_to_S_start
120
+ assert ival_A.asfreq("S", "E") == ival_A_to_S_end
121
+
122
+ assert ival_AJAN.asfreq("D", "s") == ival_AJAN_to_D_start
123
+ assert ival_AJAN.asfreq("D", "E") == ival_AJAN_to_D_end
124
+
125
+ assert ival_AJUN.asfreq("D", "s") == ival_AJUN_to_D_start
126
+ assert ival_AJUN.asfreq("D", "E") == ival_AJUN_to_D_end
127
+
128
+ assert ival_ANOV.asfreq("D", "s") == ival_ANOV_to_D_start
129
+ assert ival_ANOV.asfreq("D", "E") == ival_ANOV_to_D_end
130
+
131
+ assert ival_A.asfreq("Y") == ival_A
132
+
133
+ def test_conv_quarterly(self):
134
+ # frequency conversion tests: from Quarterly Frequency
135
+
136
+ ival_Q = Period(freq="Q", year=2007, quarter=1)
137
+ ival_Q_end_of_year = Period(freq="Q", year=2007, quarter=4)
138
+
139
+ ival_QEJAN = Period(freq="Q-JAN", year=2007, quarter=1)
140
+ ival_QEJUN = Period(freq="Q-JUN", year=2007, quarter=1)
141
+
142
+ ival_Q_to_A = Period(freq="Y", year=2007)
143
+ ival_Q_to_M_start = Period(freq="M", year=2007, month=1)
144
+ ival_Q_to_M_end = Period(freq="M", year=2007, month=3)
145
+ ival_Q_to_W_start = Period(freq="W", year=2007, month=1, day=1)
146
+ ival_Q_to_W_end = Period(freq="W", year=2007, month=3, day=31)
147
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
148
+ ival_Q_to_B_start = Period(freq="B", year=2007, month=1, day=1)
149
+ ival_Q_to_B_end = Period(freq="B", year=2007, month=3, day=30)
150
+ ival_Q_to_D_start = Period(freq="D", year=2007, month=1, day=1)
151
+ ival_Q_to_D_end = Period(freq="D", year=2007, month=3, day=31)
152
+ ival_Q_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
153
+ ival_Q_to_H_end = Period(freq="h", year=2007, month=3, day=31, hour=23)
154
+ ival_Q_to_T_start = Period(
155
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
156
+ )
157
+ ival_Q_to_T_end = Period(
158
+ freq="Min", year=2007, month=3, day=31, hour=23, minute=59
159
+ )
160
+ ival_Q_to_S_start = Period(
161
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
162
+ )
163
+ ival_Q_to_S_end = Period(
164
+ freq="s", year=2007, month=3, day=31, hour=23, minute=59, second=59
165
+ )
166
+
167
+ ival_QEJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
168
+ ival_QEJAN_to_D_end = Period(freq="D", year=2006, month=4, day=30)
169
+
170
+ ival_QEJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
171
+ ival_QEJUN_to_D_end = Period(freq="D", year=2006, month=9, day=30)
172
+
173
+ assert ival_Q.asfreq("Y") == ival_Q_to_A
174
+ assert ival_Q_end_of_year.asfreq("Y") == ival_Q_to_A
175
+
176
+ assert ival_Q.asfreq("M", "s") == ival_Q_to_M_start
177
+ assert ival_Q.asfreq("M", "E") == ival_Q_to_M_end
178
+ assert ival_Q.asfreq("W", "s") == ival_Q_to_W_start
179
+ assert ival_Q.asfreq("W", "E") == ival_Q_to_W_end
180
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
181
+ assert ival_Q.asfreq("B", "s") == ival_Q_to_B_start
182
+ assert ival_Q.asfreq("B", "E") == ival_Q_to_B_end
183
+ assert ival_Q.asfreq("D", "s") == ival_Q_to_D_start
184
+ assert ival_Q.asfreq("D", "E") == ival_Q_to_D_end
185
+ assert ival_Q.asfreq("h", "s") == ival_Q_to_H_start
186
+ assert ival_Q.asfreq("h", "E") == ival_Q_to_H_end
187
+ assert ival_Q.asfreq("Min", "s") == ival_Q_to_T_start
188
+ assert ival_Q.asfreq("Min", "E") == ival_Q_to_T_end
189
+ assert ival_Q.asfreq("s", "s") == ival_Q_to_S_start
190
+ assert ival_Q.asfreq("s", "E") == ival_Q_to_S_end
191
+
192
+ assert ival_QEJAN.asfreq("D", "s") == ival_QEJAN_to_D_start
193
+ assert ival_QEJAN.asfreq("D", "E") == ival_QEJAN_to_D_end
194
+ assert ival_QEJUN.asfreq("D", "s") == ival_QEJUN_to_D_start
195
+ assert ival_QEJUN.asfreq("D", "E") == ival_QEJUN_to_D_end
196
+
197
+ assert ival_Q.asfreq("Q") == ival_Q
198
+
199
+ def test_conv_monthly(self):
200
+ # frequency conversion tests: from Monthly Frequency
201
+
202
+ ival_M = Period(freq="M", year=2007, month=1)
203
+ ival_M_end_of_year = Period(freq="M", year=2007, month=12)
204
+ ival_M_end_of_quarter = Period(freq="M", year=2007, month=3)
205
+ ival_M_to_A = Period(freq="Y", year=2007)
206
+ ival_M_to_Q = Period(freq="Q", year=2007, quarter=1)
207
+ ival_M_to_W_start = Period(freq="W", year=2007, month=1, day=1)
208
+ ival_M_to_W_end = Period(freq="W", year=2007, month=1, day=31)
209
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
210
+ ival_M_to_B_start = Period(freq="B", year=2007, month=1, day=1)
211
+ ival_M_to_B_end = Period(freq="B", year=2007, month=1, day=31)
212
+ ival_M_to_D_start = Period(freq="D", year=2007, month=1, day=1)
213
+ ival_M_to_D_end = Period(freq="D", year=2007, month=1, day=31)
214
+ ival_M_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
215
+ ival_M_to_H_end = Period(freq="h", year=2007, month=1, day=31, hour=23)
216
+ ival_M_to_T_start = Period(
217
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
218
+ )
219
+ ival_M_to_T_end = Period(
220
+ freq="Min", year=2007, month=1, day=31, hour=23, minute=59
221
+ )
222
+ ival_M_to_S_start = Period(
223
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
224
+ )
225
+ ival_M_to_S_end = Period(
226
+ freq="s", year=2007, month=1, day=31, hour=23, minute=59, second=59
227
+ )
228
+
229
+ assert ival_M.asfreq("Y") == ival_M_to_A
230
+ assert ival_M_end_of_year.asfreq("Y") == ival_M_to_A
231
+ assert ival_M.asfreq("Q") == ival_M_to_Q
232
+ assert ival_M_end_of_quarter.asfreq("Q") == ival_M_to_Q
233
+
234
+ assert ival_M.asfreq("W", "s") == ival_M_to_W_start
235
+ assert ival_M.asfreq("W", "E") == ival_M_to_W_end
236
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
237
+ assert ival_M.asfreq("B", "s") == ival_M_to_B_start
238
+ assert ival_M.asfreq("B", "E") == ival_M_to_B_end
239
+ assert ival_M.asfreq("D", "s") == ival_M_to_D_start
240
+ assert ival_M.asfreq("D", "E") == ival_M_to_D_end
241
+ assert ival_M.asfreq("h", "s") == ival_M_to_H_start
242
+ assert ival_M.asfreq("h", "E") == ival_M_to_H_end
243
+ assert ival_M.asfreq("Min", "s") == ival_M_to_T_start
244
+ assert ival_M.asfreq("Min", "E") == ival_M_to_T_end
245
+ assert ival_M.asfreq("s", "s") == ival_M_to_S_start
246
+ assert ival_M.asfreq("s", "E") == ival_M_to_S_end
247
+
248
+ assert ival_M.asfreq("M") == ival_M
249
+
250
+ def test_conv_weekly(self):
251
+ # frequency conversion tests: from Weekly Frequency
252
+ ival_W = Period(freq="W", year=2007, month=1, day=1)
253
+
254
+ ival_WSUN = Period(freq="W", year=2007, month=1, day=7)
255
+ ival_WSAT = Period(freq="W-SAT", year=2007, month=1, day=6)
256
+ ival_WFRI = Period(freq="W-FRI", year=2007, month=1, day=5)
257
+ ival_WTHU = Period(freq="W-THU", year=2007, month=1, day=4)
258
+ ival_WWED = Period(freq="W-WED", year=2007, month=1, day=3)
259
+ ival_WTUE = Period(freq="W-TUE", year=2007, month=1, day=2)
260
+ ival_WMON = Period(freq="W-MON", year=2007, month=1, day=1)
261
+
262
+ ival_WSUN_to_D_start = Period(freq="D", year=2007, month=1, day=1)
263
+ ival_WSUN_to_D_end = Period(freq="D", year=2007, month=1, day=7)
264
+ ival_WSAT_to_D_start = Period(freq="D", year=2006, month=12, day=31)
265
+ ival_WSAT_to_D_end = Period(freq="D", year=2007, month=1, day=6)
266
+ ival_WFRI_to_D_start = Period(freq="D", year=2006, month=12, day=30)
267
+ ival_WFRI_to_D_end = Period(freq="D", year=2007, month=1, day=5)
268
+ ival_WTHU_to_D_start = Period(freq="D", year=2006, month=12, day=29)
269
+ ival_WTHU_to_D_end = Period(freq="D", year=2007, month=1, day=4)
270
+ ival_WWED_to_D_start = Period(freq="D", year=2006, month=12, day=28)
271
+ ival_WWED_to_D_end = Period(freq="D", year=2007, month=1, day=3)
272
+ ival_WTUE_to_D_start = Period(freq="D", year=2006, month=12, day=27)
273
+ ival_WTUE_to_D_end = Period(freq="D", year=2007, month=1, day=2)
274
+ ival_WMON_to_D_start = Period(freq="D", year=2006, month=12, day=26)
275
+ ival_WMON_to_D_end = Period(freq="D", year=2007, month=1, day=1)
276
+
277
+ ival_W_end_of_year = Period(freq="W", year=2007, month=12, day=31)
278
+ ival_W_end_of_quarter = Period(freq="W", year=2007, month=3, day=31)
279
+ ival_W_end_of_month = Period(freq="W", year=2007, month=1, day=31)
280
+ ival_W_to_A = Period(freq="Y", year=2007)
281
+ ival_W_to_Q = Period(freq="Q", year=2007, quarter=1)
282
+ ival_W_to_M = Period(freq="M", year=2007, month=1)
283
+
284
+ if Period(freq="D", year=2007, month=12, day=31).weekday == 6:
285
+ ival_W_to_A_end_of_year = Period(freq="Y", year=2007)
286
+ else:
287
+ ival_W_to_A_end_of_year = Period(freq="Y", year=2008)
288
+
289
+ if Period(freq="D", year=2007, month=3, day=31).weekday == 6:
290
+ ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=1)
291
+ else:
292
+ ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=2)
293
+
294
+ if Period(freq="D", year=2007, month=1, day=31).weekday == 6:
295
+ ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=1)
296
+ else:
297
+ ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=2)
298
+
299
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
300
+ ival_W_to_B_start = Period(freq="B", year=2007, month=1, day=1)
301
+ ival_W_to_B_end = Period(freq="B", year=2007, month=1, day=5)
302
+ ival_W_to_D_start = Period(freq="D", year=2007, month=1, day=1)
303
+ ival_W_to_D_end = Period(freq="D", year=2007, month=1, day=7)
304
+ ival_W_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
305
+ ival_W_to_H_end = Period(freq="h", year=2007, month=1, day=7, hour=23)
306
+ ival_W_to_T_start = Period(
307
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
308
+ )
309
+ ival_W_to_T_end = Period(
310
+ freq="Min", year=2007, month=1, day=7, hour=23, minute=59
311
+ )
312
+ ival_W_to_S_start = Period(
313
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
314
+ )
315
+ ival_W_to_S_end = Period(
316
+ freq="s", year=2007, month=1, day=7, hour=23, minute=59, second=59
317
+ )
318
+
319
+ assert ival_W.asfreq("Y") == ival_W_to_A
320
+ assert ival_W_end_of_year.asfreq("Y") == ival_W_to_A_end_of_year
321
+
322
+ assert ival_W.asfreq("Q") == ival_W_to_Q
323
+ assert ival_W_end_of_quarter.asfreq("Q") == ival_W_to_Q_end_of_quarter
324
+
325
+ assert ival_W.asfreq("M") == ival_W_to_M
326
+ assert ival_W_end_of_month.asfreq("M") == ival_W_to_M_end_of_month
327
+
328
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
329
+ assert ival_W.asfreq("B", "s") == ival_W_to_B_start
330
+ assert ival_W.asfreq("B", "E") == ival_W_to_B_end
331
+
332
+ assert ival_W.asfreq("D", "s") == ival_W_to_D_start
333
+ assert ival_W.asfreq("D", "E") == ival_W_to_D_end
334
+
335
+ assert ival_WSUN.asfreq("D", "s") == ival_WSUN_to_D_start
336
+ assert ival_WSUN.asfreq("D", "E") == ival_WSUN_to_D_end
337
+ assert ival_WSAT.asfreq("D", "s") == ival_WSAT_to_D_start
338
+ assert ival_WSAT.asfreq("D", "E") == ival_WSAT_to_D_end
339
+ assert ival_WFRI.asfreq("D", "s") == ival_WFRI_to_D_start
340
+ assert ival_WFRI.asfreq("D", "E") == ival_WFRI_to_D_end
341
+ assert ival_WTHU.asfreq("D", "s") == ival_WTHU_to_D_start
342
+ assert ival_WTHU.asfreq("D", "E") == ival_WTHU_to_D_end
343
+ assert ival_WWED.asfreq("D", "s") == ival_WWED_to_D_start
344
+ assert ival_WWED.asfreq("D", "E") == ival_WWED_to_D_end
345
+ assert ival_WTUE.asfreq("D", "s") == ival_WTUE_to_D_start
346
+ assert ival_WTUE.asfreq("D", "E") == ival_WTUE_to_D_end
347
+ assert ival_WMON.asfreq("D", "s") == ival_WMON_to_D_start
348
+ assert ival_WMON.asfreq("D", "E") == ival_WMON_to_D_end
349
+
350
+ assert ival_W.asfreq("h", "s") == ival_W_to_H_start
351
+ assert ival_W.asfreq("h", "E") == ival_W_to_H_end
352
+ assert ival_W.asfreq("Min", "s") == ival_W_to_T_start
353
+ assert ival_W.asfreq("Min", "E") == ival_W_to_T_end
354
+ assert ival_W.asfreq("s", "s") == ival_W_to_S_start
355
+ assert ival_W.asfreq("s", "E") == ival_W_to_S_end
356
+
357
+ assert ival_W.asfreq("W") == ival_W
358
+
359
+ msg = INVALID_FREQ_ERR_MSG
360
+ with pytest.raises(ValueError, match=msg):
361
+ ival_W.asfreq("WK")
362
+
363
+ def test_conv_weekly_legacy(self):
364
+ # frequency conversion tests: from Weekly Frequency
365
+ msg = INVALID_FREQ_ERR_MSG
366
+ with pytest.raises(ValueError, match=msg):
367
+ Period(freq="WK", year=2007, month=1, day=1)
368
+
369
+ with pytest.raises(ValueError, match=msg):
370
+ Period(freq="WK-SAT", year=2007, month=1, day=6)
371
+ with pytest.raises(ValueError, match=msg):
372
+ Period(freq="WK-FRI", year=2007, month=1, day=5)
373
+ with pytest.raises(ValueError, match=msg):
374
+ Period(freq="WK-THU", year=2007, month=1, day=4)
375
+ with pytest.raises(ValueError, match=msg):
376
+ Period(freq="WK-WED", year=2007, month=1, day=3)
377
+ with pytest.raises(ValueError, match=msg):
378
+ Period(freq="WK-TUE", year=2007, month=1, day=2)
379
+ with pytest.raises(ValueError, match=msg):
380
+ Period(freq="WK-MON", year=2007, month=1, day=1)
381
+
382
+ def test_conv_business(self):
383
+ # frequency conversion tests: from Business Frequency"
384
+
385
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
386
+ ival_B = Period(freq="B", year=2007, month=1, day=1)
387
+ ival_B_end_of_year = Period(freq="B", year=2007, month=12, day=31)
388
+ ival_B_end_of_quarter = Period(freq="B", year=2007, month=3, day=30)
389
+ ival_B_end_of_month = Period(freq="B", year=2007, month=1, day=31)
390
+ ival_B_end_of_week = Period(freq="B", year=2007, month=1, day=5)
391
+
392
+ ival_B_to_A = Period(freq="Y", year=2007)
393
+ ival_B_to_Q = Period(freq="Q", year=2007, quarter=1)
394
+ ival_B_to_M = Period(freq="M", year=2007, month=1)
395
+ ival_B_to_W = Period(freq="W", year=2007, month=1, day=7)
396
+ ival_B_to_D = Period(freq="D", year=2007, month=1, day=1)
397
+ ival_B_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
398
+ ival_B_to_H_end = Period(freq="h", year=2007, month=1, day=1, hour=23)
399
+ ival_B_to_T_start = Period(
400
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
401
+ )
402
+ ival_B_to_T_end = Period(
403
+ freq="Min", year=2007, month=1, day=1, hour=23, minute=59
404
+ )
405
+ ival_B_to_S_start = Period(
406
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
407
+ )
408
+ ival_B_to_S_end = Period(
409
+ freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
410
+ )
411
+
412
+ assert ival_B.asfreq("Y") == ival_B_to_A
413
+ assert ival_B_end_of_year.asfreq("Y") == ival_B_to_A
414
+ assert ival_B.asfreq("Q") == ival_B_to_Q
415
+ assert ival_B_end_of_quarter.asfreq("Q") == ival_B_to_Q
416
+ assert ival_B.asfreq("M") == ival_B_to_M
417
+ assert ival_B_end_of_month.asfreq("M") == ival_B_to_M
418
+ assert ival_B.asfreq("W") == ival_B_to_W
419
+ assert ival_B_end_of_week.asfreq("W") == ival_B_to_W
420
+
421
+ assert ival_B.asfreq("D") == ival_B_to_D
422
+
423
+ assert ival_B.asfreq("h", "s") == ival_B_to_H_start
424
+ assert ival_B.asfreq("h", "E") == ival_B_to_H_end
425
+ assert ival_B.asfreq("Min", "s") == ival_B_to_T_start
426
+ assert ival_B.asfreq("Min", "E") == ival_B_to_T_end
427
+ assert ival_B.asfreq("s", "s") == ival_B_to_S_start
428
+ assert ival_B.asfreq("s", "E") == ival_B_to_S_end
429
+
430
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
431
+ assert ival_B.asfreq("B") == ival_B
432
+
433
+ def test_conv_daily(self):
434
+ # frequency conversion tests: from Business Frequency"
435
+
436
+ ival_D = Period(freq="D", year=2007, month=1, day=1)
437
+ ival_D_end_of_year = Period(freq="D", year=2007, month=12, day=31)
438
+ ival_D_end_of_quarter = Period(freq="D", year=2007, month=3, day=31)
439
+ ival_D_end_of_month = Period(freq="D", year=2007, month=1, day=31)
440
+ ival_D_end_of_week = Period(freq="D", year=2007, month=1, day=7)
441
+
442
+ ival_D_friday = Period(freq="D", year=2007, month=1, day=5)
443
+ ival_D_saturday = Period(freq="D", year=2007, month=1, day=6)
444
+ ival_D_sunday = Period(freq="D", year=2007, month=1, day=7)
445
+
446
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
447
+ ival_B_friday = Period(freq="B", year=2007, month=1, day=5)
448
+ ival_B_monday = Period(freq="B", year=2007, month=1, day=8)
449
+
450
+ ival_D_to_A = Period(freq="Y", year=2007)
451
+
452
+ ival_Deoq_to_AJAN = Period(freq="Y-JAN", year=2008)
453
+ ival_Deoq_to_AJUN = Period(freq="Y-JUN", year=2007)
454
+ ival_Deoq_to_ADEC = Period(freq="Y-DEC", year=2007)
455
+
456
+ ival_D_to_QEJAN = Period(freq="Q-JAN", year=2007, quarter=4)
457
+ ival_D_to_QEJUN = Period(freq="Q-JUN", year=2007, quarter=3)
458
+ ival_D_to_QEDEC = Period(freq="Q-DEC", year=2007, quarter=1)
459
+
460
+ ival_D_to_M = Period(freq="M", year=2007, month=1)
461
+ ival_D_to_W = Period(freq="W", year=2007, month=1, day=7)
462
+
463
+ ival_D_to_H_start = Period(freq="h", year=2007, month=1, day=1, hour=0)
464
+ ival_D_to_H_end = Period(freq="h", year=2007, month=1, day=1, hour=23)
465
+ ival_D_to_T_start = Period(
466
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
467
+ )
468
+ ival_D_to_T_end = Period(
469
+ freq="Min", year=2007, month=1, day=1, hour=23, minute=59
470
+ )
471
+ ival_D_to_S_start = Period(
472
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
473
+ )
474
+ ival_D_to_S_end = Period(
475
+ freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
476
+ )
477
+
478
+ assert ival_D.asfreq("Y") == ival_D_to_A
479
+
480
+ assert ival_D_end_of_quarter.asfreq("Y-JAN") == ival_Deoq_to_AJAN
481
+ assert ival_D_end_of_quarter.asfreq("Y-JUN") == ival_Deoq_to_AJUN
482
+ assert ival_D_end_of_quarter.asfreq("Y-DEC") == ival_Deoq_to_ADEC
483
+
484
+ assert ival_D_end_of_year.asfreq("Y") == ival_D_to_A
485
+ assert ival_D_end_of_quarter.asfreq("Q") == ival_D_to_QEDEC
486
+ assert ival_D.asfreq("Q-JAN") == ival_D_to_QEJAN
487
+ assert ival_D.asfreq("Q-JUN") == ival_D_to_QEJUN
488
+ assert ival_D.asfreq("Q-DEC") == ival_D_to_QEDEC
489
+ assert ival_D.asfreq("M") == ival_D_to_M
490
+ assert ival_D_end_of_month.asfreq("M") == ival_D_to_M
491
+ assert ival_D.asfreq("W") == ival_D_to_W
492
+ assert ival_D_end_of_week.asfreq("W") == ival_D_to_W
493
+
494
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
495
+ assert ival_D_friday.asfreq("B") == ival_B_friday
496
+ assert ival_D_saturday.asfreq("B", "s") == ival_B_friday
497
+ assert ival_D_saturday.asfreq("B", "E") == ival_B_monday
498
+ assert ival_D_sunday.asfreq("B", "s") == ival_B_friday
499
+ assert ival_D_sunday.asfreq("B", "E") == ival_B_monday
500
+
501
+ assert ival_D.asfreq("h", "s") == ival_D_to_H_start
502
+ assert ival_D.asfreq("h", "E") == ival_D_to_H_end
503
+ assert ival_D.asfreq("Min", "s") == ival_D_to_T_start
504
+ assert ival_D.asfreq("Min", "E") == ival_D_to_T_end
505
+ assert ival_D.asfreq("s", "s") == ival_D_to_S_start
506
+ assert ival_D.asfreq("s", "E") == ival_D_to_S_end
507
+
508
+ assert ival_D.asfreq("D") == ival_D
509
+
510
+ def test_conv_hourly(self):
511
+ # frequency conversion tests: from Hourly Frequency"
512
+
513
+ ival_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
514
+ ival_H_end_of_year = Period(freq="h", year=2007, month=12, day=31, hour=23)
515
+ ival_H_end_of_quarter = Period(freq="h", year=2007, month=3, day=31, hour=23)
516
+ ival_H_end_of_month = Period(freq="h", year=2007, month=1, day=31, hour=23)
517
+ ival_H_end_of_week = Period(freq="h", year=2007, month=1, day=7, hour=23)
518
+ ival_H_end_of_day = Period(freq="h", year=2007, month=1, day=1, hour=23)
519
+ ival_H_end_of_bus = Period(freq="h", year=2007, month=1, day=1, hour=23)
520
+
521
+ ival_H_to_A = Period(freq="Y", year=2007)
522
+ ival_H_to_Q = Period(freq="Q", year=2007, quarter=1)
523
+ ival_H_to_M = Period(freq="M", year=2007, month=1)
524
+ ival_H_to_W = Period(freq="W", year=2007, month=1, day=7)
525
+ ival_H_to_D = Period(freq="D", year=2007, month=1, day=1)
526
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
527
+ ival_H_to_B = Period(freq="B", year=2007, month=1, day=1)
528
+
529
+ ival_H_to_T_start = Period(
530
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0
531
+ )
532
+ ival_H_to_T_end = Period(
533
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=59
534
+ )
535
+ ival_H_to_S_start = Period(
536
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
537
+ )
538
+ ival_H_to_S_end = Period(
539
+ freq="s", year=2007, month=1, day=1, hour=0, minute=59, second=59
540
+ )
541
+
542
+ assert ival_H.asfreq("Y") == ival_H_to_A
543
+ assert ival_H_end_of_year.asfreq("Y") == ival_H_to_A
544
+ assert ival_H.asfreq("Q") == ival_H_to_Q
545
+ assert ival_H_end_of_quarter.asfreq("Q") == ival_H_to_Q
546
+ assert ival_H.asfreq("M") == ival_H_to_M
547
+ assert ival_H_end_of_month.asfreq("M") == ival_H_to_M
548
+ assert ival_H.asfreq("W") == ival_H_to_W
549
+ assert ival_H_end_of_week.asfreq("W") == ival_H_to_W
550
+ assert ival_H.asfreq("D") == ival_H_to_D
551
+ assert ival_H_end_of_day.asfreq("D") == ival_H_to_D
552
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
553
+ assert ival_H.asfreq("B") == ival_H_to_B
554
+ assert ival_H_end_of_bus.asfreq("B") == ival_H_to_B
555
+
556
+ assert ival_H.asfreq("Min", "s") == ival_H_to_T_start
557
+ assert ival_H.asfreq("Min", "E") == ival_H_to_T_end
558
+ assert ival_H.asfreq("s", "s") == ival_H_to_S_start
559
+ assert ival_H.asfreq("s", "E") == ival_H_to_S_end
560
+
561
+ assert ival_H.asfreq("h") == ival_H
562
+
563
+ def test_conv_minutely(self):
564
+ # frequency conversion tests: from Minutely Frequency"
565
+
566
+ ival_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
567
+ ival_T_end_of_year = Period(
568
+ freq="Min", year=2007, month=12, day=31, hour=23, minute=59
569
+ )
570
+ ival_T_end_of_quarter = Period(
571
+ freq="Min", year=2007, month=3, day=31, hour=23, minute=59
572
+ )
573
+ ival_T_end_of_month = Period(
574
+ freq="Min", year=2007, month=1, day=31, hour=23, minute=59
575
+ )
576
+ ival_T_end_of_week = Period(
577
+ freq="Min", year=2007, month=1, day=7, hour=23, minute=59
578
+ )
579
+ ival_T_end_of_day = Period(
580
+ freq="Min", year=2007, month=1, day=1, hour=23, minute=59
581
+ )
582
+ ival_T_end_of_bus = Period(
583
+ freq="Min", year=2007, month=1, day=1, hour=23, minute=59
584
+ )
585
+ ival_T_end_of_hour = Period(
586
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=59
587
+ )
588
+
589
+ ival_T_to_A = Period(freq="Y", year=2007)
590
+ ival_T_to_Q = Period(freq="Q", year=2007, quarter=1)
591
+ ival_T_to_M = Period(freq="M", year=2007, month=1)
592
+ ival_T_to_W = Period(freq="W", year=2007, month=1, day=7)
593
+ ival_T_to_D = Period(freq="D", year=2007, month=1, day=1)
594
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
595
+ ival_T_to_B = Period(freq="B", year=2007, month=1, day=1)
596
+ ival_T_to_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
597
+
598
+ ival_T_to_S_start = Period(
599
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0
600
+ )
601
+ ival_T_to_S_end = Period(
602
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=59
603
+ )
604
+
605
+ assert ival_T.asfreq("Y") == ival_T_to_A
606
+ assert ival_T_end_of_year.asfreq("Y") == ival_T_to_A
607
+ assert ival_T.asfreq("Q") == ival_T_to_Q
608
+ assert ival_T_end_of_quarter.asfreq("Q") == ival_T_to_Q
609
+ assert ival_T.asfreq("M") == ival_T_to_M
610
+ assert ival_T_end_of_month.asfreq("M") == ival_T_to_M
611
+ assert ival_T.asfreq("W") == ival_T_to_W
612
+ assert ival_T_end_of_week.asfreq("W") == ival_T_to_W
613
+ assert ival_T.asfreq("D") == ival_T_to_D
614
+ assert ival_T_end_of_day.asfreq("D") == ival_T_to_D
615
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
616
+ assert ival_T.asfreq("B") == ival_T_to_B
617
+ assert ival_T_end_of_bus.asfreq("B") == ival_T_to_B
618
+ assert ival_T.asfreq("h") == ival_T_to_H
619
+ assert ival_T_end_of_hour.asfreq("h") == ival_T_to_H
620
+
621
+ assert ival_T.asfreq("s", "s") == ival_T_to_S_start
622
+ assert ival_T.asfreq("s", "E") == ival_T_to_S_end
623
+
624
+ assert ival_T.asfreq("Min") == ival_T
625
+
626
+ def test_conv_secondly(self):
627
+ # frequency conversion tests: from Secondly Frequency"
628
+
629
+ ival_S = Period(freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=0)
630
+ ival_S_end_of_year = Period(
631
+ freq="s", year=2007, month=12, day=31, hour=23, minute=59, second=59
632
+ )
633
+ ival_S_end_of_quarter = Period(
634
+ freq="s", year=2007, month=3, day=31, hour=23, minute=59, second=59
635
+ )
636
+ ival_S_end_of_month = Period(
637
+ freq="s", year=2007, month=1, day=31, hour=23, minute=59, second=59
638
+ )
639
+ ival_S_end_of_week = Period(
640
+ freq="s", year=2007, month=1, day=7, hour=23, minute=59, second=59
641
+ )
642
+ ival_S_end_of_day = Period(
643
+ freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
644
+ )
645
+ ival_S_end_of_bus = Period(
646
+ freq="s", year=2007, month=1, day=1, hour=23, minute=59, second=59
647
+ )
648
+ ival_S_end_of_hour = Period(
649
+ freq="s", year=2007, month=1, day=1, hour=0, minute=59, second=59
650
+ )
651
+ ival_S_end_of_minute = Period(
652
+ freq="s", year=2007, month=1, day=1, hour=0, minute=0, second=59
653
+ )
654
+
655
+ ival_S_to_A = Period(freq="Y", year=2007)
656
+ ival_S_to_Q = Period(freq="Q", year=2007, quarter=1)
657
+ ival_S_to_M = Period(freq="M", year=2007, month=1)
658
+ ival_S_to_W = Period(freq="W", year=2007, month=1, day=7)
659
+ ival_S_to_D = Period(freq="D", year=2007, month=1, day=1)
660
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
661
+ ival_S_to_B = Period(freq="B", year=2007, month=1, day=1)
662
+ ival_S_to_H = Period(freq="h", year=2007, month=1, day=1, hour=0)
663
+ ival_S_to_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
664
+
665
+ assert ival_S.asfreq("Y") == ival_S_to_A
666
+ assert ival_S_end_of_year.asfreq("Y") == ival_S_to_A
667
+ assert ival_S.asfreq("Q") == ival_S_to_Q
668
+ assert ival_S_end_of_quarter.asfreq("Q") == ival_S_to_Q
669
+ assert ival_S.asfreq("M") == ival_S_to_M
670
+ assert ival_S_end_of_month.asfreq("M") == ival_S_to_M
671
+ assert ival_S.asfreq("W") == ival_S_to_W
672
+ assert ival_S_end_of_week.asfreq("W") == ival_S_to_W
673
+ assert ival_S.asfreq("D") == ival_S_to_D
674
+ assert ival_S_end_of_day.asfreq("D") == ival_S_to_D
675
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
676
+ assert ival_S.asfreq("B") == ival_S_to_B
677
+ assert ival_S_end_of_bus.asfreq("B") == ival_S_to_B
678
+ assert ival_S.asfreq("h") == ival_S_to_H
679
+ assert ival_S_end_of_hour.asfreq("h") == ival_S_to_H
680
+ assert ival_S.asfreq("Min") == ival_S_to_T
681
+ assert ival_S_end_of_minute.asfreq("Min") == ival_S_to_T
682
+
683
+ assert ival_S.asfreq("s") == ival_S
684
+
685
+ def test_conv_microsecond(self):
686
+ # GH#31475 Avoid floating point errors dropping the start_time to
687
+ # before the beginning of the Period
688
+ per = Period("2020-01-30 15:57:27.576166", freq="us")
689
+ assert per.ordinal == 1580399847576166
690
+
691
+ start = per.start_time
692
+ expected = Timestamp("2020-01-30 15:57:27.576166")
693
+ assert start == expected
694
+ assert start._value == per.ordinal
695
+
696
+ def test_asfreq_mult(self):
697
+ # normal freq to mult freq
698
+ p = Period(freq="Y", year=2007)
699
+ # ordinal will not change
700
+ for freq in ["3Y", offsets.YearEnd(3)]:
701
+ result = p.asfreq(freq)
702
+ expected = Period("2007", freq="3Y")
703
+
704
+ assert result == expected
705
+ assert result.ordinal == expected.ordinal
706
+ assert result.freq == expected.freq
707
+ # ordinal will not change
708
+ for freq in ["3Y", offsets.YearEnd(3)]:
709
+ result = p.asfreq(freq, how="S")
710
+ expected = Period("2007", freq="3Y")
711
+
712
+ assert result == expected
713
+ assert result.ordinal == expected.ordinal
714
+ assert result.freq == expected.freq
715
+
716
+ # mult freq to normal freq
717
+ p = Period(freq="3Y", year=2007)
718
+ # ordinal will change because how=E is the default
719
+ for freq in ["Y", offsets.YearEnd()]:
720
+ result = p.asfreq(freq)
721
+ expected = Period("2009", freq="Y")
722
+
723
+ assert result == expected
724
+ assert result.ordinal == expected.ordinal
725
+ assert result.freq == expected.freq
726
+ # ordinal will not change
727
+ for freq in ["Y", offsets.YearEnd()]:
728
+ result = p.asfreq(freq, how="s")
729
+ expected = Period("2007", freq="Y")
730
+
731
+ assert result == expected
732
+ assert result.ordinal == expected.ordinal
733
+ assert result.freq == expected.freq
734
+
735
+ p = Period(freq="Y", year=2007)
736
+ for freq in ["2M", offsets.MonthEnd(2)]:
737
+ result = p.asfreq(freq)
738
+ expected = Period("2007-12", freq="2M")
739
+
740
+ assert result == expected
741
+ assert result.ordinal == expected.ordinal
742
+ assert result.freq == expected.freq
743
+ for freq in ["2M", offsets.MonthEnd(2)]:
744
+ result = p.asfreq(freq, how="s")
745
+ expected = Period("2007-01", freq="2M")
746
+
747
+ assert result == expected
748
+ assert result.ordinal == expected.ordinal
749
+ assert result.freq == expected.freq
750
+
751
+ p = Period(freq="3Y", year=2007)
752
+ for freq in ["2M", offsets.MonthEnd(2)]:
753
+ result = p.asfreq(freq)
754
+ expected = Period("2009-12", freq="2M")
755
+
756
+ assert result == expected
757
+ assert result.ordinal == expected.ordinal
758
+ assert result.freq == expected.freq
759
+ for freq in ["2M", offsets.MonthEnd(2)]:
760
+ result = p.asfreq(freq, how="s")
761
+ expected = Period("2007-01", freq="2M")
762
+
763
+ assert result == expected
764
+ assert result.ordinal == expected.ordinal
765
+ assert result.freq == expected.freq
766
+
767
+ def test_asfreq_combined(self):
768
+ # normal freq to combined freq
769
+ p = Period("2007", freq="h")
770
+
771
+ # ordinal will not change
772
+ expected = Period("2007", freq="25h")
773
+ for freq, how in zip(["1D1h", "1h1D"], ["E", "S"]):
774
+ result = p.asfreq(freq, how=how)
775
+ assert result == expected
776
+ assert result.ordinal == expected.ordinal
777
+ assert result.freq == expected.freq
778
+
779
+ # combined freq to normal freq
780
+ p1 = Period(freq="1D1h", year=2007)
781
+ p2 = Period(freq="1h1D", year=2007)
782
+
783
+ # ordinal will change because how=E is the default
784
+ result1 = p1.asfreq("h")
785
+ result2 = p2.asfreq("h")
786
+ expected = Period("2007-01-02", freq="h")
787
+ assert result1 == expected
788
+ assert result1.ordinal == expected.ordinal
789
+ assert result1.freq == expected.freq
790
+ assert result2 == expected
791
+ assert result2.ordinal == expected.ordinal
792
+ assert result2.freq == expected.freq
793
+
794
+ # ordinal will not change
795
+ result1 = p1.asfreq("h", how="S")
796
+ result2 = p2.asfreq("h", how="S")
797
+ expected = Period("2007-01-01", freq="h")
798
+ assert result1 == expected
799
+ assert result1.ordinal == expected.ordinal
800
+ assert result1.freq == expected.freq
801
+ assert result2 == expected
802
+ assert result2.ordinal == expected.ordinal
803
+ assert result2.freq == expected.freq
804
+
805
+ def test_asfreq_MS(self):
806
+ initial = Period("2013")
807
+
808
+ assert initial.asfreq(freq="M", how="S") == Period("2013-01", "M")
809
+
810
+ msg = INVALID_FREQ_ERR_MSG
811
+ with pytest.raises(ValueError, match=msg):
812
+ initial.asfreq(freq="MS", how="S")
813
+
814
+ with pytest.raises(ValueError, match=msg):
815
+ Period("2013-01", "MS")
.venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_period.py ADDED
@@ -0,0 +1,1208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ date,
3
+ datetime,
4
+ timedelta,
5
+ )
6
+ import re
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from pandas._libs.tslibs import (
12
+ iNaT,
13
+ to_offset,
14
+ )
15
+ from pandas._libs.tslibs.ccalendar import (
16
+ DAYS,
17
+ MONTHS,
18
+ )
19
+ from pandas._libs.tslibs.parsing import DateParseError
20
+ from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
21
+ from pandas.compat import PY314
22
+ from pandas.errors import (
23
+ OutOfBoundsDatetime,
24
+ Pandas4Warning,
25
+ )
26
+
27
+ from pandas import (
28
+ NaT,
29
+ Period,
30
+ Timedelta,
31
+ Timestamp,
32
+ offsets,
33
+ )
34
+ import pandas._testing as tm
35
+
36
+ bday_msg = "Period with BDay freq is deprecated"
37
+
38
+
39
+ class TestPeriodDisallowedFreqs:
40
+ @pytest.mark.parametrize(
41
+ "freq, freq_msg",
42
+ [
43
+ (offsets.BYearBegin(), "BYearBegin"),
44
+ (offsets.YearBegin(2), "YearBegin"),
45
+ (offsets.QuarterBegin(startingMonth=12), "QuarterBegin"),
46
+ (offsets.BusinessMonthEnd(2), "BusinessMonthEnd"),
47
+ ],
48
+ )
49
+ def test_offsets_not_supported(self, freq, freq_msg):
50
+ # GH#55785
51
+ msg = re.escape(f"{freq} is not supported as period frequency")
52
+ with pytest.raises(ValueError, match=msg):
53
+ Period(year=2014, freq=freq)
54
+
55
+ def test_custom_business_day_freq_raises(self):
56
+ # GH#52534
57
+ msg = "C is not supported as period frequency"
58
+ with pytest.raises(ValueError, match=msg):
59
+ Period("2023-04-10", freq="C")
60
+ msg = f"{offsets.CustomBusinessDay().base} is not supported as period frequency"
61
+ with pytest.raises(ValueError, match=msg):
62
+ Period("2023-04-10", freq=offsets.CustomBusinessDay())
63
+
64
+ def test_invalid_frequency_error_message(self):
65
+ msg = "WOM-1MON is not supported as period frequency"
66
+ with pytest.raises(ValueError, match=msg):
67
+ Period("2012-01-02", freq="WOM-1MON")
68
+
69
+ def test_invalid_frequency_period_error_message(self):
70
+ msg = "Invalid frequency: ME"
71
+ with pytest.raises(ValueError, match=msg):
72
+ Period("2012-01-02", freq="ME")
73
+
74
+
75
+ class TestPeriodConstruction:
76
+ def test_from_td64nat_raises(self):
77
+ # GH#44507
78
+ td = NaT.to_numpy("m8[ns]")
79
+
80
+ msg = "Value must be Period, string, integer, or datetime"
81
+ with pytest.raises(ValueError, match=msg):
82
+ Period(td)
83
+
84
+ with pytest.raises(ValueError, match=msg):
85
+ Period(td, freq="D")
86
+
87
+ def test_construction(self):
88
+ i1 = Period("1/1/2005", freq="M")
89
+ i2 = Period("Jan 2005")
90
+
91
+ assert i1 == i2
92
+
93
+ # GH#54105 - Period can be confusingly instantiated with lowercase freq
94
+ # TODO: raise in the future an error when passing lowercase freq
95
+ i1 = Period("2005", freq="Y")
96
+ i2 = Period("2005")
97
+
98
+ assert i1 == i2
99
+
100
+ i4 = Period("2005", freq="M")
101
+ assert i1 != i4
102
+
103
+ i1 = Period.now(freq="Q")
104
+ i2 = Period(datetime.now(), freq="Q")
105
+
106
+ assert i1 == i2
107
+
108
+ # Pass in freq as a keyword argument sometimes as a test for
109
+ # https://github.com/pandas-dev/pandas/issues/53369
110
+ i1 = Period.now(freq="D")
111
+ i2 = Period(datetime.now(), freq="D")
112
+ i3 = Period.now(offsets.Day())
113
+
114
+ assert i1 == i2
115
+ assert i1 == i3
116
+
117
+ i1 = Period("1982", freq="min")
118
+ msg = "'MIN' is deprecated and will be removed in a future version."
119
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
120
+ i2 = Period("1982", freq="MIN")
121
+ assert i1 == i2
122
+
123
+ i1 = Period(year=2005, month=3, day=1, freq="D")
124
+ i2 = Period("3/1/2005", freq="D")
125
+ assert i1 == i2
126
+
127
+ msg = "'d' is deprecated and will be removed in a future version."
128
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
129
+ i3 = Period(year=2005, month=3, day=1, freq="d")
130
+ assert i1 == i3
131
+
132
+ i1 = Period("2007-01-01 09:00:00.001")
133
+ expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq="ms")
134
+ assert i1 == expected
135
+
136
+ expected = Period("2007-01-01 09:00:00.001", freq="ms")
137
+ assert i1 == expected
138
+
139
+ i1 = Period("2007-01-01 09:00:00.00101")
140
+ expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq="us")
141
+ assert i1 == expected
142
+
143
+ expected = Period("2007-01-01 09:00:00.00101", freq="us")
144
+ assert i1 == expected
145
+
146
+ msg = "Must supply freq for ordinal value"
147
+ with pytest.raises(ValueError, match=msg):
148
+ Period(ordinal=200701)
149
+
150
+ msg = "Invalid frequency: X"
151
+ with pytest.raises(ValueError, match=msg):
152
+ Period("2007-1-1", freq="X")
153
+
154
+ def test_tuple_freq_disallowed(self):
155
+ # GH#34703 tuple freq disallowed
156
+ with pytest.raises(TypeError, match="pass as a string instead"):
157
+ Period("1982", freq=("Min", 1))
158
+
159
+ with pytest.raises(TypeError, match="pass as a string instead"):
160
+ Period("2006-12-31", ("w", 1))
161
+
162
+ def test_construction_from_timestamp_nanos(self):
163
+ # GH#46811 don't drop nanos from Timestamp
164
+ ts = Timestamp("2022-04-20 09:23:24.123456789")
165
+ per = Period(ts, freq="ns")
166
+
167
+ # should losslessly round-trip, not lose the 789
168
+ rt = per.to_timestamp()
169
+ assert rt == ts
170
+
171
+ # same thing but from a datetime64 object
172
+ dt64 = ts.asm8
173
+ per2 = Period(dt64, freq="ns")
174
+ rt2 = per2.to_timestamp()
175
+ assert rt2.asm8 == dt64
176
+
177
+ @pytest.mark.parametrize("freq", ["ms", "us", "ns"])
178
+ def test_construction_from_min_timestamp(self, freq):
179
+ # GH-63278
180
+ ts = Timestamp(Timestamp.min.value, unit=freq)
181
+ per = Period(ts, freq=freq)
182
+
183
+ # pandas.errors.OutOfBoundsDatetime: Out of bounds nanosecond timestamp:
184
+ # -290308-12-21 19:59:05
185
+ # rt = per.to_timestamp()
186
+
187
+ assert per.year == ts.year
188
+ assert per.month == ts.month
189
+ assert per.day == ts.day
190
+ assert per.hour == ts.hour
191
+ assert per.minute == ts.minute
192
+ assert per.second == ts.second
193
+
194
+ def test_construction_bday(self):
195
+ # Biz day construction, roll forward if non-weekday
196
+ with tm.assert_produces_warning(
197
+ Pandas4Warning, match="'b' is deprecated", raise_on_extra_warnings=False
198
+ ):
199
+ i1 = Period("3/10/12", freq="B")
200
+ i2 = Period("3/10/12", freq="D")
201
+ assert i1 == i2.asfreq("B")
202
+ i2 = Period("3/11/12", freq="D")
203
+ assert i1 == i2.asfreq("B")
204
+ i2 = Period("3/12/12", freq="D")
205
+ assert i1 == i2.asfreq("B")
206
+
207
+ i3 = Period("3/10/12", freq="b")
208
+ assert i1 == i3
209
+
210
+ i1 = Period(year=2012, month=3, day=10, freq="B")
211
+ i2 = Period("3/12/12", freq="B")
212
+ assert i1 == i2
213
+
214
+ def test_construction_quarter(self):
215
+ i1 = Period(year=2005, quarter=1, freq="Q")
216
+ i2 = Period("1/1/2005", freq="Q")
217
+ assert i1 == i2
218
+
219
+ i1 = Period(year=2005, quarter=3, freq="Q")
220
+ i2 = Period("9/1/2005", freq="Q")
221
+ assert i1 == i2
222
+
223
+ i1 = Period("2005Q1")
224
+ i2 = Period(year=2005, quarter=1, freq="Q")
225
+ i3 = Period("2005q1")
226
+ assert i1 == i2
227
+ assert i1 == i3
228
+
229
+ i1 = Period("05Q1")
230
+ assert i1 == i2
231
+ lower = Period("05q1")
232
+ assert i1 == lower
233
+
234
+ i1 = Period("1Q2005")
235
+ assert i1 == i2
236
+ lower = Period("1q2005")
237
+ assert i1 == lower
238
+
239
+ i1 = Period("1Q05")
240
+ assert i1 == i2
241
+ lower = Period("1q05")
242
+ assert i1 == lower
243
+
244
+ i1 = Period("4Q1984")
245
+ assert i1.year == 1984
246
+ lower = Period("4q1984")
247
+ assert i1 == lower
248
+
249
+ def test_construction_month(self):
250
+ expected = Period("2007-01", freq="M")
251
+ i1 = Period("200701", freq="M")
252
+ assert i1 == expected
253
+
254
+ i1 = Period("200701", freq="M")
255
+ assert i1 == expected
256
+
257
+ i1 = Period(200701, freq="M")
258
+ assert i1 == expected
259
+
260
+ i1 = Period(ordinal=200701, freq="M")
261
+ assert i1.year == 18695
262
+
263
+ i1 = Period(datetime(2007, 1, 1), freq="M")
264
+ i2 = Period("200701", freq="M")
265
+ assert i1 == i2
266
+
267
+ i1 = Period(date(2007, 1, 1), freq="M")
268
+ i2 = Period(datetime(2007, 1, 1), freq="M")
269
+ i3 = Period(np.datetime64("2007-01-01"), freq="M")
270
+ i4 = Period("2007-01-01 00:00:00", freq="M")
271
+ i5 = Period("2007-01-01 00:00:00.000", freq="M")
272
+ assert i1 == i2
273
+ assert i1 == i3
274
+ assert i1 == i4
275
+ assert i1 == i5
276
+
277
+ def test_period_constructor_offsets(self):
278
+ assert Period("1/1/2005", freq=offsets.MonthEnd()) == Period(
279
+ "1/1/2005", freq="M"
280
+ )
281
+ assert Period("2005", freq=offsets.YearEnd()) == Period("2005", freq="Y")
282
+ assert Period("2005", freq=offsets.MonthEnd()) == Period("2005", freq="M")
283
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
284
+ assert Period("3/10/12", freq=offsets.BusinessDay()) == Period(
285
+ "3/10/12", freq="B"
286
+ )
287
+ assert Period("3/10/12", freq=offsets.Day()) == Period("3/10/12", freq="D")
288
+
289
+ assert Period(
290
+ year=2005, quarter=1, freq=offsets.QuarterEnd(startingMonth=12)
291
+ ) == Period(year=2005, quarter=1, freq="Q")
292
+ assert Period(
293
+ year=2005, quarter=2, freq=offsets.QuarterEnd(startingMonth=12)
294
+ ) == Period(year=2005, quarter=2, freq="Q")
295
+
296
+ assert Period(year=2005, month=3, day=1, freq=offsets.Day()) == Period(
297
+ year=2005, month=3, day=1, freq="D"
298
+ )
299
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
300
+ assert Period(year=2012, month=3, day=10, freq=offsets.BDay()) == Period(
301
+ year=2012, month=3, day=10, freq="B"
302
+ )
303
+
304
+ expected = Period("2005-03-01", freq="3D")
305
+ assert Period(year=2005, month=3, day=1, freq=offsets.Day(3)) == expected
306
+ assert Period(year=2005, month=3, day=1, freq="3D") == expected
307
+
308
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
309
+ assert Period(year=2012, month=3, day=10, freq=offsets.BDay(3)) == Period(
310
+ year=2012, month=3, day=10, freq="3B"
311
+ )
312
+
313
+ assert Period(200701, freq=offsets.MonthEnd()) == Period(200701, freq="M")
314
+
315
+ i1 = Period(ordinal=200701, freq=offsets.MonthEnd())
316
+ i2 = Period(ordinal=200701, freq="M")
317
+ assert i1 == i2
318
+ assert i1.year == 18695
319
+ assert i2.year == 18695
320
+
321
+ i1 = Period(datetime(2007, 1, 1), freq="M")
322
+ i2 = Period("200701", freq="M")
323
+ assert i1 == i2
324
+
325
+ i1 = Period(date(2007, 1, 1), freq="M")
326
+ i2 = Period(datetime(2007, 1, 1), freq="M")
327
+ i3 = Period(np.datetime64("2007-01-01"), freq="M")
328
+ i4 = Period("2007-01-01 00:00:00", freq="M")
329
+ i5 = Period("2007-01-01 00:00:00.000", freq="M")
330
+ assert i1 == i2
331
+ assert i1 == i3
332
+ assert i1 == i4
333
+ assert i1 == i5
334
+
335
+ i1 = Period("2007-01-01 09:00:00.001")
336
+ expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1000), freq="ms")
337
+ assert i1 == expected
338
+
339
+ expected = Period("2007-01-01 09:00:00.001", freq="ms")
340
+ assert i1 == expected
341
+
342
+ i1 = Period("2007-01-01 09:00:00.00101")
343
+ expected = Period(datetime(2007, 1, 1, 9, 0, 0, 1010), freq="us")
344
+ assert i1 == expected
345
+
346
+ expected = Period("2007-01-01 09:00:00.00101", freq="us")
347
+ assert i1 == expected
348
+
349
+ def test_invalid_arguments(self):
350
+ msg = "Must supply freq for datetime value"
351
+ with pytest.raises(ValueError, match=msg):
352
+ Period(datetime.now())
353
+ with pytest.raises(ValueError, match=msg):
354
+ Period(datetime.now().date())
355
+
356
+ msg = "Value must be Period, string, integer, or datetime"
357
+ with pytest.raises(ValueError, match=msg):
358
+ Period(1.6, freq="D")
359
+ msg = "Ordinal must be an integer"
360
+ with pytest.raises(ValueError, match=msg):
361
+ Period(ordinal=1.6, freq="D")
362
+ msg = "Only value or ordinal but not both should be given but not both"
363
+ with pytest.raises(ValueError, match=msg):
364
+ Period(ordinal=2, value=1, freq="D")
365
+
366
+ msg = "If value is None, freq cannot be None"
367
+ with pytest.raises(ValueError, match=msg):
368
+ Period(month=1)
369
+
370
+ msg = '^Given date string "-2000" not likely a datetime$'
371
+ with pytest.raises(ValueError, match=msg):
372
+ Period("-2000", "Y")
373
+ if PY314:
374
+ msg = "day 0 must be in range 1..31 for month 1 in year 1: 0"
375
+ else:
376
+ msg = "day is out of range for month"
377
+ with pytest.raises(DateParseError, match=msg):
378
+ Period("0", "Y")
379
+ msg = "Unknown datetime string format, unable to parse"
380
+ with pytest.raises(DateParseError, match=msg):
381
+ Period("1/1/-2000", "Y")
382
+
383
+ def test_constructor_corner(self):
384
+ expected = Period("2007-01", freq="2M")
385
+ assert Period(year=2007, month=1, freq="2M") == expected
386
+
387
+ assert Period(None) is NaT
388
+
389
+ p = Period("2007-01-01", freq="D")
390
+
391
+ result = Period(p, freq="Y")
392
+ exp = Period("2007", freq="Y")
393
+ assert result == exp
394
+
395
+ def test_constructor_infer_freq(self):
396
+ p = Period("2007-01-01")
397
+ assert p.freq == "D"
398
+
399
+ p = Period("2007-01-01 07")
400
+ assert p.freq == "h"
401
+
402
+ p = Period("2007-01-01 07:10")
403
+ assert p.freq == "min"
404
+
405
+ p = Period("2007-01-01 07:10:15")
406
+ assert p.freq == "s"
407
+
408
+ p = Period("2007-01-01 07:10:15.123")
409
+ assert p.freq == "ms"
410
+
411
+ # We see that there are 6 digits after the decimal, so get microsecond
412
+ # even though they are all zeros.
413
+ p = Period("2007-01-01 07:10:15.123000")
414
+ assert p.freq == "us"
415
+
416
+ p = Period("2007-01-01 07:10:15.123400")
417
+ assert p.freq == "us"
418
+
419
+ def test_multiples(self):
420
+ result1 = Period("1989", freq="2Y")
421
+ result2 = Period("1989", freq="Y")
422
+ assert result1.ordinal == result2.ordinal
423
+ assert result1.freqstr == "2Y-DEC"
424
+ assert result2.freqstr == "Y-DEC"
425
+ assert result1.freq == offsets.YearEnd(2)
426
+ assert result2.freq == offsets.YearEnd()
427
+
428
+ assert (result1 + 1).ordinal == result1.ordinal + 2
429
+ assert (1 + result1).ordinal == result1.ordinal + 2
430
+ assert (result1 - 1).ordinal == result2.ordinal - 2
431
+ assert (-1 + result1).ordinal == result2.ordinal - 2
432
+
433
+ @pytest.mark.parametrize("month", MONTHS)
434
+ def test_period_cons_quarterly(self, month):
435
+ # bugs in scikits.timeseries
436
+ freq = f"Q-{month}"
437
+ exp = Period("1989Q3", freq=freq)
438
+ assert "1989Q3" in str(exp)
439
+ stamp = exp.to_timestamp("D", how="end")
440
+ p = Period(stamp, freq=freq)
441
+ assert p == exp
442
+
443
+ stamp = exp.to_timestamp("3D", how="end")
444
+ p = Period(stamp, freq=freq)
445
+ assert p == exp
446
+
447
+ @pytest.mark.parametrize("month", MONTHS)
448
+ def test_period_cons_annual(self, month):
449
+ # bugs in scikits.timeseries
450
+ freq = f"Y-{month}"
451
+ exp = Period("1989", freq=freq)
452
+ stamp = exp.to_timestamp("D", how="end") + timedelta(days=30)
453
+ p = Period(stamp, freq=freq)
454
+
455
+ assert p == exp + 1
456
+ assert isinstance(p, Period)
457
+
458
+ @pytest.mark.parametrize("day", DAYS)
459
+ @pytest.mark.parametrize("num", range(10, 17))
460
+ def test_period_cons_weekly(self, num, day):
461
+ daystr = f"2011-02-{num}"
462
+ freq = f"W-{day}"
463
+
464
+ result = Period(daystr, freq=freq)
465
+ expected = Period(daystr, freq="D").asfreq(freq)
466
+ assert result == expected
467
+ assert isinstance(result, Period)
468
+
469
+ def test_parse_week_str_roundstrip(self):
470
+ # GH#50803
471
+ per = Period("2017-01-23/2017-01-29")
472
+ assert per.freq.freqstr == "W-SUN"
473
+
474
+ per = Period("2017-01-24/2017-01-30")
475
+ assert per.freq.freqstr == "W-MON"
476
+
477
+ msg = "Could not parse as weekly-freq Period"
478
+ with pytest.raises(ValueError, match=msg):
479
+ # not 6 days apart
480
+ Period("2016-01-23/2017-01-29")
481
+
482
+ def test_period_from_ordinal(self):
483
+ p = Period("2011-01", freq="M")
484
+ res = Period._from_ordinal(p.ordinal, freq=p.freq)
485
+ assert p == res
486
+ assert isinstance(res, Period)
487
+
488
+ @pytest.mark.parametrize("freq", ["Y", "M", "D", "h"])
489
+ def test_construct_from_nat_string_and_freq(self, freq):
490
+ per = Period("NaT", freq=freq)
491
+ assert per is NaT
492
+
493
+ per = Period("NaT", freq="2" + freq)
494
+ assert per is NaT
495
+
496
+ per = Period("NaT", freq="3" + freq)
497
+ assert per is NaT
498
+
499
+ def test_period_cons_nat(self):
500
+ p = Period("nat", freq="W-SUN")
501
+ assert p is NaT
502
+
503
+ p = Period(iNaT, freq="D")
504
+ assert p is NaT
505
+
506
+ p = Period(iNaT, freq="3D")
507
+ assert p is NaT
508
+
509
+ p = Period(iNaT, freq="1D1h")
510
+ assert p is NaT
511
+
512
+ p = Period("NaT")
513
+ assert p is NaT
514
+
515
+ p = Period(iNaT)
516
+ assert p is NaT
517
+
518
+ def test_period_cons_mult(self):
519
+ p1 = Period("2011-01", freq="3M")
520
+ p2 = Period("2011-01", freq="M")
521
+ assert p1.ordinal == p2.ordinal
522
+
523
+ assert p1.freq == offsets.MonthEnd(3)
524
+ assert p1.freqstr == "3M"
525
+
526
+ assert p2.freq == offsets.MonthEnd()
527
+ assert p2.freqstr == "M"
528
+
529
+ result = p1 + 1
530
+ assert result.ordinal == (p2 + 3).ordinal
531
+
532
+ assert result.freq == p1.freq
533
+ assert result.freqstr == "3M"
534
+
535
+ result = p1 - 1
536
+ assert result.ordinal == (p2 - 3).ordinal
537
+ assert result.freq == p1.freq
538
+ assert result.freqstr == "3M"
539
+
540
+ msg = "Frequency must be positive, because it represents span: -3M"
541
+ with pytest.raises(ValueError, match=msg):
542
+ Period("2011-01", freq="-3M")
543
+
544
+ msg = "Frequency must be positive, because it represents span: 0M"
545
+ with pytest.raises(ValueError, match=msg):
546
+ Period("2011-01", freq="0M")
547
+
548
+ def test_period_cons_combined(self):
549
+ p = [
550
+ (
551
+ Period("2011-01", freq="1D1h"),
552
+ Period("2011-01", freq="1h1D"),
553
+ Period("2011-01", freq="h"),
554
+ ),
555
+ (
556
+ Period(ordinal=1, freq="1D1h"),
557
+ Period(ordinal=1, freq="1h1D"),
558
+ Period(ordinal=1, freq="h"),
559
+ ),
560
+ ]
561
+
562
+ for p1, p2, p3 in p:
563
+ assert p1.ordinal == p3.ordinal
564
+ assert p2.ordinal == p3.ordinal
565
+
566
+ assert p1.freq == offsets.Hour(25)
567
+ assert p1.freqstr == "25h"
568
+
569
+ assert p2.freq == offsets.Hour(25)
570
+ assert p2.freqstr == "25h"
571
+
572
+ assert p3.freq == offsets.Hour()
573
+ assert p3.freqstr == "h"
574
+
575
+ result = p1 + 1
576
+ assert result.ordinal == (p3 + 25).ordinal
577
+ assert result.freq == p1.freq
578
+ assert result.freqstr == "25h"
579
+
580
+ result = p2 + 1
581
+ assert result.ordinal == (p3 + 25).ordinal
582
+ assert result.freq == p2.freq
583
+ assert result.freqstr == "25h"
584
+
585
+ result = p1 - 1
586
+ assert result.ordinal == (p3 - 25).ordinal
587
+ assert result.freq == p1.freq
588
+ assert result.freqstr == "25h"
589
+
590
+ result = p2 - 1
591
+ assert result.ordinal == (p3 - 25).ordinal
592
+ assert result.freq == p2.freq
593
+ assert result.freqstr == "25h"
594
+
595
+ msg = "Frequency must be positive, because it represents span: -25h"
596
+ with pytest.raises(ValueError, match=msg):
597
+ Period("2011-01", freq="-1D1h")
598
+ with pytest.raises(ValueError, match=msg):
599
+ Period("2011-01", freq="-1h1D")
600
+ with pytest.raises(ValueError, match=msg):
601
+ Period(ordinal=1, freq="-1D1h")
602
+ with pytest.raises(ValueError, match=msg):
603
+ Period(ordinal=1, freq="-1h1D")
604
+
605
+ msg = "Frequency must be positive, because it represents span: 0D"
606
+ with pytest.raises(ValueError, match=msg):
607
+ Period("2011-01", freq="0D0h")
608
+ with pytest.raises(ValueError, match=msg):
609
+ Period(ordinal=1, freq="0D0h")
610
+
611
+ # You can only combine together day and intraday offsets
612
+ msg = "Invalid frequency: 1W1D"
613
+ with pytest.raises(ValueError, match=msg):
614
+ Period("2011-01", freq="1W1D")
615
+ msg = "Invalid frequency: 1D1W"
616
+ with pytest.raises(ValueError, match=msg):
617
+ Period("2011-01", freq="1D1W")
618
+
619
+ @pytest.mark.parametrize("day", ["1970/01/01 ", "2020-12-31 ", "1981/09/13 "])
620
+ @pytest.mark.parametrize("hour", ["00:00:00", "00:00:01", "23:59:59", "12:00:59"])
621
+ @pytest.mark.parametrize(
622
+ "sec_float, expected",
623
+ [
624
+ (".000000001", 1),
625
+ (".000000999", 999),
626
+ (".123456789", 789),
627
+ (".999999999", 999),
628
+ (".999999000", 0),
629
+ # Test femtoseconds, attoseconds, picoseconds are dropped like Timestamp
630
+ (".999999001123", 1),
631
+ (".999999001123456", 1),
632
+ (".999999001123456789", 1),
633
+ ],
634
+ )
635
+ def test_period_constructor_nanosecond(self, day, hour, sec_float, expected):
636
+ # GH 34621
637
+
638
+ assert Period(day + hour + sec_float).start_time.nanosecond == expected
639
+
640
+ @pytest.mark.parametrize("hour", range(24))
641
+ def test_period_large_ordinal(self, hour):
642
+ # Issue #36430
643
+ # Integer overflow for Period over the maximum timestamp
644
+ p = Period(ordinal=2562048 + hour, freq="1h")
645
+ assert p.hour == hour
646
+
647
+ @pytest.mark.filterwarnings(
648
+ "ignore:Period with BDay freq is deprecated:FutureWarning"
649
+ )
650
+ @pytest.mark.parametrize(
651
+ "freq,freq_depr",
652
+ [("2W", "2w"), ("2W-FRI", "2w-fri"), ("2D", "2d"), ("2B", "2b")],
653
+ )
654
+ def test_period_deprecated_lowercase_freq(self, freq, freq_depr):
655
+ # GH#58998
656
+ msg = (
657
+ f"'{freq_depr[1:]}' is deprecated and will be removed in a future version."
658
+ )
659
+
660
+ with tm.assert_produces_warning(
661
+ Pandas4Warning, match=msg, raise_on_extra_warnings=False
662
+ ):
663
+ result = Period("2016-03-01 09:00", freq=freq_depr)
664
+
665
+ expected = Period("2016-03-01 09:00", freq=freq)
666
+ assert result == expected
667
+
668
+
669
+ class TestPeriodMethods:
670
+ def test_round_trip(self, temp_file):
671
+ p = Period("2000Q1")
672
+ new_p = tm.round_trip_pickle(p, temp_file)
673
+ assert new_p == p
674
+
675
+ def test_hash(self):
676
+ assert hash(Period("2011-01", freq="M")) == hash(Period("2011-01", freq="M"))
677
+
678
+ assert hash(Period("2011-01-01", freq="D")) != hash(Period("2011-01", freq="M"))
679
+
680
+ assert hash(Period("2011-01", freq="3M")) != hash(Period("2011-01", freq="2M"))
681
+
682
+ assert hash(Period("2011-01", freq="M")) != hash(Period("2011-02", freq="M"))
683
+
684
+ # --------------------------------------------------------------
685
+ # to_timestamp
686
+
687
+ def test_to_timestamp_mult(self):
688
+ p = Period("2011-01", freq="M")
689
+ assert p.to_timestamp(how="S") == Timestamp("2011-01-01")
690
+ expected = Timestamp("2011-02-01") - Timedelta(1, unit="us")
691
+ assert p.to_timestamp(how="E") == expected
692
+
693
+ p = Period("2011-01", freq="3M")
694
+ assert p.to_timestamp(how="S") == Timestamp("2011-01-01")
695
+ expected = Timestamp("2011-04-01") - Timedelta(1, unit="us")
696
+ assert p.to_timestamp(how="E") == expected
697
+
698
+ @pytest.mark.filterwarnings(
699
+ "ignore:Period with BDay freq is deprecated:FutureWarning"
700
+ )
701
+ def test_to_timestamp(self):
702
+ p = Period("1982", freq="Y")
703
+ start_ts = p.to_timestamp(how="S")
704
+ aliases = ["s", "StarT", "BEGIn"]
705
+ for a in aliases:
706
+ assert start_ts == p.to_timestamp("D", how=a)
707
+ # freq with mult should not affect to the result
708
+ assert start_ts == p.to_timestamp("3D", how=a)
709
+
710
+ end_ts = p.to_timestamp(how="E")
711
+ aliases = ["e", "end", "FINIsH"]
712
+ for a in aliases:
713
+ assert end_ts == p.to_timestamp("D", how=a)
714
+ assert end_ts == p.to_timestamp("3D", how=a)
715
+
716
+ from_lst = ["Y", "Q", "M", "W", "B", "D", "h", "Min", "s"]
717
+
718
+ def _ex(p):
719
+ if p.freq == "B":
720
+ return p.start_time + Timedelta(days=1) - Timedelta(microseconds=1)
721
+ return Timestamp((p + p.freq).start_time._value - 1, unit="us")
722
+
723
+ for fcode in from_lst:
724
+ p = Period("1982", freq=fcode)
725
+ result = p.to_timestamp().to_period(fcode)
726
+ assert result == p
727
+
728
+ assert p.start_time == p.to_timestamp(how="S")
729
+
730
+ assert p.end_time == _ex(p)
731
+
732
+ # Frequency other than daily
733
+
734
+ p = Period("1985", freq="Y")
735
+
736
+ result = p.to_timestamp("h", how="end")
737
+ expected = Timestamp(1986, 1, 1) - Timedelta(1, unit="us")
738
+ assert result == expected
739
+ result = p.to_timestamp("3h", how="end")
740
+ assert result == expected
741
+
742
+ result = p.to_timestamp("min", how="end")
743
+ expected = Timestamp(1986, 1, 1) - Timedelta(1, unit="us")
744
+ assert result == expected
745
+ result = p.to_timestamp("2min", how="end")
746
+ assert result == expected
747
+
748
+ result = p.to_timestamp(how="end")
749
+ expected = Timestamp(1986, 1, 1) - Timedelta(1, unit="us")
750
+ assert result == expected
751
+
752
+ expected = datetime(1985, 1, 1)
753
+ result = p.to_timestamp("h", how="start")
754
+ assert result == expected
755
+ result = p.to_timestamp("min", how="start")
756
+ assert result == expected
757
+ result = p.to_timestamp("s", how="start")
758
+ assert result == expected
759
+ result = p.to_timestamp("3h", how="start")
760
+ assert result == expected
761
+ result = p.to_timestamp("5s", how="start")
762
+ assert result == expected
763
+
764
+ def test_to_timestamp_business_end(self):
765
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
766
+ per = Period("1990-01-05", "B") # Friday
767
+ result = per.to_timestamp("B", how="E")
768
+
769
+ expected = Timestamp("1990-01-06") - Timedelta(1, unit="us")
770
+ assert result == expected
771
+
772
+ @pytest.mark.parametrize(
773
+ "ts, expected",
774
+ [
775
+ ("1970-01-01 00:00:00", 0),
776
+ ("1970-01-01 00:00:00.000001", 1),
777
+ ("1970-01-01 00:00:00.00001", 10),
778
+ ("1970-01-01 00:00:00.499", 499000),
779
+ ("1999-12-31 23:59:59.999", 999000),
780
+ ("1999-12-31 23:59:59.999999", 999999),
781
+ ("2050-12-31 23:59:59.5", 500000),
782
+ ("2050-12-31 23:59:59.500001", 500001),
783
+ ("2050-12-31 23:59:59.123456", 123456),
784
+ ],
785
+ )
786
+ @pytest.mark.parametrize("freq", [None, "us", "ns"])
787
+ def test_to_timestamp_microsecond(self, ts, expected, freq):
788
+ # GH 24444
789
+ result = Period(ts).to_timestamp(freq=freq).microsecond
790
+ assert result == expected
791
+
792
+ # --------------------------------------------------------------
793
+ # Rendering: __repr__, strftime, etc
794
+
795
+ @pytest.mark.parametrize(
796
+ "str_ts,freq,str_res,str_freq",
797
+ (
798
+ ("Jan-2000", None, "2000-01", "M"),
799
+ ("2000-12-15", None, "2000-12-15", "D"),
800
+ (
801
+ "2000-12-15 13:45:26.123456789",
802
+ "ns",
803
+ "2000-12-15 13:45:26.123456789",
804
+ "ns",
805
+ ),
806
+ ("2000-12-15 13:45:26.123456789", "us", "2000-12-15 13:45:26.123456", "us"),
807
+ ("2000-12-15 13:45:26.123456", None, "2000-12-15 13:45:26.123456", "us"),
808
+ ("2000-12-15 13:45:26.123456789", "ms", "2000-12-15 13:45:26.123", "ms"),
809
+ ("2000-12-15 13:45:26.123", None, "2000-12-15 13:45:26.123", "ms"),
810
+ ("2000-12-15 13:45:26", "s", "2000-12-15 13:45:26", "s"),
811
+ ("2000-12-15 13:45:26", "min", "2000-12-15 13:45", "min"),
812
+ ("2000-12-15 13:45:26", "h", "2000-12-15 13:00", "h"),
813
+ ("2000-12-15", "Y", "2000", "Y-DEC"),
814
+ ("2000-12-15", "Q", "2000Q4", "Q-DEC"),
815
+ ("2000-12-15", "M", "2000-12", "M"),
816
+ ("2000-12-15", "W", "2000-12-11/2000-12-17", "W-SUN"),
817
+ ("2000-12-15", "D", "2000-12-15", "D"),
818
+ ("2000-12-15", "B", "2000-12-15", "B"),
819
+ ),
820
+ )
821
+ @pytest.mark.filterwarnings(
822
+ "ignore:Period with BDay freq is deprecated:FutureWarning"
823
+ )
824
+ def test_repr(self, str_ts, freq, str_res, str_freq):
825
+ p = Period(str_ts, freq=freq)
826
+ assert str(p) == str_res
827
+ assert repr(p) == f"Period('{str_res}', '{str_freq}')"
828
+
829
+ def test_repr_nat(self):
830
+ p = Period("nat", freq="M")
831
+ assert repr(NaT) in repr(p)
832
+
833
+ def test_strftime(self):
834
+ # GH#3363
835
+ p = Period("2000-1-1 12:34:12", freq="s")
836
+ res = p.strftime("%Y-%m-%d %H:%M:%S")
837
+ assert res == "2000-01-01 12:34:12"
838
+ assert isinstance(res, str)
839
+
840
+
841
+ class TestPeriodProperties:
842
+ """Test properties such as year, month, weekday, etc...."""
843
+
844
+ @pytest.mark.parametrize("freq", ["Y", "M", "D", "h"])
845
+ def test_is_leap_year(self, freq):
846
+ # GH 13727
847
+ p = Period("2000-01-01 00:00:00", freq=freq)
848
+ assert p.is_leap_year
849
+ assert isinstance(p.is_leap_year, bool)
850
+
851
+ p = Period("1999-01-01 00:00:00", freq=freq)
852
+ assert not p.is_leap_year
853
+
854
+ p = Period("2004-01-01 00:00:00", freq=freq)
855
+ assert p.is_leap_year
856
+
857
+ p = Period("2100-01-01 00:00:00", freq=freq)
858
+ assert not p.is_leap_year
859
+
860
+ def test_quarterly_negative_ordinals(self):
861
+ p = Period(ordinal=-1, freq="Q-DEC")
862
+ assert p.year == 1969
863
+ assert p.quarter == 4
864
+ assert isinstance(p, Period)
865
+
866
+ p = Period(ordinal=-2, freq="Q-DEC")
867
+ assert p.year == 1969
868
+ assert p.quarter == 3
869
+ assert isinstance(p, Period)
870
+
871
+ p = Period(ordinal=-2, freq="M")
872
+ assert p.year == 1969
873
+ assert p.month == 11
874
+ assert isinstance(p, Period)
875
+
876
+ def test_freq_str(self):
877
+ i1 = Period("1982", freq="Min")
878
+ assert i1.freq == offsets.Minute()
879
+ assert i1.freqstr == "min"
880
+
881
+ @pytest.mark.filterwarnings(
882
+ "ignore:Period with BDay freq is deprecated:FutureWarning"
883
+ )
884
+ def test_period_deprecated_freq(self):
885
+ cases = {
886
+ "M": ["MTH", "MONTH", "MONTHLY", "Mth", "month", "monthly"],
887
+ "B": ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY", "bus"],
888
+ "D": ["DAY", "DLY", "DAILY", "Day", "Dly", "Daily"],
889
+ "h": ["HR", "HOUR", "HRLY", "HOURLY", "hr", "Hour", "HRly"],
890
+ "min": ["minute", "MINUTE", "MINUTELY", "minutely"],
891
+ "s": ["sec", "SEC", "SECOND", "SECONDLY", "second"],
892
+ "ms": ["MILLISECOND", "MILLISECONDLY", "millisecond"],
893
+ "us": ["MICROSECOND", "MICROSECONDLY", "microsecond"],
894
+ "ns": ["NANOSECOND", "NANOSECONDLY", "nanosecond"],
895
+ }
896
+
897
+ msg = INVALID_FREQ_ERR_MSG
898
+ for exp, freqs in cases.items():
899
+ for freq in freqs:
900
+ with pytest.raises(ValueError, match=msg):
901
+ Period("2016-03-01 09:00", freq=freq)
902
+ with pytest.raises(ValueError, match=msg):
903
+ Period(ordinal=1, freq=freq)
904
+
905
+ # check supported freq-aliases still works
906
+ p1 = Period("2016-03-01 09:00", freq=exp)
907
+ p2 = Period(ordinal=1, freq=exp)
908
+ assert isinstance(p1, Period)
909
+ assert isinstance(p2, Period)
910
+
911
+ @staticmethod
912
+ def _period_constructor(bound, offset):
913
+ return Period(
914
+ year=bound.year,
915
+ month=bound.month,
916
+ day=bound.day,
917
+ hour=bound.hour,
918
+ minute=bound.minute,
919
+ second=bound.second + offset,
920
+ freq="us",
921
+ )
922
+
923
+ @pytest.mark.parametrize("bound, offset", [(Timestamp.min, -1), (Timestamp.max, 1)])
924
+ @pytest.mark.parametrize("period_property", ["start_time", "end_time"])
925
+ def test_outer_bounds_start_and_end_time(self, bound, offset, period_property):
926
+ # GH #13346
927
+ period = TestPeriodProperties._period_constructor(bound, offset)
928
+ # post-GH#63760 this no longer raises OutOfBoundsDatetime
929
+ getattr(period, period_property)
930
+
931
+ per = Period._from_ordinal(bound.value, freq=to_offset("ms"))
932
+
933
+ if period_property == "end_time" and offset == 1:
934
+ err = OverflowError
935
+ msg = "value too large"
936
+ else:
937
+ err = OutOfBoundsDatetime
938
+ msg = "Out of bounds microsecond"
939
+ # But if we do the same thing with a much-more-remote date, we still
940
+ # raise OutOfBoundsDatetime (as opposed to silently giving a wrong result)
941
+ with pytest.raises(err, match=msg):
942
+ getattr(per, period_property)
943
+
944
+ @pytest.mark.parametrize("bound, offset", [(Timestamp.min, -1), (Timestamp.max, 1)])
945
+ @pytest.mark.parametrize("period_property", ["start_time", "end_time"])
946
+ def test_inner_bounds_start_and_end_time(self, bound, offset, period_property):
947
+ # GH #13346
948
+ period = TestPeriodProperties._period_constructor(bound, -offset)
949
+ expected = period.to_timestamp().round(freq="s")
950
+ assert getattr(period, period_property).round(freq="s") == expected
951
+ expected = (bound - offset * Timedelta(1, unit="s")).floor("s")
952
+ assert getattr(period, period_property).floor("s") == expected
953
+
954
+ def test_start_time(self):
955
+ freq_lst = ["Y", "Q", "M", "D", "h", "min", "s"]
956
+ xp = datetime(2012, 1, 1)
957
+ for f in freq_lst:
958
+ p = Period("2012", freq=f)
959
+ assert p.start_time == xp
960
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
961
+ assert Period("2012", freq="B").start_time == datetime(2012, 1, 2)
962
+ assert Period("2012", freq="W").start_time == datetime(2011, 12, 26)
963
+
964
+ def test_end_time(self):
965
+ p = Period("2012", freq="Y")
966
+
967
+ exp = Timestamp(2012, 12, 31, 23, 59, 59, 999999)
968
+ assert p.end_time == exp
969
+
970
+ p = Period("2012", freq="Q")
971
+ exp = Timestamp(2012, 3, 31, 23, 59, 59, 999999)
972
+ assert p.end_time == exp
973
+
974
+ p = Period("2012", freq="M")
975
+ exp = Timestamp(2012, 1, 31, 23, 59, 59, 999999)
976
+ assert p.end_time == exp
977
+
978
+ p = Period("2012", freq="D")
979
+ exp = Timestamp(2012, 1, 1, 23, 59, 59, 999999)
980
+ assert p.end_time == exp
981
+
982
+ p = Period("2012", freq="h")
983
+ exp = Timestamp(2012, 1, 1, 0, 59, 59, 999999)
984
+ assert p.end_time == exp
985
+
986
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
987
+ p = Period("2012", freq="B")
988
+ exp = Timestamp(2012, 1, 2, 23, 59, 59, 999999)
989
+ assert p.end_time == exp
990
+
991
+ p = Period("2012", freq="W")
992
+ exp = Timestamp(2012, 1, 1, 23, 59, 59, 999999)
993
+ assert p.end_time == exp
994
+
995
+ # Test for GH 11738
996
+ p = Period("2012", freq="15D")
997
+ exp = Timestamp(2012, 1, 15, 23, 59, 59, 999999)
998
+ assert p.end_time == exp
999
+
1000
+ p = Period("2012", freq="1D1h")
1001
+ exp = Timestamp(2012, 1, 2, 0, 59, 59, 999999)
1002
+ assert p.end_time == exp
1003
+
1004
+ p = Period("2012", freq="1h1D")
1005
+ exp = Timestamp(2012, 1, 2, 0, 59, 59, 999999)
1006
+ assert p.end_time == exp
1007
+
1008
+ def test_end_time_business_friday(self):
1009
+ # GH#34449
1010
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
1011
+ per = Period("1990-01-05", "B")
1012
+ result = per.end_time
1013
+
1014
+ expected = Timestamp("1990-01-06") - Timedelta(1, unit="us")
1015
+ assert result == expected
1016
+
1017
+ def test_anchor_week_end_time(self):
1018
+ p = Period("2013-1-1", "W-SAT")
1019
+ exp = Timestamp(2013, 1, 5, 23, 59, 59, 999999)
1020
+ assert p.end_time == exp
1021
+
1022
+ def test_properties_annually(self):
1023
+ # Test properties on Periods with annually frequency.
1024
+ a_date = Period(freq="Y", year=2007)
1025
+ assert a_date.year == 2007
1026
+
1027
+ def test_properties_quarterly(self):
1028
+ # Test properties on Periods with daily frequency.
1029
+ qedec_date = Period(freq="Q-DEC", year=2007, quarter=1)
1030
+ qejan_date = Period(freq="Q-JAN", year=2007, quarter=1)
1031
+ qejun_date = Period(freq="Q-JUN", year=2007, quarter=1)
1032
+ for x in range(3):
1033
+ for qd in (qedec_date, qejan_date, qejun_date):
1034
+ assert (qd + x).qyear == 2007
1035
+ assert (qd + x).quarter == x + 1
1036
+
1037
+ def test_properties_monthly(self):
1038
+ # Test properties on Periods with daily frequency.
1039
+ m_date = Period(freq="M", year=2007, month=1)
1040
+ for x in range(11):
1041
+ m_ival_x = m_date + x
1042
+ assert m_ival_x.year == 2007
1043
+ if 1 <= x + 1 <= 3:
1044
+ assert m_ival_x.quarter == 1
1045
+ elif 4 <= x + 1 <= 6:
1046
+ assert m_ival_x.quarter == 2
1047
+ elif 7 <= x + 1 <= 9:
1048
+ assert m_ival_x.quarter == 3
1049
+ elif 10 <= x + 1 <= 12:
1050
+ assert m_ival_x.quarter == 4
1051
+ assert m_ival_x.month == x + 1
1052
+
1053
+ def test_properties_weekly(self):
1054
+ # Test properties on Periods with daily frequency.
1055
+ w_date = Period(freq="W", year=2007, month=1, day=7)
1056
+ assert w_date.year == 2007
1057
+ assert w_date.quarter == 1
1058
+ assert w_date.month == 1
1059
+ assert w_date.week == 1
1060
+ assert (w_date - 1).week == 52
1061
+ assert w_date.days_in_month == 31
1062
+ assert Period(freq="W", year=2012, month=2, day=1).days_in_month == 29
1063
+
1064
+ def test_properties_weekly_legacy(self):
1065
+ # Test properties on Periods with daily frequency.
1066
+ w_date = Period(freq="W", year=2007, month=1, day=7)
1067
+ assert w_date.year == 2007
1068
+ assert w_date.quarter == 1
1069
+ assert w_date.month == 1
1070
+ assert w_date.week == 1
1071
+ assert (w_date - 1).week == 52
1072
+ assert w_date.days_in_month == 31
1073
+
1074
+ exp = Period(freq="W", year=2012, month=2, day=1)
1075
+ assert exp.days_in_month == 29
1076
+
1077
+ msg = INVALID_FREQ_ERR_MSG
1078
+ with pytest.raises(ValueError, match=msg):
1079
+ Period(freq="WK", year=2007, month=1, day=7)
1080
+
1081
+ def test_properties_daily(self):
1082
+ # Test properties on Periods with daily frequency.
1083
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
1084
+ b_date = Period(freq="B", year=2007, month=1, day=1)
1085
+ assert b_date.year == 2007
1086
+ assert b_date.quarter == 1
1087
+ assert b_date.month == 1
1088
+ assert b_date.day == 1
1089
+ assert b_date.weekday == 0
1090
+ assert b_date.dayofyear == 1
1091
+ assert b_date.days_in_month == 31
1092
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
1093
+ assert Period(freq="B", year=2012, month=2, day=1).days_in_month == 29
1094
+
1095
+ d_date = Period(freq="D", year=2007, month=1, day=1)
1096
+
1097
+ assert d_date.year == 2007
1098
+ assert d_date.quarter == 1
1099
+ assert d_date.month == 1
1100
+ assert d_date.day == 1
1101
+ assert d_date.weekday == 0
1102
+ assert d_date.dayofyear == 1
1103
+ assert d_date.days_in_month == 31
1104
+ assert Period(freq="D", year=2012, month=2, day=1).days_in_month == 29
1105
+
1106
+ def test_properties_hourly(self):
1107
+ # Test properties on Periods with hourly frequency.
1108
+ h_date1 = Period(freq="h", year=2007, month=1, day=1, hour=0)
1109
+ h_date2 = Period(freq="2h", year=2007, month=1, day=1, hour=0)
1110
+
1111
+ for h_date in [h_date1, h_date2]:
1112
+ assert h_date.year == 2007
1113
+ assert h_date.quarter == 1
1114
+ assert h_date.month == 1
1115
+ assert h_date.day == 1
1116
+ assert h_date.weekday == 0
1117
+ assert h_date.dayofyear == 1
1118
+ assert h_date.hour == 0
1119
+ assert h_date.days_in_month == 31
1120
+ assert (
1121
+ Period(freq="h", year=2012, month=2, day=1, hour=0).days_in_month == 29
1122
+ )
1123
+
1124
+ def test_properties_minutely(self):
1125
+ # Test properties on Periods with minutely frequency.
1126
+ t_date = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
1127
+ assert t_date.quarter == 1
1128
+ assert t_date.month == 1
1129
+ assert t_date.day == 1
1130
+ assert t_date.weekday == 0
1131
+ assert t_date.dayofyear == 1
1132
+ assert t_date.hour == 0
1133
+ assert t_date.minute == 0
1134
+ assert t_date.days_in_month == 31
1135
+ assert (
1136
+ Period(freq="D", year=2012, month=2, day=1, hour=0, minute=0).days_in_month
1137
+ == 29
1138
+ )
1139
+
1140
+ def test_properties_secondly(self):
1141
+ # Test properties on Periods with secondly frequency.
1142
+ s_date = Period(
1143
+ freq="Min", year=2007, month=1, day=1, hour=0, minute=0, second=0
1144
+ )
1145
+ assert s_date.year == 2007
1146
+ assert s_date.quarter == 1
1147
+ assert s_date.month == 1
1148
+ assert s_date.day == 1
1149
+ assert s_date.weekday == 0
1150
+ assert s_date.dayofyear == 1
1151
+ assert s_date.hour == 0
1152
+ assert s_date.minute == 0
1153
+ assert s_date.second == 0
1154
+ assert s_date.days_in_month == 31
1155
+ assert (
1156
+ Period(
1157
+ freq="Min", year=2012, month=2, day=1, hour=0, minute=0, second=0
1158
+ ).days_in_month
1159
+ == 29
1160
+ )
1161
+
1162
+
1163
+ class TestPeriodComparisons:
1164
+ def test_sort_periods(self):
1165
+ jan = Period("2000-01", "M")
1166
+ feb = Period("2000-02", "M")
1167
+ mar = Period("2000-03", "M")
1168
+ periods = [mar, jan, feb]
1169
+ correctPeriods = [jan, feb, mar]
1170
+ assert sorted(periods) == correctPeriods
1171
+
1172
+
1173
+ def test_period_immutable():
1174
+ # see gh-17116
1175
+ msg = "not writable"
1176
+
1177
+ per = Period("2014Q1")
1178
+ with pytest.raises(AttributeError, match=msg):
1179
+ per.ordinal = 14
1180
+
1181
+ freq = per.freq
1182
+ with pytest.raises(AttributeError, match=msg):
1183
+ per.freq = 2 * freq
1184
+
1185
+
1186
+ def test_small_year_parsing():
1187
+ per1 = Period("0001-01-07", "D")
1188
+ assert per1.year == 1
1189
+ assert per1.day == 7
1190
+
1191
+
1192
+ def test_negone_ordinals():
1193
+ freqs = ["Y", "M", "Q", "D", "h", "min", "s"]
1194
+
1195
+ period = Period(ordinal=-1, freq="D")
1196
+ for freq in freqs:
1197
+ repr(period.asfreq(freq))
1198
+
1199
+ for freq in freqs:
1200
+ period = Period(ordinal=-1, freq=freq)
1201
+ repr(period)
1202
+ assert period.year == 1969
1203
+
1204
+ with tm.assert_produces_warning(FutureWarning, match=bday_msg):
1205
+ period = Period(ordinal=-1, freq="B")
1206
+ repr(period)
1207
+ period = Period(ordinal=-1, freq="W")
1208
+ repr(period)
.venv/lib/python3.13/site-packages/pandas/tests/scalar/test_na_scalar.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ date,
3
+ time,
4
+ timedelta,
5
+ )
6
+ import pickle
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from pandas._libs.missing import NA
12
+
13
+ from pandas.core.dtypes.common import is_scalar
14
+
15
+ import pandas as pd
16
+ import pandas._testing as tm
17
+
18
+
19
+ def test_singleton():
20
+ assert NA is NA
21
+ new_NA = type(NA)()
22
+ assert new_NA is NA
23
+
24
+
25
+ def test_repr():
26
+ assert repr(NA) == "<NA>"
27
+ assert str(NA) == "<NA>"
28
+
29
+
30
+ def test_format():
31
+ # GH-34740
32
+ assert format(NA) == "<NA>"
33
+ assert format(NA, ">10") == " <NA>"
34
+ assert format(NA, "xxx") == "<NA>" # NA is flexible, accept any format spec
35
+
36
+ assert f"{NA}" == "<NA>"
37
+ assert f"{NA:>10}" == " <NA>"
38
+ assert f"{NA:xxx}" == "<NA>"
39
+
40
+
41
+ def test_truthiness():
42
+ msg = "boolean value of NA is ambiguous"
43
+
44
+ with pytest.raises(TypeError, match=msg):
45
+ bool(NA)
46
+
47
+ with pytest.raises(TypeError, match=msg):
48
+ not NA
49
+
50
+
51
+ def test_hashable():
52
+ assert hash(NA) == hash(NA)
53
+ d = {NA: "test"}
54
+ assert d[NA] == "test"
55
+
56
+
57
+ @pytest.mark.parametrize(
58
+ "other", [NA, 1, 1.0, "a", b"a", np.int64(1), np.nan], ids=repr
59
+ )
60
+ def test_arithmetic_ops(all_arithmetic_functions, other):
61
+ op = all_arithmetic_functions
62
+
63
+ if op.__name__ in ("pow", "rpow", "rmod") and isinstance(other, (str, bytes)):
64
+ pytest.skip(reason=f"{op.__name__} with NA and {other} not defined.")
65
+ if op.__name__ in ("divmod", "rdivmod"):
66
+ assert op(NA, other) is (NA, NA)
67
+ else:
68
+ if op.__name__ == "rpow":
69
+ # avoid special case
70
+ other += 1
71
+ assert op(NA, other) is NA
72
+
73
+
74
+ @pytest.mark.parametrize(
75
+ "other",
76
+ [
77
+ NA,
78
+ 1,
79
+ 1.0,
80
+ "a",
81
+ b"a",
82
+ np.int64(1),
83
+ np.nan,
84
+ np.bool_(True),
85
+ time(0),
86
+ date(1, 2, 3),
87
+ timedelta(1),
88
+ pd.NaT,
89
+ ],
90
+ )
91
+ def test_comparison_ops(comparison_op, other):
92
+ assert comparison_op(NA, other) is NA
93
+ assert comparison_op(other, NA) is NA
94
+
95
+
96
+ @pytest.mark.parametrize(
97
+ "value",
98
+ [
99
+ 0,
100
+ 0.0,
101
+ -0,
102
+ -0.0,
103
+ False,
104
+ np.bool_(False),
105
+ np.int_(0),
106
+ np.float64(0),
107
+ np.int_(-0),
108
+ np.float64(-0),
109
+ ],
110
+ )
111
+ @pytest.mark.parametrize("asarray", [True, False])
112
+ def test_pow_special(value, asarray):
113
+ if asarray:
114
+ value = np.array([value])
115
+ result = NA**value
116
+
117
+ if asarray:
118
+ result = result[0]
119
+ else:
120
+ # this assertion isn't possible for ndarray.
121
+ assert isinstance(result, type(value))
122
+ assert result == 1
123
+
124
+
125
+ @pytest.mark.parametrize(
126
+ "value", [1, 1.0, True, np.bool_(True), np.int_(1), np.float64(1)]
127
+ )
128
+ @pytest.mark.parametrize("asarray", [True, False])
129
+ def test_rpow_special(value, asarray):
130
+ if asarray:
131
+ value = np.array([value])
132
+ result = value**NA
133
+
134
+ if asarray:
135
+ result = result[0]
136
+ elif not isinstance(value, (np.float64, np.bool_, np.int_)):
137
+ # this assertion isn't possible with asarray=True
138
+ assert isinstance(result, type(value))
139
+
140
+ assert result == value
141
+
142
+
143
+ @pytest.mark.parametrize("value", [-1, -1.0, np.int_(-1), np.float64(-1)])
144
+ @pytest.mark.parametrize("asarray", [True, False])
145
+ def test_rpow_minus_one(value, asarray):
146
+ if asarray:
147
+ value = np.array([value])
148
+ result = value**NA
149
+
150
+ if asarray:
151
+ result = result[0]
152
+
153
+ assert pd.isna(result)
154
+
155
+
156
+ def test_unary_ops():
157
+ assert +NA is NA
158
+ assert -NA is NA
159
+ assert abs(NA) is NA
160
+ assert ~NA is NA
161
+
162
+
163
+ def test_logical_and():
164
+ assert NA & True is NA
165
+ assert True & NA is NA
166
+ assert NA & False is False
167
+ assert False & NA is False
168
+ assert NA & NA is NA
169
+
170
+ # GH#58427
171
+ assert NA & np.bool_(True) is NA
172
+ assert np.bool_(True) & NA is NA
173
+ assert NA & np.bool_(False) is False
174
+ assert np.bool_(False) & NA is False
175
+
176
+ msg = "unsupported operand type"
177
+ with pytest.raises(TypeError, match=msg):
178
+ NA & 5
179
+
180
+
181
+ def test_logical_or():
182
+ assert NA | True is True
183
+ assert True | NA is True
184
+ assert NA | False is NA
185
+ assert False | NA is NA
186
+ assert NA | NA is NA
187
+
188
+ # GH#58427
189
+ assert NA | np.bool_(True) is True
190
+ assert np.bool_(True) | NA is True
191
+ assert NA | np.bool_(False) is NA
192
+ assert np.bool_(False) | NA is NA
193
+
194
+ msg = "unsupported operand type"
195
+ with pytest.raises(TypeError, match=msg):
196
+ NA | 5
197
+
198
+
199
+ def test_logical_xor():
200
+ assert NA ^ True is NA
201
+ assert True ^ NA is NA
202
+ assert NA ^ False is NA
203
+ assert False ^ NA is NA
204
+ assert NA ^ NA is NA
205
+
206
+ # GH#58427
207
+ assert NA ^ np.bool_(True) is NA
208
+ assert np.bool_(True) ^ NA is NA
209
+ assert NA ^ np.bool_(False) is NA
210
+ assert np.bool_(False) ^ NA is NA
211
+
212
+ msg = "unsupported operand type"
213
+ with pytest.raises(TypeError, match=msg):
214
+ NA ^ 5
215
+
216
+
217
+ def test_logical_not():
218
+ assert ~NA is NA
219
+
220
+
221
+ @pytest.mark.parametrize("shape", [(3,), (3, 3), (1, 2, 3)])
222
+ def test_arithmetic_ndarray(shape, all_arithmetic_functions):
223
+ op = all_arithmetic_functions
224
+ a = np.zeros(shape)
225
+ if op.__name__ == "pow":
226
+ a += 5
227
+ result = op(NA, a)
228
+ expected = np.full(a.shape, NA, dtype=object)
229
+ tm.assert_numpy_array_equal(result, expected)
230
+
231
+
232
+ def test_is_scalar():
233
+ assert is_scalar(NA) is True
234
+
235
+
236
+ def test_isna():
237
+ assert pd.isna(NA) is True
238
+ assert pd.notna(NA) is False
239
+
240
+
241
+ def test_series_isna():
242
+ s = pd.Series([1, NA], dtype=object)
243
+ expected = pd.Series([False, True])
244
+ tm.assert_series_equal(s.isna(), expected)
245
+
246
+
247
+ def test_ufunc():
248
+ assert np.log(NA) is NA
249
+ assert np.add(NA, 1) is NA
250
+ result = np.divmod(NA, 1)
251
+ assert result[0] is NA and result[1] is NA
252
+
253
+ result = np.frexp(NA)
254
+ assert result[0] is NA and result[1] is NA
255
+
256
+
257
+ def test_ufunc_raises():
258
+ msg = "ufunc method 'at'"
259
+ with pytest.raises(ValueError, match=msg):
260
+ np.log.at(NA, 0)
261
+
262
+
263
+ def test_binary_input_not_dunder():
264
+ a = np.array([1, 2, 3])
265
+ expected = np.array([NA, NA, NA], dtype=object)
266
+ result = np.logaddexp(a, NA)
267
+ tm.assert_numpy_array_equal(result, expected)
268
+
269
+ result = np.logaddexp(NA, a)
270
+ tm.assert_numpy_array_equal(result, expected)
271
+
272
+ # all NA, multiple inputs
273
+ assert np.logaddexp(NA, NA) is NA
274
+
275
+ result = np.modf(NA, NA)
276
+ assert len(result) == 2
277
+ assert all(x is NA for x in result)
278
+
279
+
280
+ def test_divmod_ufunc():
281
+ # binary in, binary out.
282
+ a = np.array([1, 2, 3])
283
+ expected = np.array([NA, NA, NA], dtype=object)
284
+
285
+ result = np.divmod(a, NA)
286
+ assert isinstance(result, tuple)
287
+ for arr in result:
288
+ tm.assert_numpy_array_equal(arr, expected)
289
+ tm.assert_numpy_array_equal(arr, expected)
290
+
291
+ result = np.divmod(NA, a)
292
+ for arr in result:
293
+ tm.assert_numpy_array_equal(arr, expected)
294
+ tm.assert_numpy_array_equal(arr, expected)
295
+
296
+
297
+ def test_integer_hash_collision_dict():
298
+ # GH 30013
299
+ result = {NA: "foo", hash(NA): "bar"}
300
+
301
+ assert result[NA] == "foo"
302
+ assert result[hash(NA)] == "bar"
303
+
304
+
305
+ def test_integer_hash_collision_set():
306
+ # GH 30013
307
+ result = {NA, hash(NA)}
308
+
309
+ assert len(result) == 2
310
+ assert NA in result
311
+ assert hash(NA) in result
312
+
313
+
314
+ def test_pickle_roundtrip():
315
+ # https://github.com/pandas-dev/pandas/issues/31847
316
+ result = pickle.loads(pickle.dumps(NA))
317
+ assert result is NA
318
+
319
+
320
+ def test_pickle_roundtrip_pandas(temp_file):
321
+ result = tm.round_trip_pickle(NA, temp_file)
322
+ assert result is NA
323
+
324
+
325
+ @pytest.mark.parametrize(
326
+ "values, dtype", [([1, 2, NA], "Int64"), (["A", "B", NA], "string")]
327
+ )
328
+ @pytest.mark.parametrize("as_frame", [True, False])
329
+ def test_pickle_roundtrip_containers(as_frame, values, dtype, temp_file):
330
+ s = pd.Series(pd.array(values, dtype=dtype))
331
+ if as_frame:
332
+ s = s.to_frame(name="A")
333
+ result = tm.round_trip_pickle(s, temp_file)
334
+ tm.assert_equal(result, s)
.venv/lib/python3.13/site-packages/pandas/tests/scalar/test_nat.py ADDED
@@ -0,0 +1,684 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ datetime,
3
+ timedelta,
4
+ )
5
+ import operator
6
+ import zoneinfo
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from pandas._libs.tslibs import iNaT
12
+
13
+ from pandas import (
14
+ DatetimeIndex,
15
+ DatetimeTZDtype,
16
+ Index,
17
+ NaT,
18
+ Period,
19
+ Series,
20
+ Timedelta,
21
+ TimedeltaIndex,
22
+ Timestamp,
23
+ isna,
24
+ offsets,
25
+ )
26
+ import pandas._testing as tm
27
+ from pandas.core import roperator
28
+ from pandas.core.arrays import (
29
+ DatetimeArray,
30
+ PeriodArray,
31
+ TimedeltaArray,
32
+ )
33
+
34
+
35
+ class TestNaTFormatting:
36
+ def test_repr(self):
37
+ assert repr(NaT) == "NaT"
38
+
39
+ def test_str(self):
40
+ assert str(NaT) == "NaT"
41
+
42
+ def test_isoformat(self):
43
+ assert NaT.isoformat() == "NaT"
44
+
45
+
46
+ @pytest.mark.parametrize(
47
+ "nat,idx",
48
+ [
49
+ (Timestamp("NaT"), DatetimeArray),
50
+ (Timedelta("NaT"), TimedeltaArray),
51
+ (Period("NaT", freq="M"), PeriodArray),
52
+ ],
53
+ )
54
+ def test_nat_fields(nat, idx):
55
+ for field in idx._field_ops:
56
+ # weekday is a property of DTI, but a method
57
+ # on NaT/Timestamp for compat with datetime
58
+ if field == "weekday":
59
+ continue
60
+
61
+ result = getattr(NaT, field)
62
+ assert np.isnan(result)
63
+
64
+ result = getattr(nat, field)
65
+ assert np.isnan(result)
66
+
67
+ for field in idx._bool_ops:
68
+ result = getattr(NaT, field)
69
+ assert result is False
70
+
71
+ result = getattr(nat, field)
72
+ assert result is False
73
+
74
+
75
+ def test_nat_vector_field_access():
76
+ idx = DatetimeIndex(["1/1/2000", None, None, "1/4/2000"])
77
+
78
+ for field in DatetimeArray._field_ops:
79
+ # weekday is a property of DTI, but a method
80
+ # on NaT/Timestamp for compat with datetime
81
+ if field == "weekday":
82
+ continue
83
+
84
+ result = getattr(idx, field)
85
+ expected = Index([getattr(x, field) for x in idx])
86
+ tm.assert_index_equal(result, expected)
87
+
88
+ ser = Series(idx)
89
+
90
+ for field in DatetimeArray._field_ops:
91
+ # weekday is a property of DTI, but a method
92
+ # on NaT/Timestamp for compat with datetime
93
+ if field == "weekday":
94
+ continue
95
+
96
+ result = getattr(ser.dt, field)
97
+ expected = [getattr(x, field) for x in idx]
98
+ tm.assert_series_equal(result, Series(expected))
99
+
100
+ for field in DatetimeArray._bool_ops:
101
+ result = getattr(ser.dt, field)
102
+ expected = [getattr(x, field) for x in idx]
103
+ tm.assert_series_equal(result, Series(expected))
104
+
105
+
106
+ @pytest.mark.parametrize("klass", [Timestamp, Timedelta, Period])
107
+ @pytest.mark.parametrize(
108
+ "value", [None, np.nan, iNaT, float("nan"), NaT, "NaT", "nat", "", "NAT"]
109
+ )
110
+ def test_identity(klass, value):
111
+ assert klass(value) is NaT
112
+
113
+
114
+ @pytest.mark.parametrize("klass", [Timestamp, Timedelta])
115
+ @pytest.mark.parametrize("method", ["round", "floor", "ceil"])
116
+ @pytest.mark.parametrize("freq", ["s", "5s", "min", "5min", "h", "5h"])
117
+ def test_round_nat(klass, method, freq):
118
+ # see gh-14940
119
+ ts = klass("nat")
120
+
121
+ round_method = getattr(ts, method)
122
+ assert round_method(freq) is ts
123
+
124
+
125
+ @pytest.mark.parametrize(
126
+ "method",
127
+ [
128
+ "astimezone",
129
+ "combine",
130
+ "ctime",
131
+ "dst",
132
+ "fromordinal",
133
+ "fromtimestamp",
134
+ "fromisocalendar",
135
+ "isocalendar",
136
+ "strftime",
137
+ "strptime",
138
+ "time",
139
+ "timestamp",
140
+ "timetuple",
141
+ "timetz",
142
+ "toordinal",
143
+ "tzname",
144
+ "utcfromtimestamp",
145
+ "utcnow",
146
+ "utcoffset",
147
+ "utctimetuple",
148
+ ],
149
+ )
150
+ def test_nat_methods_raise(method):
151
+ # see gh-9513, gh-17329
152
+ msg = f"NaTType does not support {method}"
153
+
154
+ with pytest.raises(ValueError, match=msg):
155
+ getattr(NaT, method)()
156
+
157
+
158
+ @pytest.mark.parametrize("method", ["weekday", "isoweekday"])
159
+ def test_nat_methods_nan(method):
160
+ # see gh-9513, gh-17329
161
+ assert np.isnan(getattr(NaT, method)())
162
+
163
+
164
+ @pytest.mark.parametrize(
165
+ "method", ["date", "now", "replace", "today", "tz_convert", "tz_localize"]
166
+ )
167
+ def test_nat_methods_nat(method):
168
+ # see gh-8254, gh-9513, gh-17329
169
+ assert getattr(NaT, method)() is NaT
170
+
171
+
172
+ @pytest.mark.parametrize(
173
+ "get_nat", [lambda x: NaT, lambda x: Timedelta(x), lambda x: Timestamp(x)]
174
+ )
175
+ def test_nat_iso_format(get_nat):
176
+ # see gh-12300
177
+ assert get_nat("NaT").isoformat() == "NaT"
178
+ assert get_nat("NaT").isoformat(timespec="nanoseconds") == "NaT"
179
+
180
+
181
+ @pytest.mark.parametrize(
182
+ "klass,expected",
183
+ [
184
+ (Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]),
185
+ (
186
+ Timedelta,
187
+ [
188
+ "components",
189
+ "resolution_string",
190
+ "to_pytimedelta",
191
+ "to_timedelta64",
192
+ "unit",
193
+ "view",
194
+ ],
195
+ ),
196
+ ],
197
+ )
198
+ def test_missing_public_nat_methods(klass, expected):
199
+ # see gh-17327
200
+ #
201
+ # NaT should have *most* of the Timestamp and Timedelta methods.
202
+ # Here, we check which public methods NaT does not have. We
203
+ # ignore any missing private methods.
204
+ nat_names = dir(NaT)
205
+ klass_names = dir(klass)
206
+
207
+ missing = [x for x in klass_names if x not in nat_names and not x.startswith("_")]
208
+ missing.sort()
209
+
210
+ assert missing == expected
211
+
212
+
213
+ def _get_overlap_public_nat_methods(klass, as_tuple=False):
214
+ """
215
+ Get overlapping public methods between NaT and another class.
216
+
217
+ Parameters
218
+ ----------
219
+ klass : type
220
+ The class to compare with NaT
221
+ as_tuple : bool, default False
222
+ Whether to return a list of tuples of the form (klass, method).
223
+
224
+ Returns
225
+ -------
226
+ overlap : list
227
+ """
228
+ nat_names = dir(NaT)
229
+ klass_names = dir(klass)
230
+
231
+ overlap = [
232
+ x
233
+ for x in nat_names
234
+ if x in klass_names and not x.startswith("_") and callable(getattr(klass, x))
235
+ ]
236
+
237
+ # Timestamp takes precedence over Timedelta in terms of overlap.
238
+ if klass is Timedelta:
239
+ ts_names = dir(Timestamp)
240
+ overlap = [x for x in overlap if x not in ts_names]
241
+
242
+ if as_tuple:
243
+ overlap = [(klass, method) for method in overlap]
244
+
245
+ overlap.sort()
246
+ return overlap
247
+
248
+
249
+ @pytest.mark.parametrize(
250
+ "klass,expected",
251
+ [
252
+ (
253
+ Timestamp,
254
+ [
255
+ "as_unit",
256
+ "astimezone",
257
+ "ceil",
258
+ "combine",
259
+ "ctime",
260
+ "date",
261
+ "day_name",
262
+ "dst",
263
+ "floor",
264
+ "fromisocalendar",
265
+ "fromisoformat",
266
+ "fromordinal",
267
+ "fromtimestamp",
268
+ "isocalendar",
269
+ "isoformat",
270
+ "isoweekday",
271
+ "month_name",
272
+ "now",
273
+ "replace",
274
+ "round",
275
+ "strftime",
276
+ "strptime",
277
+ "time",
278
+ "timestamp",
279
+ "timetuple",
280
+ "timetz",
281
+ "to_datetime64",
282
+ "to_numpy",
283
+ "to_pydatetime",
284
+ "today",
285
+ "toordinal",
286
+ "tz_convert",
287
+ "tz_localize",
288
+ "tzname",
289
+ "utcfromtimestamp",
290
+ "utcnow",
291
+ "utcoffset",
292
+ "utctimetuple",
293
+ "weekday",
294
+ ],
295
+ ),
296
+ (Timedelta, ["total_seconds"]),
297
+ ],
298
+ )
299
+ def test_overlap_public_nat_methods(klass, expected):
300
+ # see gh-17327
301
+ #
302
+ # NaT should have *most* of the Timestamp and Timedelta methods.
303
+ # In case when Timestamp, Timedelta, and NaT are overlap, the overlap
304
+ # is considered to be with Timestamp and NaT, not Timedelta.
305
+ assert _get_overlap_public_nat_methods(klass) == expected
306
+
307
+
308
+ @pytest.mark.parametrize(
309
+ "compare",
310
+ (
311
+ _get_overlap_public_nat_methods(Timestamp, True)
312
+ + _get_overlap_public_nat_methods(Timedelta, True)
313
+ ),
314
+ ids=lambda x: f"{x[0].__name__}.{x[1]}",
315
+ )
316
+ def test_nat_doc_strings(compare):
317
+ # see gh-17327
318
+ #
319
+ # The docstrings for overlapping methods should match.
320
+ klass, method = compare
321
+ klass_doc = getattr(klass, method).__doc__
322
+
323
+ if klass == Timestamp and method == "isoformat":
324
+ pytest.skip(
325
+ "Ignore differences with Timestamp.isoformat() as they're intentional"
326
+ )
327
+
328
+ if method == "to_numpy":
329
+ # GH#44460 can return either dt64 or td64 depending on dtype,
330
+ # different docstring is intentional
331
+ pytest.skip(f"different docstring for {method} is intentional")
332
+
333
+ nat_doc = getattr(NaT, method).__doc__
334
+ assert klass_doc == nat_doc
335
+
336
+
337
+ _ops = {
338
+ "left_plus_right": lambda a, b: a + b,
339
+ "right_plus_left": lambda a, b: b + a,
340
+ "left_minus_right": lambda a, b: a - b,
341
+ "right_minus_left": lambda a, b: b - a,
342
+ "left_times_right": lambda a, b: a * b,
343
+ "right_times_left": lambda a, b: b * a,
344
+ "left_div_right": lambda a, b: a / b,
345
+ "right_div_left": lambda a, b: b / a,
346
+ }
347
+
348
+
349
+ @pytest.mark.parametrize("op_name", list(_ops.keys()))
350
+ @pytest.mark.parametrize(
351
+ "value,val_type",
352
+ [
353
+ (2, "scalar"),
354
+ (1.5, "floating"),
355
+ (np.nan, "floating"),
356
+ ("foo", "str"),
357
+ (timedelta(3600), "timedelta"),
358
+ (Timedelta("5s"), "timedelta"),
359
+ (datetime(2014, 1, 1), "timestamp"),
360
+ (Timestamp("2014-01-01"), "timestamp"),
361
+ (Timestamp("2014-01-01", tz="UTC"), "timestamp"),
362
+ (Timestamp("2014-01-01", tz="US/Eastern"), "timestamp"),
363
+ (datetime(2014, 1, 1).astimezone(zoneinfo.ZoneInfo("Asia/Tokyo")), "timestamp"),
364
+ ],
365
+ )
366
+ def test_nat_arithmetic_scalar(op_name, value, val_type):
367
+ # see gh-6873
368
+ invalid_ops = {
369
+ "scalar": {"right_div_left"},
370
+ "floating": {
371
+ "right_div_left",
372
+ "left_minus_right",
373
+ "right_minus_left",
374
+ "left_plus_right",
375
+ "right_plus_left",
376
+ },
377
+ "str": set(_ops.keys()),
378
+ "timedelta": {"left_times_right", "right_times_left"},
379
+ "timestamp": {
380
+ "left_times_right",
381
+ "right_times_left",
382
+ "left_div_right",
383
+ "right_div_left",
384
+ },
385
+ }
386
+
387
+ op = _ops[op_name]
388
+
389
+ if op_name in invalid_ops.get(val_type, set()):
390
+ if (
391
+ val_type == "timedelta"
392
+ and "times" in op_name
393
+ and isinstance(value, Timedelta)
394
+ ):
395
+ typs = "(Timedelta|NaTType)"
396
+ msg = rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'"
397
+ elif val_type == "str":
398
+ # un-specific check here because the message comes from str
399
+ # and varies by method
400
+ msg = "|".join(
401
+ [
402
+ "can only concatenate str",
403
+ "unsupported operand type",
404
+ "can't multiply sequence",
405
+ "Can't convert 'NaTType'",
406
+ "must be str, not NaTType",
407
+ ]
408
+ )
409
+ else:
410
+ msg = "unsupported operand type"
411
+
412
+ with pytest.raises(TypeError, match=msg):
413
+ op(NaT, value)
414
+ else:
415
+ if val_type == "timedelta" and "div" in op_name:
416
+ expected = np.nan
417
+ else:
418
+ expected = NaT
419
+
420
+ assert op(NaT, value) is expected
421
+
422
+
423
+ @pytest.mark.parametrize(
424
+ "val,expected",
425
+ [(np.nan, NaT), (NaT, np.nan), (np.timedelta64("NaT", "ns"), np.nan)],
426
+ )
427
+ def test_nat_rfloordiv_timedelta(val, expected):
428
+ # see gh-#18846
429
+ #
430
+ # See also test_timedelta.TestTimedeltaArithmetic.test_floordiv
431
+ td = Timedelta(hours=3, minutes=4)
432
+ assert td // val is expected
433
+
434
+
435
+ @pytest.mark.parametrize(
436
+ "op_name",
437
+ ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
438
+ )
439
+ @pytest.mark.parametrize(
440
+ "value",
441
+ [
442
+ DatetimeIndex(["2011-01-01", "2011-01-02"], dtype="M8[ns]", name="x"),
443
+ DatetimeIndex(
444
+ ["2011-01-01", "2011-01-02"], dtype="M8[ns, US/Eastern]", name="x"
445
+ ),
446
+ DatetimeArray._from_sequence(["2011-01-01", "2011-01-02"], dtype="M8[ns]"),
447
+ DatetimeArray._from_sequence(
448
+ ["2011-01-01", "2011-01-02"], dtype=DatetimeTZDtype(tz="US/Pacific")
449
+ ),
450
+ TimedeltaIndex(["1 day", "2 day"], name="x"),
451
+ ],
452
+ )
453
+ def test_nat_arithmetic_index(op_name, value):
454
+ # see gh-11718
455
+ exp_name = "x"
456
+ exp_data = [NaT] * 2
457
+
458
+ if value.dtype.kind == "M" and "plus" in op_name:
459
+ expected = DatetimeIndex(exp_data, tz=value.tz, name=exp_name)
460
+ else:
461
+ expected = TimedeltaIndex(exp_data, name=exp_name)
462
+ expected = expected.as_unit(value.unit)
463
+
464
+ if not isinstance(value, Index):
465
+ expected = expected.array
466
+
467
+ op = _ops[op_name]
468
+ result = op(NaT, value)
469
+ tm.assert_equal(result, expected)
470
+
471
+
472
+ @pytest.mark.parametrize(
473
+ "op_name",
474
+ ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
475
+ )
476
+ @pytest.mark.parametrize("box", [TimedeltaIndex, Series, TimedeltaArray._from_sequence])
477
+ def test_nat_arithmetic_td64_vector(op_name, box):
478
+ # see gh-19124
479
+ vec = box(["1 day", "2 day"], dtype="timedelta64[ns]")
480
+ box_nat = box([NaT, NaT], dtype="timedelta64[ns]")
481
+ tm.assert_equal(_ops[op_name](vec, NaT), box_nat)
482
+
483
+
484
+ @pytest.mark.parametrize(
485
+ "dtype,op,out_dtype",
486
+ [
487
+ ("datetime64[ns]", operator.add, "datetime64[ns]"),
488
+ ("datetime64[ns]", roperator.radd, "datetime64[ns]"),
489
+ ("datetime64[ns]", operator.sub, "timedelta64[ns]"),
490
+ ("datetime64[ns]", roperator.rsub, "timedelta64[ns]"),
491
+ ("timedelta64[ns]", operator.add, "datetime64[ns]"),
492
+ ("timedelta64[ns]", roperator.radd, "datetime64[ns]"),
493
+ ("timedelta64[ns]", operator.sub, "datetime64[ns]"),
494
+ ("timedelta64[ns]", roperator.rsub, "timedelta64[ns]"),
495
+ ],
496
+ )
497
+ def test_nat_arithmetic_ndarray(dtype, op, out_dtype):
498
+ other = np.arange(10).astype(dtype)
499
+ result = op(NaT, other)
500
+
501
+ expected = np.empty(other.shape, dtype=out_dtype)
502
+ expected.fill("NaT")
503
+ tm.assert_numpy_array_equal(result, expected)
504
+
505
+
506
+ def test_nat_pinned_docstrings():
507
+ # see gh-17327
508
+ assert NaT.ctime.__doc__ == Timestamp.ctime.__doc__
509
+
510
+
511
+ def test_to_numpy_alias():
512
+ # GH 24653: alias .to_numpy() for scalars
513
+ expected = NaT.to_datetime64()
514
+ result = NaT.to_numpy()
515
+
516
+ assert isna(expected) and isna(result)
517
+
518
+ # GH#44460
519
+ result = NaT.to_numpy("M8[s]")
520
+ assert isinstance(result, np.datetime64)
521
+ assert result.dtype == "M8[s]"
522
+
523
+ result = NaT.to_numpy("m8[ns]")
524
+ assert isinstance(result, np.timedelta64)
525
+ assert result.dtype == "m8[ns]"
526
+
527
+ result = NaT.to_numpy("m8[s]")
528
+ assert isinstance(result, np.timedelta64)
529
+ assert result.dtype == "m8[s]"
530
+
531
+ with pytest.raises(ValueError, match="NaT.to_numpy dtype must be a "):
532
+ NaT.to_numpy(np.int64)
533
+
534
+
535
+ @pytest.mark.parametrize(
536
+ "other",
537
+ [
538
+ Timedelta(0),
539
+ Timedelta(0).to_pytimedelta(),
540
+ Timedelta(0).to_timedelta64(),
541
+ Timestamp(0),
542
+ Timestamp(0).to_pydatetime(),
543
+ Timestamp(0).to_datetime64(),
544
+ Timestamp(0).tz_localize("UTC"),
545
+ NaT,
546
+ ],
547
+ )
548
+ def test_nat_comparisons(compare_operators_no_eq_ne, other):
549
+ # GH 26039
550
+ opname = compare_operators_no_eq_ne
551
+
552
+ assert getattr(NaT, opname)(other) is False
553
+
554
+ op = getattr(operator, opname.strip("_"))
555
+ assert op(NaT, other) is False
556
+ assert op(other, NaT) is False
557
+
558
+
559
+ @pytest.mark.parametrize("other_and_type", [("foo", "str"), (2, "int"), (2.0, "float")])
560
+ @pytest.mark.parametrize(
561
+ "symbol_and_op",
562
+ [("<=", operator.le), ("<", operator.lt), (">=", operator.ge), (">", operator.gt)],
563
+ )
564
+ def test_nat_comparisons_invalid(other_and_type, symbol_and_op):
565
+ # GH#35585
566
+ other, other_type = other_and_type
567
+ symbol, op = symbol_and_op
568
+
569
+ assert not NaT == other
570
+ assert not other == NaT
571
+
572
+ assert NaT != other
573
+ assert other != NaT
574
+
575
+ msg = f"'{symbol}' not supported between instances of 'NaTType' and '{other_type}'"
576
+ with pytest.raises(TypeError, match=msg):
577
+ op(NaT, other)
578
+
579
+ msg = f"'{symbol}' not supported between instances of '{other_type}' and 'NaTType'"
580
+ with pytest.raises(TypeError, match=msg):
581
+ op(other, NaT)
582
+
583
+
584
+ @pytest.mark.parametrize(
585
+ "other",
586
+ [
587
+ np.array(["foo"] * 2, dtype=object),
588
+ np.array([2, 3], dtype="int64"),
589
+ np.array([2.0, 3.5], dtype="float64"),
590
+ ],
591
+ ids=["str", "int", "float"],
592
+ )
593
+ def test_nat_comparisons_invalid_ndarray(other):
594
+ # GH#40722
595
+ expected = np.array([False, False])
596
+ result = NaT == other
597
+ tm.assert_numpy_array_equal(result, expected)
598
+ result = other == NaT
599
+ tm.assert_numpy_array_equal(result, expected)
600
+
601
+ expected = np.array([True, True])
602
+ result = NaT != other
603
+ tm.assert_numpy_array_equal(result, expected)
604
+ result = other != NaT
605
+ tm.assert_numpy_array_equal(result, expected)
606
+
607
+ for symbol, op in [
608
+ ("<=", operator.le),
609
+ ("<", operator.lt),
610
+ (">=", operator.ge),
611
+ (">", operator.gt),
612
+ ]:
613
+ msg = f"'{symbol}' not supported between"
614
+
615
+ with pytest.raises(TypeError, match=msg):
616
+ op(NaT, other)
617
+
618
+ if other.dtype == np.dtype("object"):
619
+ # uses the reverse operator, so symbol changes
620
+ msg = None
621
+ with pytest.raises(TypeError, match=msg):
622
+ op(other, NaT)
623
+
624
+
625
+ def test_compare_date(fixed_now_ts):
626
+ # GH#39151 comparing NaT with date object is deprecated
627
+ # See also: tests.scalar.timestamps.test_comparisons::test_compare_date
628
+
629
+ dt = fixed_now_ts.to_pydatetime().date()
630
+
631
+ msg = "Cannot compare NaT with datetime.date object"
632
+ for left, right in [(NaT, dt), (dt, NaT)]:
633
+ assert not left == right
634
+ assert left != right
635
+
636
+ with pytest.raises(TypeError, match=msg):
637
+ left < right
638
+ with pytest.raises(TypeError, match=msg):
639
+ left <= right
640
+ with pytest.raises(TypeError, match=msg):
641
+ left > right
642
+ with pytest.raises(TypeError, match=msg):
643
+ left >= right
644
+
645
+
646
+ @pytest.mark.parametrize(
647
+ "obj",
648
+ [
649
+ offsets.YearEnd(2),
650
+ offsets.YearBegin(2),
651
+ offsets.MonthBegin(1),
652
+ offsets.MonthEnd(2),
653
+ offsets.MonthEnd(12),
654
+ offsets.Day(2),
655
+ offsets.Day(5),
656
+ offsets.Hour(24),
657
+ offsets.Hour(3),
658
+ offsets.Minute(),
659
+ np.timedelta64(3, "h"),
660
+ np.timedelta64(4, "h"),
661
+ np.timedelta64(3200, "s"),
662
+ np.timedelta64(3600, "s"),
663
+ np.timedelta64(3600 * 24, "s"),
664
+ np.timedelta64(2, "D"),
665
+ np.timedelta64(365, "D"),
666
+ timedelta(-2),
667
+ timedelta(365),
668
+ timedelta(minutes=120),
669
+ timedelta(days=4, minutes=180),
670
+ timedelta(hours=23),
671
+ timedelta(hours=23, minutes=30),
672
+ timedelta(hours=48),
673
+ ],
674
+ )
675
+ def test_nat_addsub_tdlike_scalar(obj):
676
+ assert NaT + obj is NaT
677
+ assert obj + NaT is NaT
678
+ assert NaT - obj is NaT
679
+
680
+
681
+ def test_pickle(temp_file):
682
+ # GH#4606
683
+ p = tm.round_trip_pickle(NaT, temp_file)
684
+ assert p is NaT
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
4
+ from pandas.errors import OutOfBoundsTimedelta
5
+
6
+ from pandas import Timedelta
7
+
8
+
9
+ class TestAsUnit:
10
+ def test_as_unit(self):
11
+ td = Timedelta(days=1)
12
+
13
+ assert td.as_unit("us") is td
14
+
15
+ res = td.as_unit("ns")
16
+ assert res._value == td._value * 1000
17
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ns.value
18
+
19
+ rt = res.as_unit("us")
20
+ assert rt._value == td._value
21
+ assert rt._creso == td._creso
22
+
23
+ res = td.as_unit("ms")
24
+ assert res._value == td._value // 1_000
25
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
26
+
27
+ rt = res.as_unit("us")
28
+ assert rt._value == td._value
29
+ assert rt._creso == td._creso
30
+
31
+ res = td.as_unit("s")
32
+ assert res._value == td._value // 1_000_000
33
+ assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
34
+
35
+ rt = res.as_unit("us")
36
+ assert rt._value == td._value
37
+ assert rt._creso == td._creso
38
+
39
+ def test_as_unit_overflows(self):
40
+ # microsecond that would be just out of bounds for nano
41
+ us = 9223372800000000
42
+ td = Timedelta._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value)
43
+
44
+ msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
45
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
46
+ td.as_unit("ns")
47
+
48
+ res = td.as_unit("ms")
49
+ assert res._value == us // 1000
50
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
51
+
52
+ def test_as_unit_rounding(self):
53
+ td = Timedelta(microseconds=1500)
54
+ res = td.as_unit("ms")
55
+
56
+ expected = Timedelta(milliseconds=1)
57
+ assert res == expected
58
+
59
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
60
+ assert res._value == 1
61
+
62
+ with pytest.raises(ValueError, match="Cannot losslessly convert units"):
63
+ td.as_unit("ms", round_ok=False)
64
+
65
+ def test_as_unit_non_nano(self):
66
+ # case where we are going neither to nor from nano
67
+ td = Timedelta(days=1).as_unit("ms")
68
+ assert td.days == 1
69
+ assert td._value == 86_400_000
70
+ assert td.components.days == 1
71
+ assert td._d == 1
72
+ assert td.total_seconds() == 86400
73
+
74
+ res = td.as_unit("us")
75
+ assert res._value == 86_400_000_000
76
+ assert res.components.days == 1
77
+ assert res.components.hours == 0
78
+ assert res._d == 1
79
+ assert res._h == 0
80
+ assert res.total_seconds() == 86400
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from hypothesis import (
2
+ given,
3
+ strategies as st,
4
+ )
5
+ import numpy as np
6
+ import pytest
7
+
8
+ from pandas._libs import lib
9
+ from pandas._libs.tslibs import iNaT
10
+ from pandas.errors import OutOfBoundsTimedelta
11
+
12
+ from pandas import Timedelta
13
+
14
+
15
+ class TestTimedeltaRound:
16
+ @pytest.mark.parametrize(
17
+ "freq,s1,s2",
18
+ [
19
+ # This first case has s1, s2 being the same as t1,t2 below
20
+ (
21
+ "ns",
22
+ "1 days 02:34:56.789123456",
23
+ "-1 days 02:34:56.789123456",
24
+ ),
25
+ (
26
+ "us",
27
+ "1 days 02:34:56.789123000",
28
+ "-1 days 02:34:56.789123000",
29
+ ),
30
+ (
31
+ "ms",
32
+ "1 days 02:34:56.789000000",
33
+ "-1 days 02:34:56.789000000",
34
+ ),
35
+ ("s", "1 days 02:34:57", "-1 days 02:34:57"),
36
+ ("2s", "1 days 02:34:56", "-1 days 02:34:56"),
37
+ ("5s", "1 days 02:34:55", "-1 days 02:34:55"),
38
+ ("min", "1 days 02:35:00", "-1 days 02:35:00"),
39
+ ("12min", "1 days 02:36:00", "-1 days 02:36:00"),
40
+ ("h", "1 days 03:00:00", "-1 days 03:00:00"),
41
+ ("D", "1 days", "-1 days"),
42
+ ],
43
+ )
44
+ def test_round(self, freq, s1, s2):
45
+ s1 = Timedelta(s1)
46
+ s2 = Timedelta(s2)
47
+ t1 = Timedelta("1 days 02:34:56.789123456")
48
+ t2 = Timedelta("-1 days 02:34:56.789123456")
49
+
50
+ r1 = t1.round(freq)
51
+ assert r1 == s1
52
+ r2 = t2.round(freq)
53
+ assert r2 == s2
54
+
55
+ def test_round_invalid(self):
56
+ t1 = Timedelta("1 days 02:34:56.789123456")
57
+
58
+ for freq, msg in [
59
+ ("YE", "<YearEnd: month=12> is a non-fixed frequency"),
60
+ ("ME", "<MonthEnd> is a non-fixed frequency"),
61
+ ("foobar", "Invalid frequency: foobar"),
62
+ ]:
63
+ with pytest.raises(ValueError, match=msg):
64
+ t1.round(freq)
65
+
66
+ def test_round_implementation_bounds(self):
67
+ # See also: analogous test for Timestamp
68
+ # GH#38964
69
+ result = Timedelta.min.ceil("s")
70
+ expected = Timedelta.min + Timedelta(seconds=1) - Timedelta(145224193)
71
+ assert result == expected
72
+
73
+ result = Timedelta.max.floor("s")
74
+ expected = Timedelta.max - Timedelta(854775807)
75
+ assert result == expected
76
+
77
+ msg = (
78
+ r"Cannot round -106752 days \+00:12:43.145224193 to freq=s without overflow"
79
+ )
80
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
81
+ Timedelta.min.floor("s")
82
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
83
+ Timedelta.min.round("s")
84
+
85
+ msg = "Cannot round 106751 days 23:47:16.854775807 to freq=s without overflow"
86
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
87
+ Timedelta.max.ceil("s")
88
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
89
+ Timedelta.max.round("s")
90
+
91
+ @pytest.mark.slow
92
+ @given(val=st.integers(min_value=iNaT + 1, max_value=lib.i8max))
93
+ @pytest.mark.parametrize(
94
+ "method", [Timedelta.round, Timedelta.floor, Timedelta.ceil]
95
+ )
96
+ def test_round_sanity(self, val, method):
97
+ cls = Timedelta
98
+ err_cls = OutOfBoundsTimedelta
99
+
100
+ val = np.int64(val)
101
+ td = cls(val)
102
+
103
+ def checker(ts, nanos, unit):
104
+ # First check that we do raise in cases where we should
105
+ if nanos == 1:
106
+ pass
107
+ else:
108
+ div, mod = divmod(ts._value, nanos)
109
+ diff = int(nanos - mod)
110
+ lb = ts._value - mod
111
+ assert lb <= ts._value # i.e. no overflows with python ints
112
+ ub = ts._value + diff
113
+ assert ub > ts._value # i.e. no overflows with python ints
114
+
115
+ msg = "without overflow"
116
+ if mod == 0:
117
+ # We should never be raising in this
118
+ pass
119
+ elif method is cls.ceil:
120
+ if ub > cls.max._value:
121
+ with pytest.raises(err_cls, match=msg):
122
+ method(ts, unit)
123
+ return
124
+ elif method is cls.floor:
125
+ if lb < cls.min._value:
126
+ with pytest.raises(err_cls, match=msg):
127
+ method(ts, unit)
128
+ return
129
+ elif mod >= diff:
130
+ if ub > cls.max._value:
131
+ with pytest.raises(err_cls, match=msg):
132
+ method(ts, unit)
133
+ return
134
+ elif lb < cls.min._value:
135
+ with pytest.raises(err_cls, match=msg):
136
+ method(ts, unit)
137
+ return
138
+
139
+ res = method(ts, unit)
140
+
141
+ td = res - ts
142
+ diff = abs(td._value)
143
+ assert diff < nanos
144
+ assert res._value % nanos == 0
145
+
146
+ if method is cls.round:
147
+ assert diff <= nanos / 2
148
+ elif method is cls.floor:
149
+ assert res <= ts
150
+ elif method is cls.ceil:
151
+ assert res >= ts
152
+
153
+ nanos = 1
154
+ checker(td, nanos, "ns")
155
+
156
+ nanos = 1000
157
+ checker(td, nanos, "us")
158
+
159
+ nanos = 1_000_000
160
+ checker(td, nanos, "ms")
161
+
162
+ nanos = 1_000_000_000
163
+ checker(td, nanos, "s")
164
+
165
+ nanos = 60 * 1_000_000_000
166
+ checker(td, nanos, "min")
167
+
168
+ nanos = 60 * 60 * 1_000_000_000
169
+ checker(td, nanos, "h")
170
+
171
+ nanos = 24 * 60 * 60 * 1_000_000_000
172
+ checker(td, nanos, "D")
173
+
174
+ def test_round_non_nano(self, unit):
175
+ td = Timedelta("1 days 02:34:57").as_unit(unit)
176
+
177
+ res = td.round("min")
178
+ assert res == Timedelta("1 days 02:35:00")
179
+ assert res._creso == td._creso
180
+
181
+ res = td.floor("min")
182
+ assert res == Timedelta("1 days 02:34:00")
183
+ assert res._creso == td._creso
184
+
185
+ res = td.ceil("min")
186
+ assert res == Timedelta("1 days 02:35:00")
187
+ assert res._creso == td._creso
188
+
189
+ def test_round_freq_finer_than_resolution(self):
190
+ # GH#64828
191
+ td = Timedelta(1.0, unit="days").as_unit("s")
192
+ assert td.unit == "s"
193
+ assert td.round("100ms") == Timedelta("1 days 00:00:00")
194
+ assert td.floor("100ms") == Timedelta("1 days 00:00:00")
195
+ assert td.ceil("100ms") == Timedelta("1 days 00:00:00")
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py ADDED
@@ -0,0 +1,1251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tests for scalar Timedelta arithmetic ops
3
+ """
4
+
5
+ from datetime import (
6
+ datetime,
7
+ timedelta,
8
+ )
9
+ import operator
10
+
11
+ import numpy as np
12
+ import pytest
13
+
14
+ from pandas.errors import (
15
+ OutOfBoundsTimedelta,
16
+ Pandas4Warning,
17
+ )
18
+
19
+ import pandas as pd
20
+ from pandas import (
21
+ NaT,
22
+ Timedelta,
23
+ Timestamp,
24
+ offsets,
25
+ )
26
+ import pandas._testing as tm
27
+ from pandas.core import ops
28
+
29
+
30
+ class TestTimedeltaAdditionSubtraction:
31
+ """
32
+ Tests for Timedelta methods:
33
+
34
+ __add__, __radd__,
35
+ __sub__, __rsub__
36
+ """
37
+
38
+ def test_td_add_sub_pydatetime(self, unit):
39
+ # GH#53643
40
+ td = Timedelta(hours=23).as_unit(unit)
41
+ dt = datetime(2016, 1, 1)
42
+
43
+ expected = datetime(2016, 1, 1, 23)
44
+ result = dt + td
45
+ assert result == expected
46
+ result = td + dt
47
+ assert result == expected
48
+
49
+ expected = datetime(2015, 12, 31, 1)
50
+ result = dt - td
51
+ assert result == expected
52
+
53
+ @pytest.mark.parametrize(
54
+ "ten_seconds",
55
+ [
56
+ Timedelta(10, unit="s"),
57
+ timedelta(seconds=10),
58
+ np.timedelta64(10, "s"),
59
+ np.timedelta64(10000000000, "ns"),
60
+ offsets.Second(10),
61
+ ],
62
+ )
63
+ def test_td_add_sub_ten_seconds(self, ten_seconds):
64
+ # GH#6808
65
+ base = Timestamp("20130101 09:01:12.123456")
66
+ expected_add = Timestamp("20130101 09:01:22.123456")
67
+ expected_sub = Timestamp("20130101 09:01:02.123456")
68
+
69
+ result = base + ten_seconds
70
+ assert result == expected_add
71
+
72
+ result = base - ten_seconds
73
+ assert result == expected_sub
74
+
75
+ @pytest.mark.parametrize(
76
+ "one_day_ten_secs",
77
+ [
78
+ Timedelta("1 day, 00:00:10"),
79
+ Timedelta("1 days, 00:00:10"),
80
+ timedelta(days=1, seconds=10),
81
+ np.timedelta64(1, "D") + np.timedelta64(10, "s"),
82
+ offsets.Day() + offsets.Second(10),
83
+ ],
84
+ )
85
+ def test_td_add_sub_one_day_ten_seconds(self, one_day_ten_secs):
86
+ # GH#6808
87
+ base = Timestamp("20130102 09:01:12.123456")
88
+ expected_add = Timestamp("20130103 09:01:22.123456")
89
+ expected_sub = Timestamp("20130101 09:01:02.123456")
90
+
91
+ result = base + one_day_ten_secs
92
+ assert result == expected_add
93
+
94
+ result = base - one_day_ten_secs
95
+ assert result == expected_sub
96
+
97
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
98
+ def test_td_add_datetimelike_scalar(self, op):
99
+ # GH#19738
100
+ td = Timedelta(10, unit="D")
101
+
102
+ result = op(td, datetime(2016, 1, 1))
103
+ if op is operator.add:
104
+ # datetime + Timedelta does _not_ call Timedelta.__radd__,
105
+ # so we get a datetime back instead of a Timestamp
106
+ assert isinstance(result, Timestamp)
107
+ assert result == Timestamp(2016, 1, 11)
108
+
109
+ result = op(td, Timestamp("2018-01-12 18:09"))
110
+ assert isinstance(result, Timestamp)
111
+ assert result == Timestamp("2018-01-22 18:09")
112
+
113
+ result = op(td, np.datetime64("2018-01-12"))
114
+ assert isinstance(result, Timestamp)
115
+ assert result == Timestamp("2018-01-22")
116
+
117
+ result = op(td, NaT)
118
+ assert result is NaT
119
+
120
+ def test_td_add_timestamp_overflow(self):
121
+ ts = Timestamp("1700-01-01").as_unit("ns")
122
+ msg = "Cannot cast 259987 days 00:00:00 to unit='ns' without overflow."
123
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
124
+ ts + Timedelta(13 * 19999, unit="D")
125
+
126
+ msg = "Cannot cast 259987 days 00:00:00 to unit='ns' without overflow"
127
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
128
+ ts + timedelta(days=13 * 19999)
129
+
130
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
131
+ def test_td_add_td(self, op):
132
+ td = Timedelta(10, unit="D")
133
+
134
+ result = op(td, Timedelta(days=10))
135
+ assert isinstance(result, Timedelta)
136
+ assert result == Timedelta(days=20)
137
+
138
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
139
+ def test_td_add_pytimedelta(self, op):
140
+ td = Timedelta(10, unit="D")
141
+ result = op(td, timedelta(days=9))
142
+ assert isinstance(result, Timedelta)
143
+ assert result == Timedelta(days=19)
144
+
145
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
146
+ def test_td_add_timedelta64(self, op):
147
+ td = Timedelta(10, unit="D")
148
+ result = op(td, np.timedelta64(-4, "D"))
149
+ assert isinstance(result, Timedelta)
150
+ assert result == Timedelta(days=6)
151
+
152
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
153
+ def test_td_add_offset(self, op):
154
+ td = Timedelta(10, unit="D")
155
+
156
+ result = op(td, offsets.Hour(6))
157
+ assert isinstance(result, Timedelta)
158
+ assert result == Timedelta(days=10, hours=6)
159
+
160
+ def test_td_sub_td(self):
161
+ td = Timedelta(10, unit="D")
162
+ expected = Timedelta(0, unit="ns")
163
+ result = td - td
164
+ assert isinstance(result, Timedelta)
165
+ assert result == expected
166
+
167
+ def test_td_sub_pytimedelta(self):
168
+ td = Timedelta(10, unit="D")
169
+ expected = Timedelta(0, unit="ns")
170
+
171
+ result = td - td.to_pytimedelta()
172
+ assert isinstance(result, Timedelta)
173
+ assert result == expected
174
+
175
+ result = td.to_pytimedelta() - td
176
+ assert isinstance(result, Timedelta)
177
+ assert result == expected
178
+
179
+ def test_td_sub_timedelta64(self):
180
+ td = Timedelta(10, unit="D")
181
+ expected = Timedelta(0, unit="ns")
182
+
183
+ result = td - td.to_timedelta64()
184
+ assert isinstance(result, Timedelta)
185
+ assert result == expected
186
+
187
+ result = td.to_timedelta64() - td
188
+ assert isinstance(result, Timedelta)
189
+ assert result == expected
190
+
191
+ def test_td_sub_nat(self):
192
+ # In this context pd.NaT is treated as timedelta-like
193
+ td = Timedelta(10, unit="D")
194
+ result = td - NaT
195
+ assert result is NaT
196
+
197
+ def test_td_sub_td64_nat(self):
198
+ td = Timedelta(10, unit="D")
199
+ td_nat = np.timedelta64("NaT", "ns")
200
+
201
+ result = td - td_nat
202
+ assert result is NaT
203
+
204
+ result = td_nat - td
205
+ assert result is NaT
206
+
207
+ def test_td_sub_offset(self):
208
+ td = Timedelta(10, unit="D")
209
+ result = td - offsets.Hour(1)
210
+ assert isinstance(result, Timedelta)
211
+ assert result == Timedelta(239, unit="h")
212
+
213
+ def test_td_add_sub_numeric_raises(self):
214
+ td = Timedelta(10, unit="D")
215
+ msg = "unsupported operand type"
216
+ for other in [2, 2.0, np.int64(2), np.float64(2)]:
217
+ with pytest.raises(TypeError, match=msg):
218
+ td + other
219
+ with pytest.raises(TypeError, match=msg):
220
+ other + td
221
+ with pytest.raises(TypeError, match=msg):
222
+ td - other
223
+ with pytest.raises(TypeError, match=msg):
224
+ other - td
225
+
226
+ def test_td_add_sub_int_ndarray(self):
227
+ td = Timedelta("1 day")
228
+ other = np.array([1])
229
+
230
+ msg = r"unsupported operand type\(s\) for \+: 'Timedelta' and 'int'"
231
+ with pytest.raises(TypeError, match=msg):
232
+ td + np.array([1])
233
+
234
+ msg = "|".join(
235
+ [
236
+ (
237
+ r"unsupported operand type\(s\) for \+: 'numpy.ndarray' "
238
+ "and 'Timedelta'"
239
+ ),
240
+ # This message goes on to say "Please do not rely on this error;
241
+ # it may not be given on all Python implementations"
242
+ "Concatenation operation is not implemented for NumPy arrays",
243
+ ]
244
+ )
245
+ with pytest.raises(TypeError, match=msg):
246
+ other + td
247
+ msg = r"unsupported operand type\(s\) for -: 'Timedelta' and 'int'"
248
+ with pytest.raises(TypeError, match=msg):
249
+ td - other
250
+ msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timedelta'"
251
+ with pytest.raises(TypeError, match=msg):
252
+ other - td
253
+
254
+ def test_td_rsub_nat(self):
255
+ td = Timedelta(10, unit="D")
256
+ result = NaT - td
257
+ assert result is NaT
258
+
259
+ result = np.datetime64("NaT", "ns") - td
260
+ assert result is NaT
261
+
262
+ def test_td_rsub_offset(self):
263
+ result = offsets.Hour(1) - Timedelta(10, unit="D")
264
+ assert isinstance(result, Timedelta)
265
+ assert result == Timedelta(-239, unit="h")
266
+
267
+ def test_td_sub_timedeltalike_object_dtype_array(self):
268
+ # GH#21980
269
+ arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
270
+ exp = np.array([Timestamp("20121231 9:01"), Timestamp("20121229 9:02")])
271
+ res = arr - Timedelta("1D")
272
+ tm.assert_numpy_array_equal(res, exp)
273
+
274
+ def test_td_sub_mixed_most_timedeltalike_object_dtype_array(self):
275
+ # GH#21980
276
+ now = Timestamp("2021-11-09 09:54:00")
277
+ arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
278
+ exp = np.array(
279
+ [
280
+ now - Timedelta("1D"),
281
+ Timedelta("0D"),
282
+ np.timedelta64(2, "h") - Timedelta("1D"),
283
+ ]
284
+ )
285
+ res = arr - Timedelta("1D")
286
+ tm.assert_numpy_array_equal(res, exp)
287
+
288
+ def test_td_rsub_mixed_most_timedeltalike_object_dtype_array(self):
289
+ # GH#21980
290
+ now = Timestamp("2021-11-09 09:54:00")
291
+ arr = np.array([now, Timedelta("1D"), np.timedelta64(2, "h")])
292
+ msg = r"unsupported operand type\(s\) for \-: 'Timedelta' and 'Timestamp'"
293
+ with pytest.raises(TypeError, match=msg):
294
+ Timedelta("1D") - arr
295
+
296
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
297
+ def test_td_add_timedeltalike_object_dtype_array(self, op):
298
+ # GH#21980
299
+ arr = np.array([Timestamp("20130101 9:01"), Timestamp("20121230 9:02")])
300
+ exp = np.array([Timestamp("20130102 9:01"), Timestamp("20121231 9:02")])
301
+ res = op(arr, Timedelta("1D"))
302
+ tm.assert_numpy_array_equal(res, exp)
303
+
304
+ @pytest.mark.parametrize("op", [operator.add, ops.radd])
305
+ def test_td_add_mixed_timedeltalike_object_dtype_array(self, op):
306
+ # GH#21980
307
+ now = Timestamp("2021-11-09 09:54:00")
308
+ arr = np.array([now, Timedelta("1D")])
309
+ exp = np.array([now + Timedelta("1D"), Timedelta("2D")])
310
+ res = op(arr, Timedelta("1D"))
311
+ tm.assert_numpy_array_equal(res, exp)
312
+
313
+ def test_td_add_sub_td64_ndarray(self):
314
+ td = Timedelta("1 day")
315
+
316
+ other = np.array([td.to_timedelta64()])
317
+ expected = np.array([Timedelta("2 Days").to_timedelta64()])
318
+
319
+ result = td + other
320
+ tm.assert_numpy_array_equal(result, expected)
321
+ result = other + td
322
+ tm.assert_numpy_array_equal(result, expected)
323
+
324
+ result = td - other
325
+ tm.assert_numpy_array_equal(result, expected * 0)
326
+ result = other - td
327
+ tm.assert_numpy_array_equal(result, expected * 0)
328
+
329
+ def test_td_add_sub_dt64_ndarray(self):
330
+ td = Timedelta("1 day")
331
+ other = np.array(["2000-01-01"], dtype="M8[ns]")
332
+
333
+ expected = np.array(["2000-01-02"], dtype="M8[ns]")
334
+ tm.assert_numpy_array_equal(td + other, expected)
335
+ tm.assert_numpy_array_equal(other + td, expected)
336
+
337
+ expected = np.array(["1999-12-31"], dtype="M8[ns]")
338
+ tm.assert_numpy_array_equal(-td + other, expected)
339
+ tm.assert_numpy_array_equal(other - td, expected)
340
+
341
+ def test_td_add_sub_ndarray_0d(self):
342
+ td = Timedelta("1 day")
343
+ other = np.array(td.asm8)
344
+
345
+ result = td + other
346
+ assert isinstance(result, Timedelta)
347
+ assert result == 2 * td
348
+
349
+ result = other + td
350
+ assert isinstance(result, Timedelta)
351
+ assert result == 2 * td
352
+
353
+ result = other - td
354
+ assert isinstance(result, Timedelta)
355
+ assert result == 0 * td
356
+
357
+ result = td - other
358
+ assert isinstance(result, Timedelta)
359
+ assert result == 0 * td
360
+
361
+
362
+ class TestTimedeltaMultiplicationDivision:
363
+ """
364
+ Tests for Timedelta methods:
365
+
366
+ __mul__, __rmul__,
367
+ __div__, __rdiv__,
368
+ __truediv__, __rtruediv__,
369
+ __floordiv__, __rfloordiv__,
370
+ __mod__, __rmod__,
371
+ __divmod__, __rdivmod__
372
+ """
373
+
374
+ # ---------------------------------------------------------------
375
+ # Timedelta.__mul__, __rmul__
376
+
377
+ @pytest.mark.parametrize("td_nat", [NaT, np.timedelta64("NaT", "ns")])
378
+ @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
379
+ def test_td_mul_nat(self, op, td_nat):
380
+ # GH#19819
381
+ td = Timedelta(10, unit="D")
382
+ typs = "|".join(["numpy.timedelta64", "NaTType", "Timedelta"])
383
+ msg = "|".join(
384
+ [
385
+ rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'",
386
+ r"ufunc '?multiply'? cannot use operands with types",
387
+ ]
388
+ )
389
+ with pytest.raises(TypeError, match=msg):
390
+ op(td, td_nat)
391
+
392
+ @pytest.mark.parametrize("nan", [np.nan, np.float64("NaN"), float("nan")])
393
+ @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
394
+ def test_td_mul_nan(self, op, nan):
395
+ # np.float64('NaN') has a 'dtype' attr, avoid treating as array
396
+ td = Timedelta(10, unit="D")
397
+ result = op(td, nan)
398
+ assert result is NaT
399
+
400
+ @pytest.mark.parametrize("op", [operator.mul, ops.rmul])
401
+ def test_td_mul_scalar(self, op):
402
+ # GH#19738
403
+ td = Timedelta(minutes=3)
404
+
405
+ result = op(td, 2)
406
+ assert result == Timedelta(minutes=6)
407
+
408
+ result = op(td, 1.5)
409
+ assert result == Timedelta(minutes=4, seconds=30)
410
+
411
+ assert op(td, np.nan) is NaT
412
+
413
+ assert op(-1, td)._value == -1 * td._value
414
+ assert op(-1.0, td)._value == -1.0 * td._value
415
+
416
+ msg = "unsupported operand type"
417
+ with pytest.raises(TypeError, match=msg):
418
+ # timedelta * datetime is gibberish
419
+ op(td, Timestamp(2016, 1, 2))
420
+
421
+ with pytest.raises(TypeError, match=msg):
422
+ # invalid multiply with another timedelta
423
+ op(td, td)
424
+
425
+ def test_td_mul_numeric_ndarray(self):
426
+ td = Timedelta("1 day")
427
+ other = np.array([2])
428
+ expected = np.array([Timedelta("2 Days").to_timedelta64()])
429
+
430
+ result = td * other
431
+ tm.assert_numpy_array_equal(result, expected)
432
+
433
+ result = other * td
434
+ tm.assert_numpy_array_equal(result, expected)
435
+
436
+ def test_td_mul_numeric_ndarray_0d(self):
437
+ td = Timedelta("1 day")
438
+ other = np.array(2, dtype=np.int64)
439
+ assert other.ndim == 0
440
+ expected = Timedelta("2 days")
441
+
442
+ res = td * other
443
+ assert type(res) is Timedelta
444
+ assert res == expected
445
+
446
+ res = other * td
447
+ assert type(res) is Timedelta
448
+ assert res == expected
449
+
450
+ def test_td_mul_td64_ndarray_invalid(self):
451
+ td = Timedelta("1 day")
452
+ other = np.array([Timedelta("2 Days").to_timedelta64()])
453
+
454
+ msg = (
455
+ "ufunc '?multiply'? cannot use operands with types "
456
+ rf"dtype\('{tm.ENDIAN}m8\[us\]'\) and dtype\('{tm.ENDIAN}m8\[us\]'\)"
457
+ )
458
+ with pytest.raises(TypeError, match=msg):
459
+ td * other
460
+ with pytest.raises(TypeError, match=msg):
461
+ other * td
462
+
463
+ # ---------------------------------------------------------------
464
+ # Timedelta.__div__, __truediv__
465
+
466
+ def test_td_div_timedeltalike_scalar(self):
467
+ # GH#19738
468
+ td = Timedelta(10, unit="D")
469
+
470
+ result = td / offsets.Hour(1)
471
+ assert result == 240
472
+
473
+ assert td / td == 1
474
+ assert td / np.timedelta64(60, "h") == 4
475
+
476
+ assert np.isnan(td / NaT)
477
+
478
+ def test_td_div_td64_non_nano(self):
479
+ # truediv
480
+ td = Timedelta("1 days 2 hours 3 ns")
481
+ result = td / np.timedelta64(1, "D")
482
+ assert result == td._value / (86400 * 10**9)
483
+ result = td / np.timedelta64(1, "s")
484
+ assert result == td._value / 10**9
485
+ result = td / np.timedelta64(1, "ns")
486
+ assert result == td._value
487
+
488
+ # floordiv
489
+ td = Timedelta("1 days 2 hours 3 ns")
490
+ result = td // np.timedelta64(1, "D")
491
+ assert result == 1
492
+ result = td // np.timedelta64(1, "s")
493
+ assert result == 93600
494
+ result = td // np.timedelta64(1, "ns")
495
+ assert result == td._value
496
+
497
+ def test_td_div_numeric_scalar(self):
498
+ # GH#19738
499
+ td = Timedelta(10, unit="D")
500
+
501
+ result = td / 2
502
+ assert isinstance(result, Timedelta)
503
+ assert result == Timedelta(days=5)
504
+
505
+ result = td / 5
506
+ assert isinstance(result, Timedelta)
507
+ assert result == Timedelta(days=2)
508
+
509
+ @pytest.mark.parametrize(
510
+ "nan",
511
+ [
512
+ np.nan,
513
+ np.float64("NaN"),
514
+ float("nan"),
515
+ ],
516
+ )
517
+ def test_td_div_nan(self, nan):
518
+ # np.float64('NaN') has a 'dtype' attr, avoid treating as array
519
+ td = Timedelta(10, unit="D")
520
+ result = td / nan
521
+ assert result is NaT
522
+
523
+ result = td // nan
524
+ assert result is NaT
525
+
526
+ def test_td_div_td64_ndarray(self):
527
+ td = Timedelta("1 day")
528
+
529
+ other = np.array([Timedelta("2 Days").to_timedelta64()])
530
+ expected = np.array([0.5])
531
+
532
+ result = td / other
533
+ tm.assert_numpy_array_equal(result, expected)
534
+
535
+ result = other / td
536
+ tm.assert_numpy_array_equal(result, expected * 4)
537
+
538
+ def test_td_div_ndarray_0d(self):
539
+ td = Timedelta("1 day")
540
+
541
+ other = np.array(1)
542
+ res = td / other
543
+ assert isinstance(res, Timedelta)
544
+ assert res == td
545
+
546
+ # ---------------------------------------------------------------
547
+ # Timedelta.__rdiv__
548
+
549
+ def test_td_rdiv_timedeltalike_scalar(self):
550
+ # GH#19738
551
+ td = Timedelta(10, unit="D")
552
+ result = offsets.Hour(1) / td
553
+ assert result == 1 / 240.0
554
+
555
+ assert np.timedelta64(60, "h") / td == 0.25
556
+
557
+ def test_td_rdiv_na_scalar(self):
558
+ # GH#31869 None gets cast to NaT
559
+ td = Timedelta(10, unit="D")
560
+
561
+ result = NaT / td
562
+ assert np.isnan(result)
563
+
564
+ result = None / td
565
+ assert np.isnan(result)
566
+
567
+ result = np.timedelta64("NaT", "ns") / td
568
+ assert np.isnan(result)
569
+
570
+ msg = r"unsupported operand type\(s\) for /: 'numpy.datetime64' and 'Timedelta'"
571
+ with pytest.raises(TypeError, match=msg):
572
+ np.datetime64("NaT", "ns") / td
573
+
574
+ msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
575
+ with pytest.raises(TypeError, match=msg):
576
+ np.nan / td
577
+
578
+ def test_td_rdiv_ndarray(self):
579
+ td = Timedelta(10, unit="D")
580
+
581
+ arr = np.array([td], dtype=object)
582
+ result = arr / td
583
+ expected = np.array([1], dtype=np.float64)
584
+ tm.assert_numpy_array_equal(result, expected)
585
+
586
+ arr = np.array([None])
587
+ result = arr / td
588
+ expected = np.array([np.nan])
589
+ tm.assert_numpy_array_equal(result, expected)
590
+
591
+ arr = np.array([np.nan], dtype=object)
592
+ msg = r"unsupported operand type\(s\) for /: 'float' and 'Timedelta'"
593
+ with pytest.raises(TypeError, match=msg):
594
+ arr / td
595
+
596
+ arr = np.array([np.nan], dtype=np.float64)
597
+ msg = "cannot use operands with types dtype"
598
+ with pytest.raises(TypeError, match=msg):
599
+ arr / td
600
+
601
+ def test_td_rdiv_ndarray_0d(self):
602
+ td = Timedelta(10, unit="D")
603
+
604
+ arr = np.array(td.asm8)
605
+
606
+ assert arr / td == 1
607
+
608
+ # ---------------------------------------------------------------
609
+ # Timedelta.__floordiv__
610
+
611
+ def test_td_floordiv_timedeltalike_scalar(self):
612
+ # GH#18846
613
+ td = Timedelta(hours=3, minutes=4)
614
+ scalar = Timedelta(hours=3, minutes=3)
615
+
616
+ assert td // scalar == 1
617
+ assert -td // scalar.to_pytimedelta() == -2
618
+ assert (2 * td) // scalar.to_timedelta64() == 2
619
+
620
+ def test_td_floordiv_null_scalar(self):
621
+ # GH#18846
622
+ td = Timedelta(hours=3, minutes=4)
623
+
624
+ assert td // np.nan is NaT
625
+ assert np.isnan(td // NaT)
626
+ assert np.isnan(td // np.timedelta64("NaT", "ns"))
627
+
628
+ def test_td_floordiv_offsets(self):
629
+ # GH#19738
630
+ td = Timedelta(hours=3, minutes=4)
631
+ assert td // offsets.Hour(1) == 3
632
+ assert td // offsets.Minute(2) == 92
633
+
634
+ def test_td_floordiv_invalid_scalar(self):
635
+ # GH#18846
636
+ td = Timedelta(hours=3, minutes=4)
637
+
638
+ msg = "|".join(
639
+ [
640
+ r"Invalid dtype datetime64\[D\] for __floordiv__",
641
+ "'dtype' is an invalid keyword argument for this function",
642
+ "this function got an unexpected keyword argument 'dtype'",
643
+ r"ufunc '?floor_divide'? cannot use operands with types",
644
+ ]
645
+ )
646
+ with pytest.raises(TypeError, match=msg):
647
+ td // np.datetime64("2016-01-01", dtype="datetime64[us]")
648
+
649
+ def test_td_floordiv_numeric_scalar(self):
650
+ # GH#18846
651
+ td = Timedelta(hours=3, minutes=4)
652
+
653
+ expected = Timedelta(hours=1, minutes=32)
654
+ assert td // 2 == expected
655
+ assert td // 2.0 == expected
656
+ assert td // np.float64(2.0) == expected
657
+ assert td // np.int32(2.0) == expected
658
+ assert td // np.uint8(2.0) == expected
659
+
660
+ def test_td_floordiv_timedeltalike_array(self):
661
+ # GH#18846
662
+ td = Timedelta(hours=3, minutes=4)
663
+ scalar = Timedelta(hours=3, minutes=3)
664
+
665
+ # Array-like others
666
+ assert td // np.array(scalar.to_timedelta64()) == 1
667
+
668
+ res = (3 * td) // np.array([scalar.to_timedelta64()])
669
+ expected = np.array([3], dtype=np.int64)
670
+ tm.assert_numpy_array_equal(res, expected)
671
+
672
+ res = (10 * td) // np.array(
673
+ [scalar.to_timedelta64(), np.timedelta64("NaT", "ns")]
674
+ )
675
+ expected = np.array([10, np.nan])
676
+ tm.assert_numpy_array_equal(res, expected)
677
+
678
+ def test_td_floordiv_numeric_series(self):
679
+ # GH#18846
680
+ td = Timedelta(hours=3, minutes=4)
681
+ ser = pd.Series([1], dtype=np.int64)
682
+ res = td // ser
683
+ assert res.dtype.kind == "m"
684
+
685
+ # ---------------------------------------------------------------
686
+ # Timedelta.__rfloordiv__
687
+
688
+ def test_td_rfloordiv_timedeltalike_scalar(self):
689
+ # GH#18846
690
+ td = Timedelta(hours=3, minutes=3)
691
+ scalar = Timedelta(hours=3, minutes=4)
692
+
693
+ # scalar others
694
+ # x // Timedelta is defined only for timedelta-like x. int-like,
695
+ # float-like, and date-like, in particular, should all either
696
+ # a) raise TypeError directly or
697
+ # b) return NotImplemented, following which the reversed
698
+ # operation will raise TypeError.
699
+ assert td.__rfloordiv__(scalar) == 1
700
+ assert (-td).__rfloordiv__(scalar.to_pytimedelta()) == -2
701
+ assert (2 * td).__rfloordiv__(scalar.to_timedelta64()) == 0
702
+
703
+ def test_td_rfloordiv_null_scalar(self):
704
+ # GH#18846
705
+ td = Timedelta(hours=3, minutes=3)
706
+
707
+ assert np.isnan(td.__rfloordiv__(NaT))
708
+ assert np.isnan(td.__rfloordiv__(np.timedelta64("NaT", "ns")))
709
+
710
+ def test_td_rfloordiv_offsets(self):
711
+ # GH#19738
712
+ assert offsets.Hour(1) // Timedelta(minutes=25) == 2
713
+
714
+ def test_td_rfloordiv_invalid_scalar(self):
715
+ # GH#18846
716
+ td = Timedelta(hours=3, minutes=3)
717
+
718
+ dt64 = np.datetime64("2016-01-01", "us")
719
+
720
+ assert td.__rfloordiv__(dt64) is NotImplemented
721
+
722
+ msg = (
723
+ r"unsupported operand type\(s\) for //: 'numpy.datetime64' and 'Timedelta'"
724
+ )
725
+ with pytest.raises(TypeError, match=msg):
726
+ dt64 // td
727
+
728
+ def test_td_rfloordiv_numeric_scalar(self):
729
+ # GH#18846
730
+ td = Timedelta(hours=3, minutes=3)
731
+
732
+ assert td.__rfloordiv__(np.nan) is NotImplemented
733
+ assert td.__rfloordiv__(3.5) is NotImplemented
734
+ assert td.__rfloordiv__(2) is NotImplemented
735
+ assert td.__rfloordiv__(np.float64(2.0)) is NotImplemented
736
+ assert td.__rfloordiv__(np.uint8(9)) is NotImplemented
737
+ assert td.__rfloordiv__(np.int32(2.0)) is NotImplemented
738
+
739
+ msg = r"unsupported operand type\(s\) for //: '.*' and 'Timedelta"
740
+ with pytest.raises(TypeError, match=msg):
741
+ np.float64(2.0) // td
742
+ with pytest.raises(TypeError, match=msg):
743
+ np.uint8(9) // td
744
+ with pytest.raises(TypeError, match=msg):
745
+ # deprecated GH#19761, enforced GH#29797
746
+ np.int32(2.0) // td
747
+
748
+ def test_td_rfloordiv_timedeltalike_array(self):
749
+ # GH#18846
750
+ td = Timedelta(hours=3, minutes=3)
751
+ scalar = Timedelta(hours=3, minutes=4)
752
+
753
+ # Array-like others
754
+ assert td.__rfloordiv__(np.array(scalar.to_timedelta64())) == 1
755
+
756
+ res = td.__rfloordiv__(np.array([(3 * scalar).to_timedelta64()]))
757
+ expected = np.array([3], dtype=np.int64)
758
+ tm.assert_numpy_array_equal(res, expected)
759
+
760
+ arr = np.array([(10 * scalar).to_timedelta64(), np.timedelta64("NaT", "ns")])
761
+ res = td.__rfloordiv__(arr)
762
+ expected = np.array([10, np.nan])
763
+ tm.assert_numpy_array_equal(res, expected)
764
+
765
+ def test_td_rfloordiv_intarray(self):
766
+ # deprecated GH#19761, enforced GH#29797
767
+ ints = np.array([1349654400, 1349740800, 1349827200, 1349913600]) * 10**9
768
+
769
+ msg = "Invalid dtype"
770
+ with pytest.raises(TypeError, match=msg):
771
+ ints // Timedelta(1, unit="s")
772
+
773
+ def test_td_rfloordiv_numeric_series(self):
774
+ # GH#18846
775
+ td = Timedelta(hours=3, minutes=3)
776
+ ser = pd.Series([1], dtype=np.int64)
777
+ res = td.__rfloordiv__(ser)
778
+ assert res is NotImplemented
779
+
780
+ msg = "Invalid dtype"
781
+ with pytest.raises(TypeError, match=msg):
782
+ # Deprecated GH#19761, enforced GH#29797
783
+ ser // td
784
+
785
+ # ----------------------------------------------------------------
786
+ # Timedelta.__mod__, __rmod__
787
+
788
+ def test_mod_timedeltalike(self):
789
+ # GH#19365
790
+ td = Timedelta(hours=37)
791
+
792
+ # Timedelta-like others
793
+ result = td % Timedelta(hours=6)
794
+ assert isinstance(result, Timedelta)
795
+ assert result == Timedelta(hours=1)
796
+
797
+ result = td % timedelta(minutes=60)
798
+ assert isinstance(result, Timedelta)
799
+ assert result == Timedelta(0)
800
+
801
+ result = td % NaT
802
+ assert result is NaT
803
+
804
+ def test_mod_timedelta64_nat(self):
805
+ # GH#19365
806
+ td = Timedelta(hours=37)
807
+
808
+ result = td % np.timedelta64("NaT", "ns")
809
+ assert result is NaT
810
+
811
+ def test_mod_timedelta64(self):
812
+ # GH#19365
813
+ td = Timedelta(hours=37)
814
+
815
+ result = td % np.timedelta64(2, "h")
816
+ assert isinstance(result, Timedelta)
817
+ assert result == Timedelta(hours=1)
818
+
819
+ def test_mod_offset(self):
820
+ # GH#19365
821
+ td = Timedelta(hours=37)
822
+
823
+ result = td % offsets.Hour(5)
824
+ assert isinstance(result, Timedelta)
825
+ assert result == Timedelta(hours=2)
826
+
827
+ def test_mod_numeric(self):
828
+ # GH#19365
829
+ td = Timedelta(hours=37)
830
+
831
+ # Numeric Others
832
+ result = td % 2
833
+ assert isinstance(result, Timedelta)
834
+ assert result == Timedelta(0)
835
+
836
+ result = td % 1e9
837
+ assert isinstance(result, Timedelta)
838
+ assert result == Timedelta(minutes=3, seconds=20)
839
+
840
+ result = td % int(1e9)
841
+ assert isinstance(result, Timedelta)
842
+ assert result == Timedelta(minutes=3, seconds=20)
843
+
844
+ def test_mod_invalid(self):
845
+ # GH#19365
846
+ td = Timedelta(hours=37)
847
+ msg = "unsupported operand type"
848
+ with pytest.raises(TypeError, match=msg):
849
+ td % Timestamp("2018-01-22")
850
+
851
+ with pytest.raises(TypeError, match=msg):
852
+ td % []
853
+
854
+ def test_rmod_pytimedelta(self):
855
+ # GH#19365
856
+ td = Timedelta(minutes=3)
857
+
858
+ result = timedelta(minutes=4) % td
859
+ assert isinstance(result, Timedelta)
860
+ assert result == Timedelta(minutes=1)
861
+
862
+ def test_rmod_timedelta64(self):
863
+ # GH#19365
864
+ td = Timedelta(minutes=3)
865
+ result = np.timedelta64(5, "m") % td
866
+ assert isinstance(result, Timedelta)
867
+ assert result == Timedelta(minutes=2)
868
+
869
+ def test_rmod_invalid(self):
870
+ # GH#19365
871
+ td = Timedelta(minutes=3)
872
+
873
+ msg = "unsupported operand"
874
+ with pytest.raises(TypeError, match=msg):
875
+ Timestamp("2018-01-22") % td
876
+
877
+ with pytest.raises(TypeError, match=msg):
878
+ 15 % td
879
+
880
+ with pytest.raises(TypeError, match=msg):
881
+ 16.0 % td
882
+
883
+ msg = "Invalid dtype int"
884
+ with pytest.raises(TypeError, match=msg):
885
+ np.array([22, 24]) % td
886
+
887
+ # ----------------------------------------------------------------
888
+ # Timedelta.__divmod__, __rdivmod__
889
+
890
+ def test_divmod_numeric(self):
891
+ # GH#19365
892
+ td = Timedelta(days=2, hours=6)
893
+
894
+ result = divmod(td, 53 * 3600 * 1e6)
895
+ assert result[0] == Timedelta(1, unit="us").as_unit("us")
896
+ assert isinstance(result[1], Timedelta)
897
+ assert result[1] == Timedelta(hours=1)
898
+
899
+ assert result
900
+ result = divmod(td, np.nan)
901
+ assert result[0] is NaT
902
+ assert result[1] is NaT
903
+
904
+ def test_divmod(self):
905
+ # GH#19365
906
+ td = Timedelta(days=2, hours=6)
907
+
908
+ result = divmod(td, timedelta(days=1))
909
+ assert result[0] == 2
910
+ assert isinstance(result[1], Timedelta)
911
+ assert result[1] == Timedelta(hours=6)
912
+
913
+ result = divmod(td, 54)
914
+ assert result[0] == Timedelta(hours=1)
915
+ assert isinstance(result[1], Timedelta)
916
+ assert result[1] == Timedelta(0)
917
+
918
+ result = divmod(td, NaT)
919
+ assert np.isnan(result[0])
920
+ assert result[1] is NaT
921
+
922
+ def test_divmod_offset(self):
923
+ # GH#19365
924
+ td = Timedelta(days=2, hours=6)
925
+
926
+ result = divmod(td, offsets.Hour(-4))
927
+ assert result[0] == -14
928
+ assert isinstance(result[1], Timedelta)
929
+ assert result[1] == Timedelta(hours=-2)
930
+
931
+ def test_divmod_invalid(self):
932
+ # GH#19365
933
+ td = Timedelta(days=2, hours=6)
934
+
935
+ msg = r"unsupported operand type\(s\) for //: 'Timedelta' and 'Timestamp'"
936
+ with pytest.raises(TypeError, match=msg):
937
+ divmod(td, Timestamp("2018-01-22"))
938
+
939
+ def test_rdivmod_pytimedelta(self):
940
+ # GH#19365
941
+ result = divmod(timedelta(days=2, hours=6), Timedelta(days=1))
942
+ assert result[0] == 2
943
+ assert isinstance(result[1], Timedelta)
944
+ assert result[1] == Timedelta(hours=6)
945
+
946
+ def test_rdivmod_offset(self):
947
+ result = divmod(offsets.Hour(54), Timedelta(hours=-4))
948
+ assert result[0] == -14
949
+ assert isinstance(result[1], Timedelta)
950
+ assert result[1] == Timedelta(hours=-2)
951
+
952
+ def test_rdivmod_invalid(self):
953
+ # GH#19365
954
+ td = Timedelta(minutes=3)
955
+ msg = "unsupported operand type"
956
+
957
+ with pytest.raises(TypeError, match=msg):
958
+ divmod(Timestamp("2018-01-22"), td)
959
+
960
+ with pytest.raises(TypeError, match=msg):
961
+ divmod(15, td)
962
+
963
+ with pytest.raises(TypeError, match=msg):
964
+ divmod(16.0, td)
965
+
966
+ msg = "Invalid dtype int"
967
+ with pytest.raises(TypeError, match=msg):
968
+ divmod(np.array([22, 24]), td)
969
+
970
+ # ----------------------------------------------------------------
971
+
972
+ @pytest.mark.parametrize(
973
+ "op", [operator.mul, ops.rmul, operator.truediv, ops.rdiv, ops.rsub]
974
+ )
975
+ @pytest.mark.parametrize(
976
+ "arr",
977
+ [
978
+ [Timestamp("20130101 9:01"), Timestamp("20121230 9:02")],
979
+ [Timestamp("2021-11-09 09:54:00"), Timedelta("1D")],
980
+ ],
981
+ )
982
+ def test_td_op_timedelta_timedeltalike_array(self, op, arr):
983
+ arr = np.array(arr)
984
+ msg = "unsupported operand type|cannot use operands with types"
985
+ with pytest.raises(TypeError, match=msg):
986
+ op(arr, Timedelta("1D"))
987
+
988
+ def test_mul_bool_invalid(self):
989
+ # GH#62316
990
+ msg = "Cannot multiply Timedelta by bool. Explicitly cast to integer"
991
+ with pytest.raises(TypeError, match=msg):
992
+ Timedelta("1 day") * True
993
+
994
+
995
+ class TestTimedeltaComparison:
996
+ def test_compare_pytimedelta_bounds(self):
997
+ # GH#49021 don't overflow on comparison with very large pytimedeltas
998
+
999
+ for unit in ["ns", "us"]:
1000
+ tdmax = Timedelta.max.as_unit(unit).max
1001
+ tdmin = Timedelta.min.as_unit(unit).min
1002
+
1003
+ assert tdmax < timedelta.max
1004
+ assert tdmax <= timedelta.max
1005
+ assert not tdmax > timedelta.max
1006
+ assert not tdmax >= timedelta.max
1007
+ assert tdmax != timedelta.max
1008
+ assert not tdmax == timedelta.max
1009
+
1010
+ assert tdmin > timedelta.min
1011
+ assert tdmin >= timedelta.min
1012
+ assert not tdmin < timedelta.min
1013
+ assert not tdmin <= timedelta.min
1014
+ assert tdmin != timedelta.min
1015
+ assert not tdmin == timedelta.min
1016
+
1017
+ # But the "ms" and "s"-reso bounds extend pass pytimedelta
1018
+ for unit in ["ms", "s"]:
1019
+ tdmax = Timedelta.max.as_unit(unit).max
1020
+ tdmin = Timedelta.min.as_unit(unit).min
1021
+
1022
+ assert tdmax > timedelta.max
1023
+ assert tdmax >= timedelta.max
1024
+ assert not tdmax < timedelta.max
1025
+ assert not tdmax <= timedelta.max
1026
+ assert tdmax != timedelta.max
1027
+ assert not tdmax == timedelta.max
1028
+
1029
+ assert tdmin < timedelta.min
1030
+ assert tdmin <= timedelta.min
1031
+ assert not tdmin > timedelta.min
1032
+ assert not tdmin >= timedelta.min
1033
+ assert tdmin != timedelta.min
1034
+ assert not tdmin == timedelta.min
1035
+
1036
+ def test_compare_pytimedelta_bounds2(self):
1037
+ # a pytimedelta outside the microsecond bounds
1038
+ pytd = timedelta(days=999999999, seconds=86399)
1039
+ # NB: np.timedelta64(td, "s"") incorrectly overflows
1040
+ td64 = np.timedelta64(pytd.days, "D") + np.timedelta64(pytd.seconds, "s")
1041
+ td = Timedelta(td64)
1042
+ assert td.days == pytd.days
1043
+ assert td.seconds == pytd.seconds
1044
+
1045
+ assert td == pytd
1046
+ assert not td != pytd
1047
+ assert not td < pytd
1048
+ assert not td > pytd
1049
+ assert td <= pytd
1050
+ assert td >= pytd
1051
+
1052
+ td2 = td - Timedelta(seconds=1).as_unit("s")
1053
+ assert td2 != pytd
1054
+ assert not td2 == pytd
1055
+ assert td2 < pytd
1056
+ assert td2 <= pytd
1057
+ assert not td2 > pytd
1058
+ assert not td2 >= pytd
1059
+
1060
+ def test_compare_tick(self, tick_classes):
1061
+ cls = tick_classes
1062
+
1063
+ off = cls(4)
1064
+ td = off._as_pd_timedelta
1065
+ assert isinstance(td, Timedelta)
1066
+
1067
+ assert td == off
1068
+ assert not td != off
1069
+ assert td <= off
1070
+ assert td >= off
1071
+ assert not td < off
1072
+ assert not td > off
1073
+
1074
+ assert not td == 2 * off
1075
+ assert td != 2 * off
1076
+ assert td <= 2 * off
1077
+ assert td < 2 * off
1078
+ assert not td >= 2 * off
1079
+ assert not td > 2 * off
1080
+
1081
+ def test_comparison_object_array(self):
1082
+ # analogous to GH#15183
1083
+ td = Timedelta("2 days")
1084
+ other = Timedelta("3 hours")
1085
+
1086
+ arr = np.array([other, td], dtype=object)
1087
+ res = arr == td
1088
+ expected = np.array([False, True], dtype=bool)
1089
+ assert (res == expected).all()
1090
+
1091
+ # 2D case
1092
+ arr = np.array([[other, td], [td, other]], dtype=object)
1093
+ res = arr != td
1094
+ expected = np.array([[True, False], [False, True]], dtype=bool)
1095
+ assert res.shape == expected.shape
1096
+ assert (res == expected).all()
1097
+
1098
+ def test_compare_timedelta_ndarray(self):
1099
+ # GH#11835
1100
+ periods = [Timedelta("0 days 01:00:00"), Timedelta("0 days 01:00:00")]
1101
+ arr = np.array(periods)
1102
+ result = arr[0] > arr
1103
+ expected = np.array([False, False])
1104
+ tm.assert_numpy_array_equal(result, expected)
1105
+
1106
+ def test_compare_td64_ndarray(self):
1107
+ # GG#33441
1108
+ arr = np.arange(5).astype("timedelta64[ns]")
1109
+ td = Timedelta(arr[1])
1110
+
1111
+ expected = np.array([False, True, False, False, False], dtype=bool)
1112
+
1113
+ result = td == arr
1114
+ tm.assert_numpy_array_equal(result, expected)
1115
+
1116
+ result = arr == td
1117
+ tm.assert_numpy_array_equal(result, expected)
1118
+
1119
+ result = td != arr
1120
+ tm.assert_numpy_array_equal(result, ~expected)
1121
+
1122
+ result = arr != td
1123
+ tm.assert_numpy_array_equal(result, ~expected)
1124
+
1125
+ def test_compare_custom_object(self):
1126
+ """
1127
+ Make sure non supported operations on Timedelta returns NonImplemented
1128
+ and yields to other operand (GH#20829).
1129
+ """
1130
+
1131
+ class CustomClass:
1132
+ def __init__(self, cmp_result=None) -> None:
1133
+ self.cmp_result = cmp_result
1134
+
1135
+ def generic_result(self):
1136
+ if self.cmp_result is None:
1137
+ return NotImplemented
1138
+ else:
1139
+ return self.cmp_result
1140
+
1141
+ def __eq__(self, other):
1142
+ return self.generic_result()
1143
+
1144
+ def __gt__(self, other):
1145
+ return self.generic_result()
1146
+
1147
+ t = Timedelta("1s")
1148
+
1149
+ assert t != "string"
1150
+ assert t != 1
1151
+ assert t != CustomClass()
1152
+ assert t != CustomClass(cmp_result=False)
1153
+
1154
+ assert t < CustomClass(cmp_result=True)
1155
+ assert not t < CustomClass(cmp_result=False)
1156
+
1157
+ assert t == CustomClass(cmp_result=True)
1158
+
1159
+ @pytest.mark.parametrize("val", ["string", 1])
1160
+ def test_compare_unknown_type(self, val):
1161
+ # GH#20829
1162
+ t = Timedelta("1s")
1163
+ msg = "not supported between instances of 'Timedelta' and '(int|str)'"
1164
+ with pytest.raises(TypeError, match=msg):
1165
+ t >= val
1166
+ with pytest.raises(TypeError, match=msg):
1167
+ t > val
1168
+ with pytest.raises(TypeError, match=msg):
1169
+ t <= val
1170
+ with pytest.raises(TypeError, match=msg):
1171
+ t < val
1172
+
1173
+
1174
+ def test_ops_notimplemented():
1175
+ class Other:
1176
+ pass
1177
+
1178
+ other = Other()
1179
+
1180
+ td = Timedelta("1 day")
1181
+ assert td.__add__(other) is NotImplemented
1182
+ assert td.__sub__(other) is NotImplemented
1183
+ assert td.__truediv__(other) is NotImplemented
1184
+ assert td.__mul__(other) is NotImplemented
1185
+ assert td.__floordiv__(other) is NotImplemented
1186
+
1187
+
1188
+ def test_ops_error_str():
1189
+ # GH#13624
1190
+ td = Timedelta("1 day")
1191
+
1192
+ for left, right in [(td, "a"), ("a", td)]:
1193
+ msg = "|".join(
1194
+ [
1195
+ "unsupported operand type",
1196
+ r'can only concatenate str \(not "Timedelta"\) to str',
1197
+ "must be str, not Timedelta",
1198
+ ]
1199
+ )
1200
+ with pytest.raises(TypeError, match=msg):
1201
+ left + right
1202
+
1203
+ msg = "not supported between instances of"
1204
+ with pytest.raises(TypeError, match=msg):
1205
+ left > right
1206
+
1207
+ assert not left == right
1208
+ assert left != right
1209
+
1210
+
1211
+ @pytest.mark.parametrize("box", [True, False])
1212
+ def test_ops_str_deprecated(box):
1213
+ # GH#59653
1214
+ td = Timedelta("1 day")
1215
+ item = "1"
1216
+ if box:
1217
+ item = np.array([item], dtype=object)
1218
+
1219
+ msg = "Scalar operations between Timedelta and string are deprecated"
1220
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1221
+ td + item
1222
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1223
+ item + td
1224
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1225
+ td - item
1226
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1227
+ item - td
1228
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1229
+ item / td
1230
+ if not box:
1231
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1232
+ td / item
1233
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1234
+ item // td
1235
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
1236
+ td // item
1237
+ else:
1238
+ msg = "|".join(
1239
+ [
1240
+ "ufunc 'divide' cannot use operands",
1241
+ "Invalid dtype object for __floordiv__",
1242
+ r"unsupported operand type\(s\) for /: 'int' and 'str'",
1243
+ r"unsupported operand type\(s\) for /: 'datetime.timedelta' and 'str'",
1244
+ ]
1245
+ )
1246
+ with pytest.raises(TypeError, match=msg):
1247
+ td / item
1248
+ with pytest.raises(TypeError, match=msg):
1249
+ item // td
1250
+ with pytest.raises(TypeError, match=msg):
1251
+ td // item
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_constructors.py ADDED
@@ -0,0 +1,795 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import timedelta
2
+ from itertools import product
3
+
4
+ import numpy as np
5
+ import pytest
6
+
7
+ from pandas._libs.tslibs import OutOfBoundsTimedelta
8
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
9
+ from pandas.compat.numpy import (
10
+ is_numpy_dev,
11
+ np_version_gt2_5,
12
+ )
13
+ from pandas.errors import Pandas4Warning
14
+
15
+ from pandas import (
16
+ Index,
17
+ NaT,
18
+ Timedelta,
19
+ TimedeltaIndex,
20
+ offsets,
21
+ to_timedelta,
22
+ )
23
+ import pandas._testing as tm
24
+
25
+
26
+ class TestTimedeltaConstructorKeywordBased:
27
+ # Tests for constructing a Timedelta from keywords like the pytimedelta
28
+ # base class
29
+ def test_nanosecond_keyword(self):
30
+ # GH#63216
31
+ td = Timedelta(nanoseconds=1000)
32
+ assert td.unit == "ns"
33
+
34
+ def test_noninteger_microseconds(self):
35
+ # GH#63216
36
+ td = Timedelta(microseconds=1.5)
37
+ assert td.unit == "ns"
38
+ assert td == Timedelta(nanoseconds=1500)
39
+
40
+
41
+ class TestTimedeltaConstructorUnitKeyword:
42
+ def test_result_unit(self):
43
+ # For supported units, we get result.unit == unit
44
+ for unit in ["s", "ms", "us", "ns"]:
45
+ td = Timedelta(1, unit=unit)
46
+ assert td.unit == unit
47
+
48
+ td = to_timedelta(1, unit=unit)
49
+ assert td.unit == unit
50
+
51
+ # For non-supported units we get the closest-supported unit
52
+ for unit in ["W", "D", "h", "m"]:
53
+ td = Timedelta(1, unit=unit)
54
+ assert td.unit == "s"
55
+
56
+ td = to_timedelta(1, unit=unit)
57
+ assert td.unit == "s"
58
+
59
+ @pytest.mark.parametrize("unit", ["Y", "y", "M"])
60
+ def test_unit_m_y_raises(self, unit):
61
+ msg = "Units 'M', 'Y', and 'y' are no longer supported"
62
+
63
+ with pytest.raises(ValueError, match=msg):
64
+ Timedelta(10, unit)
65
+
66
+ with pytest.raises(ValueError, match=msg):
67
+ to_timedelta(10, unit)
68
+
69
+ with pytest.raises(ValueError, match=msg):
70
+ to_timedelta([1, 2], unit)
71
+
72
+ @pytest.mark.parametrize(
73
+ "unit,unit_depr",
74
+ [
75
+ ("W", "w"),
76
+ ("D", "d"),
77
+ ("min", "MIN"),
78
+ ("s", "S"),
79
+ ("h", "H"),
80
+ ("ms", "MS"),
81
+ ("us", "US"),
82
+ ],
83
+ )
84
+ def test_unit_deprecated(self, unit, unit_depr):
85
+ # GH#52536
86
+ msg = f"'{unit_depr}' is deprecated and will be removed in a future version."
87
+
88
+ expected = Timedelta(1, unit=unit)
89
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
90
+ result = Timedelta(1, unit=unit_depr)
91
+ tm.assert_equal(result, expected)
92
+
93
+ @pytest.mark.parametrize(
94
+ "unit, np_unit",
95
+ [("W", "W")]
96
+ + [(value, "D") for value in ["D", "days", "day", "Days", "Day"]]
97
+ + [
98
+ (value, "m")
99
+ for value in [
100
+ "m",
101
+ "minute",
102
+ "min",
103
+ "minutes",
104
+ "Minute",
105
+ "Min",
106
+ "Minutes",
107
+ ]
108
+ ]
109
+ + [
110
+ (value, "s")
111
+ for value in [
112
+ "s",
113
+ "seconds",
114
+ "sec",
115
+ "second",
116
+ "Seconds",
117
+ "Sec",
118
+ "Second",
119
+ ]
120
+ ]
121
+ + [
122
+ (value, "ms")
123
+ for value in [
124
+ "ms",
125
+ "milliseconds",
126
+ "millisecond",
127
+ "milli",
128
+ "millis",
129
+ "Milliseconds",
130
+ "Millisecond",
131
+ "Milli",
132
+ "Millis",
133
+ ]
134
+ ]
135
+ + [
136
+ (value, "us")
137
+ for value in [
138
+ "us",
139
+ "microseconds",
140
+ "microsecond",
141
+ "micro",
142
+ "micros",
143
+ "Microseconds",
144
+ "Microsecond",
145
+ "Micro",
146
+ "Micros",
147
+ ]
148
+ ]
149
+ + [
150
+ (value, "ns")
151
+ for value in [
152
+ "ns",
153
+ "nanoseconds",
154
+ "nanosecond",
155
+ "nano",
156
+ "nanos",
157
+ "Nanoseconds",
158
+ "Nanosecond",
159
+ "Nano",
160
+ "Nanos",
161
+ ]
162
+ ],
163
+ )
164
+ @pytest.mark.parametrize("wrapper", [np.array, list, Index])
165
+ def test_unit_parser(self, unit, np_unit, wrapper):
166
+ # validate all units, GH 6855, GH 21762
167
+ # array-likes
168
+ exp_unit = np_unit if np_unit not in ["W", "D", "m"] else "s"
169
+ expected = TimedeltaIndex(
170
+ [np.timedelta64(i, np_unit) for i in np.arange(5).tolist()],
171
+ dtype=f"m8[{exp_unit}]",
172
+ )
173
+
174
+ result = to_timedelta(wrapper(range(5)), unit=unit)
175
+ tm.assert_index_equal(result, expected)
176
+
177
+ str_repr = [f"{x}{unit}" for x in np.arange(5)]
178
+ exp_unit = "us" if np_unit != "ns" else "ns"
179
+ result = to_timedelta(wrapper(str_repr))
180
+ tm.assert_index_equal(result, expected.as_unit(exp_unit))
181
+ result = to_timedelta(wrapper(str_repr))
182
+ tm.assert_index_equal(result, expected.as_unit(exp_unit))
183
+
184
+ # scalar
185
+ expected = Timedelta(np.timedelta64(2, np_unit)).as_unit(exp_unit)
186
+ result = to_timedelta(2, unit=unit)
187
+ assert result == expected
188
+ result = Timedelta(2, unit=unit)
189
+ assert result == expected
190
+
191
+ result = to_timedelta(f"2{unit}")
192
+ assert result == expected
193
+ result = Timedelta(f"2{unit}")
194
+ assert result == expected
195
+
196
+ @pytest.mark.parametrize("unit", ["T", "t", "L", "l", "U", "u", "N", "n"])
197
+ def test_unit_T_L_N_U_raises(self, unit):
198
+ msg = f"invalid unit abbreviation: {unit}"
199
+ with pytest.raises(ValueError, match=msg):
200
+ Timedelta(1, unit=unit)
201
+
202
+ with pytest.raises(ValueError, match=msg):
203
+ to_timedelta(10, unit)
204
+
205
+ with pytest.raises(ValueError, match=msg):
206
+ to_timedelta([1, 2], unit)
207
+
208
+ def test_unit_round_float(self):
209
+ # When the float is round, we give the requested unit
210
+ # (or nearest-supported) like we do with integers
211
+ td = Timedelta(45.0, unit="s")
212
+ assert td.unit == "s"
213
+ assert td == Timedelta(45, unit="s")
214
+
215
+ td = to_timedelta(45.0, unit="s")
216
+ assert td.unit == "s"
217
+ assert td == Timedelta(45, unit="s")
218
+
219
+ def test_unit_non_round_float(self):
220
+ # With non-round floats, we have to give nanosecond
221
+ td = Timedelta(45.5, unit="s")
222
+ assert td.unit == "ns"
223
+ assert td == Timedelta(45_500, unit="ms")
224
+
225
+ td = to_timedelta(45.5, unit="s")
226
+ assert td.unit == "ns"
227
+ assert td == Timedelta(45_500, unit="ms")
228
+
229
+
230
+ def test_construct_from_kwargs_overflow():
231
+ # GH#55503
232
+ msg = "seconds=86400000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
233
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
234
+ Timedelta(days=10**6)
235
+ msg = "seconds=60000000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
236
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
237
+ Timedelta(minutes=10**9)
238
+
239
+
240
+ def test_construct_with_weeks_unit_overflow():
241
+ # GH#47268 don't silently wrap around
242
+ msg = "1000000000000000000 weeks"
243
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
244
+ Timedelta(1000000000000000000, unit="W")
245
+
246
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
247
+ Timedelta(1000000000000000000.0, unit="W")
248
+
249
+
250
+ def test_construct_from_td64_with_unit():
251
+ # ignore the unit, as it may cause silently overflows leading to incorrect
252
+ # results, and in non-overflow cases is irrelevant GH#46827
253
+ obj = np.timedelta64(123456789000000000, "h")
254
+
255
+ msg = "The 'unit' keyword is only used when the Timedelta input is"
256
+
257
+ with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
258
+ with tm.assert_produces_warning(UserWarning, match=msg):
259
+ Timedelta(obj, unit="ps")
260
+
261
+ with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
262
+ with tm.assert_produces_warning(UserWarning, match=msg):
263
+ Timedelta(obj, unit="ns")
264
+
265
+ with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
266
+ Timedelta(obj)
267
+
268
+
269
+ def test_from_td64_retain_resolution():
270
+ # case where we retain millisecond resolution
271
+ obj = np.timedelta64(12345, "ms")
272
+
273
+ td = Timedelta(obj)
274
+ assert td._value == obj.view("i8")
275
+ assert td._creso == NpyDatetimeUnit.NPY_FR_ms.value
276
+
277
+ # Case where we cast to nearest-supported reso
278
+ obj2 = np.timedelta64(1234, "D")
279
+ td2 = Timedelta(obj2)
280
+ assert td2._creso == NpyDatetimeUnit.NPY_FR_s.value
281
+ assert td2 == obj2
282
+ assert td2.days == 1234
283
+
284
+ # Case that _would_ overflow if we didn't support non-nano
285
+ obj3 = np.timedelta64(1000000000000000000, "us")
286
+ td3 = Timedelta(obj3)
287
+ assert td3.total_seconds() == 1000000000000
288
+ assert td3._creso == NpyDatetimeUnit.NPY_FR_us.value
289
+
290
+
291
+ def test_from_pytimedelta_us_reso():
292
+ # pytimedelta has microsecond resolution, so Timedelta(pytd) inherits that
293
+ td = timedelta(days=4, minutes=3)
294
+ result = Timedelta(td)
295
+ assert result.to_pytimedelta() == td
296
+ assert result._creso == NpyDatetimeUnit.NPY_FR_us.value
297
+
298
+
299
+ def test_from_tick_reso():
300
+ tick = offsets.Nano()
301
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ns.value
302
+
303
+ tick = offsets.Micro()
304
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_us.value
305
+
306
+ tick = offsets.Milli()
307
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ms.value
308
+
309
+ tick = offsets.Second()
310
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
311
+
312
+ # everything above Second gets cast to the closest supported reso: second
313
+ tick = offsets.Minute()
314
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
315
+
316
+ tick = offsets.Hour()
317
+ assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
318
+
319
+ tick = offsets.Day()
320
+ msg = (
321
+ "Value must be Timedelta, string, integer, float, timedelta "
322
+ "or convertible, not Day"
323
+ )
324
+ with pytest.raises(ValueError, match=msg):
325
+ # GH#41943 Day is no longer a Tick
326
+ Timedelta(tick)
327
+
328
+
329
+ def test_construction():
330
+ expected = np.timedelta64(10, "D").astype("m8[ns]").view("i8")
331
+ assert Timedelta(10, unit="D")._value == expected // 10**9
332
+ assert Timedelta(10.0, unit="D")._value == expected // 10**9
333
+ assert Timedelta("10 days")._value == expected // 1000
334
+ assert Timedelta(days=10)._value == expected // 1000
335
+ assert Timedelta(days=10.0)._value == expected // 1000
336
+
337
+ expected += np.timedelta64(10, "s").astype("m8[ns]").view("i8")
338
+ assert Timedelta("10 days 00:00:10")._value == expected // 1000
339
+ assert Timedelta(days=10, seconds=10)._value == expected // 1000
340
+ assert Timedelta(days=10, milliseconds=10 * 1000)._value == expected // 1000
341
+ assert Timedelta(days=10, microseconds=10 * 1000 * 1000)._value == expected // 1000
342
+
343
+ # rounding cases
344
+ assert Timedelta(82739999850000)._value == 82739999850000
345
+ assert "0 days 22:58:59.999850" in str(Timedelta(82739999850000))
346
+ assert Timedelta(123072001000000)._value == 123072001000000
347
+ assert "1 days 10:11:12.001" in str(Timedelta(123072001000000))
348
+
349
+ # string conversion with/without leading zero
350
+ # GH#9570
351
+ assert Timedelta("0:00:00") == timedelta(hours=0)
352
+ assert Timedelta("00:00:00") == timedelta(hours=0)
353
+ assert Timedelta("-1:00:00") == -timedelta(hours=1)
354
+ assert Timedelta("-01:00:00") == -timedelta(hours=1)
355
+
356
+ # more strings & abbrevs
357
+ # GH#8190
358
+ assert Timedelta("1 h") == timedelta(hours=1)
359
+ assert Timedelta("1 hour") == timedelta(hours=1)
360
+ assert Timedelta("1 hr") == timedelta(hours=1)
361
+ assert Timedelta("1 hours") == timedelta(hours=1)
362
+ assert Timedelta("-1 hours") == -timedelta(hours=1)
363
+ assert Timedelta("1 m") == timedelta(minutes=1)
364
+ assert Timedelta("1.5 m") == timedelta(seconds=90)
365
+ assert Timedelta("1 minute") == timedelta(minutes=1)
366
+ assert Timedelta("1 minutes") == timedelta(minutes=1)
367
+ assert Timedelta("1 s") == timedelta(seconds=1)
368
+ assert Timedelta("1 second") == timedelta(seconds=1)
369
+ assert Timedelta("1 seconds") == timedelta(seconds=1)
370
+ assert Timedelta("1 ms") == timedelta(milliseconds=1)
371
+ assert Timedelta("1 milli") == timedelta(milliseconds=1)
372
+ assert Timedelta("1 millisecond") == timedelta(milliseconds=1)
373
+ assert Timedelta("1 us") == timedelta(microseconds=1)
374
+ assert Timedelta("1 µs") == timedelta(microseconds=1)
375
+ assert Timedelta("1 micros") == timedelta(microseconds=1)
376
+ assert Timedelta("1 microsecond") == timedelta(microseconds=1)
377
+ assert Timedelta("1.5 microsecond") == Timedelta("00:00:00.000001500")
378
+ assert Timedelta("1 ns") == Timedelta("00:00:00.000000001")
379
+ assert Timedelta("1 nano") == Timedelta("00:00:00.000000001")
380
+ assert Timedelta("1 nanosecond") == Timedelta("00:00:00.000000001")
381
+
382
+ # combos
383
+ assert Timedelta("10 days 1 hour") == timedelta(days=10, hours=1)
384
+ assert Timedelta("10 days 1 h") == timedelta(days=10, hours=1)
385
+ assert Timedelta("10 days 1 h 1m 1s") == timedelta(
386
+ days=10, hours=1, minutes=1, seconds=1
387
+ )
388
+ assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
389
+ days=10, hours=1, minutes=1, seconds=1
390
+ )
391
+ assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
392
+ days=10, hours=1, minutes=1, seconds=1
393
+ )
394
+ assert Timedelta("-10 days 1 h 1m 1s 3us") == -timedelta(
395
+ days=10, hours=1, minutes=1, seconds=1, microseconds=3
396
+ )
397
+ assert Timedelta("-10 days 1 h 1.5m 1s 3us") == -timedelta(
398
+ days=10, hours=1, minutes=1, seconds=31, microseconds=3
399
+ )
400
+
401
+ # Currently invalid as it has a - on the hh:mm:dd part
402
+ # (only allowed on the days)
403
+ msg = "only leading negative signs are allowed"
404
+ with pytest.raises(ValueError, match=msg):
405
+ Timedelta("-10 days -1 h 1.5m 1s 3us")
406
+
407
+ # only leading neg signs are allowed
408
+ with pytest.raises(ValueError, match=msg):
409
+ Timedelta("10 days -1 h 1.5m 1s 3us")
410
+
411
+ # no units specified
412
+ msg = "no units specified"
413
+ with pytest.raises(ValueError, match=msg):
414
+ Timedelta("3.1415")
415
+
416
+ # invalid construction
417
+ msg = "cannot construct a Timedelta"
418
+ with pytest.raises(ValueError, match=msg):
419
+ Timedelta()
420
+
421
+ msg = "unit abbreviation w/o a number"
422
+ with pytest.raises(ValueError, match=msg):
423
+ Timedelta("foo")
424
+
425
+ msg = (
426
+ "cannot construct a Timedelta from the passed arguments, allowed keywords are "
427
+ )
428
+ with pytest.raises(ValueError, match=msg):
429
+ Timedelta(day=10)
430
+
431
+ # floats
432
+ expected = np.timedelta64(10, "s").astype("m8[ns]").view("i8") + np.timedelta64(
433
+ 500, "ms"
434
+ ).astype("m8[ns]").view("i8")
435
+ assert Timedelta(10.5, unit="s")._value == expected
436
+
437
+ # offset
438
+ assert to_timedelta(offsets.Hour(2)) == Timedelta(hours=2)
439
+ assert Timedelta(offsets.Hour(2)) == Timedelta(hours=2)
440
+ assert Timedelta(offsets.Second(2)) == Timedelta(seconds=2)
441
+
442
+ # GH#11995: unicode
443
+ expected = Timedelta("1h")
444
+ result = Timedelta("1h")
445
+ assert result == expected
446
+ assert to_timedelta(offsets.Hour(2)) == Timedelta("0 days, 02:00:00")
447
+
448
+ msg = "unit abbreviation w/o a number"
449
+ with pytest.raises(ValueError, match=msg):
450
+ Timedelta("foo bar")
451
+
452
+
453
+ @pytest.mark.parametrize(
454
+ "item",
455
+ list(
456
+ {
457
+ "days": "D",
458
+ "seconds": "s",
459
+ "microseconds": "us",
460
+ "milliseconds": "ms",
461
+ "minutes": "m",
462
+ "hours": "h",
463
+ "weeks": "W",
464
+ }.items()
465
+ ),
466
+ )
467
+ @pytest.mark.parametrize(
468
+ "npdtype", [np.int64, np.int32, np.int16, np.float64, np.float32, np.float16]
469
+ )
470
+ def test_td_construction_with_np_dtypes(npdtype, item):
471
+ # GH#8757: test construction with np dtypes
472
+ pykwarg, npkwarg = item
473
+ expected = np.timedelta64(1, npkwarg).astype("m8[us]").view("i8")
474
+ assert Timedelta(**{pykwarg: npdtype(1)})._value == expected
475
+
476
+
477
+ @pytest.mark.parametrize(
478
+ "val",
479
+ [
480
+ "1s",
481
+ "-1s",
482
+ "1us",
483
+ "-1us",
484
+ "1 day",
485
+ "-1 day",
486
+ "-23:59:59.999999",
487
+ "-1 days +23:59:59.999999",
488
+ "-1ns",
489
+ "1ns",
490
+ "-23:59:59.999999999",
491
+ ],
492
+ )
493
+ def test_td_from_repr_roundtrip(val):
494
+ # round-trip both for string and value
495
+ td = Timedelta(val)
496
+ assert Timedelta(td.value) == td
497
+
498
+ assert Timedelta(str(td)) == td
499
+ assert Timedelta(td._repr_base(format="all")) == td
500
+ assert Timedelta(td._repr_base()) == td
501
+
502
+
503
+ def test_overflow_on_construction():
504
+ # GH#3374
505
+ value = Timedelta("1day").as_unit("ns")._value * 20169940
506
+ msg = "Cannot cast 1742682816000000000000 from ns to 'ns' without overflow"
507
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
508
+ Timedelta(value)
509
+
510
+ # xref GH#17637
511
+ # used to overflows before we changed output unit to "s"
512
+ td = Timedelta(7 * 19999, unit="D")
513
+ assert td.unit == "s"
514
+
515
+ # used to overflow before non-ns support
516
+ td = Timedelta(timedelta(days=13 * 19999))
517
+ assert td._creso == NpyDatetimeUnit.NPY_FR_us.value
518
+ assert td.days == 13 * 19999
519
+
520
+
521
+ @pytest.mark.parametrize(
522
+ "val, unit",
523
+ [
524
+ (15251, "W"), # 1
525
+ (106752, "D"), # change from previous:
526
+ (2562048, "h"), # 0 hours
527
+ (153722868, "m"), # 13 minutes
528
+ (9223372037, "s"), # 44 seconds
529
+ ],
530
+ )
531
+ def test_construction_out_of_bounds_td64ns(val, unit):
532
+ # TODO: parametrize over units just above/below the implementation bounds
533
+ # once GH#38964 is resolved
534
+
535
+ # Timedelta.max is just under 106752 days
536
+ td64 = np.timedelta64(val, unit)
537
+ if is_numpy_dev or np_version_gt2_5:
538
+ # numpy now raises instead of silently overflowing
539
+ with pytest.raises(OverflowError, match="Overflow"):
540
+ td64.astype("m8[ns]")
541
+ else:
542
+ assert td64.astype("m8[ns]").view("i8") < 0 # i.e. naive astype will be wrong
543
+
544
+ td = Timedelta(td64)
545
+ if unit != "M":
546
+ # with unit="M" the conversion to "s" is poorly defined
547
+ # (and numpy issues DeprecationWarning)
548
+ assert td.asm8 == td64
549
+ assert td.asm8.dtype == "m8[s]"
550
+ msg = r"Cannot cast 1067\d\d days .* to unit='ns' without overflow"
551
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
552
+ td.as_unit("ns")
553
+
554
+ # But just back in bounds and we are OK
555
+ one = np.timedelta64(1, unit)
556
+ assert Timedelta(td64 - one) == td64 - one
557
+
558
+ td64 = np.timedelta64(-val, unit)
559
+ if is_numpy_dev or np_version_gt2_5:
560
+ with pytest.raises(OverflowError, match="Overflow"):
561
+ td64.astype("m8[ns]")
562
+ else:
563
+ assert td64.astype("m8[ns]").view("i8") > 0 # i.e. naive astype will be wrong
564
+
565
+ td2 = Timedelta(td64)
566
+ msg = r"Cannot cast -1067\d\d days .* to unit='ns' without overflow"
567
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
568
+ td2.as_unit("ns")
569
+
570
+ # But just back in bounds and we are OK
571
+ assert Timedelta(td64 + one) == td64 + one
572
+
573
+
574
+ @pytest.mark.parametrize(
575
+ "val, unit",
576
+ [
577
+ (15251 * 10**9, "W"),
578
+ (106752 * 10**9, "D"),
579
+ (2562048 * 10**9, "h"),
580
+ (153722868 * 10**9, "m"),
581
+ ],
582
+ )
583
+ def test_construction_out_of_bounds_td64s(val, unit):
584
+ td64 = np.timedelta64(val, unit)
585
+ with pytest.raises(OutOfBoundsTimedelta, match=str(td64)):
586
+ Timedelta(td64)
587
+
588
+ # But just back in bounds and we are OK
589
+ offset = np.timedelta64(10**9, unit)
590
+ assert Timedelta(td64 - offset) == td64 - offset
591
+
592
+
593
+ @pytest.mark.parametrize(
594
+ "fmt,exp",
595
+ [
596
+ (
597
+ "P6DT0H50M3.010010012S",
598
+ Timedelta(
599
+ days=6,
600
+ minutes=50,
601
+ seconds=3,
602
+ milliseconds=10,
603
+ microseconds=10,
604
+ nanoseconds=12,
605
+ ),
606
+ ),
607
+ (
608
+ "P-6DT0H50M3.010010012S",
609
+ Timedelta(
610
+ days=-6,
611
+ minutes=50,
612
+ seconds=3,
613
+ milliseconds=10,
614
+ microseconds=10,
615
+ nanoseconds=12,
616
+ ),
617
+ ),
618
+ ("P4DT12H30M5S", Timedelta(days=4, hours=12, minutes=30, seconds=5)),
619
+ ("P0DT0H0M0.000000123S", Timedelta(nanoseconds=123)),
620
+ ("P0DT0H0M0.00001S", Timedelta(microseconds=10)),
621
+ ("P0DT0H0M0.001S", Timedelta(milliseconds=1)),
622
+ ("P0DT0H1M0S", Timedelta(minutes=1)),
623
+ ("P1DT25H61M61S", Timedelta(days=1, hours=25, minutes=61, seconds=61)),
624
+ ("PT1S", Timedelta(seconds=1)),
625
+ ("PT0S", Timedelta(seconds=0)),
626
+ ("P1WT0S", Timedelta(days=7, seconds=0)),
627
+ ("P1D", Timedelta(days=1)),
628
+ ("P1DT1H", Timedelta(days=1, hours=1)),
629
+ ("P1W", Timedelta(days=7)),
630
+ ("PT300S", Timedelta(seconds=300)),
631
+ ("P1DT0H0M00000000000S", Timedelta(days=1)),
632
+ ("PT-6H3M", Timedelta(hours=-6, minutes=3)),
633
+ ("-PT6H3M", Timedelta(hours=-6, minutes=-3)),
634
+ ("-PT-6H+3M", Timedelta(hours=6, minutes=-3)),
635
+ ],
636
+ )
637
+ def test_iso_constructor(fmt, exp):
638
+ assert Timedelta(fmt) == exp
639
+
640
+
641
+ @pytest.mark.parametrize(
642
+ "fmt",
643
+ [
644
+ "PPPPPPPPPPPP",
645
+ "PDTHMS",
646
+ "P0DT999H999M999S",
647
+ "P1DT0H0M0.0000000000000S",
648
+ "P1DT0H0M0.S",
649
+ "P",
650
+ "-P",
651
+ ],
652
+ )
653
+ def test_iso_constructor_raises(fmt):
654
+ msg = f"Invalid ISO 8601 Duration format - {fmt}"
655
+ with pytest.raises(ValueError, match=msg):
656
+ Timedelta(fmt)
657
+
658
+
659
+ @pytest.mark.parametrize(
660
+ "constructed_td, conversion",
661
+ [
662
+ (Timedelta(nanoseconds=100), "100ns"),
663
+ (
664
+ Timedelta(
665
+ days=1,
666
+ hours=1,
667
+ minutes=1,
668
+ weeks=1,
669
+ seconds=1,
670
+ milliseconds=1,
671
+ microseconds=1,
672
+ nanoseconds=1,
673
+ ),
674
+ 694861001001001,
675
+ ),
676
+ (Timedelta(microseconds=1) + Timedelta(nanoseconds=1), "1us1ns"),
677
+ (Timedelta(microseconds=1) - Timedelta(nanoseconds=1), "999ns"),
678
+ (Timedelta(microseconds=1) + 5 * Timedelta(nanoseconds=-2), "990ns"),
679
+ ],
680
+ )
681
+ def test_td_constructor_on_nanoseconds(constructed_td, conversion):
682
+ # GH#9273
683
+ assert constructed_td == Timedelta(conversion)
684
+
685
+
686
+ def test_td_constructor_value_error():
687
+ msg = "Invalid type <class 'str'>. Must be int or float."
688
+ with pytest.raises(TypeError, match=msg):
689
+ Timedelta(nanoseconds="abc")
690
+
691
+
692
+ def test_timedelta_constructor_identity():
693
+ # Test for #30543
694
+ expected = Timedelta(np.timedelta64(1, "s"))
695
+ result = Timedelta(expected)
696
+ assert result is expected
697
+
698
+
699
+ def test_timedelta_pass_td_and_kwargs_raises():
700
+ # don't silently ignore the kwargs GH#48898
701
+ td = Timedelta(days=1)
702
+ msg = (
703
+ "Cannot pass both a Timedelta input and timedelta keyword arguments, "
704
+ r"got \['days'\]"
705
+ )
706
+ with pytest.raises(ValueError, match=msg):
707
+ Timedelta(td, days=2)
708
+
709
+
710
+ @pytest.mark.filterwarnings(
711
+ "ignore:The 'unit' keyword is only used when the Timedelta input:UserWarning"
712
+ )
713
+ @pytest.mark.parametrize(
714
+ "constructor, value, unit",
715
+ [
716
+ (Timedelta, "10s", "ms"),
717
+ (to_timedelta, "10s", "ms"),
718
+ (to_timedelta, ["1", 2, 3], "s"),
719
+ ],
720
+ )
721
+ def test_string_with_unit(constructor, value, unit):
722
+ with pytest.raises(ValueError, match="unit must not be specified"):
723
+ constructor(value, unit=unit)
724
+
725
+
726
+ @pytest.mark.parametrize(
727
+ "value",
728
+ [
729
+ "".join(elements)
730
+ for repetition in (1, 2)
731
+ for elements in product("+-, ", repeat=repetition)
732
+ ],
733
+ )
734
+ def test_string_without_numbers(value):
735
+ # GH39710 Timedelta input string with only symbols and no digits raises an error
736
+ msg = (
737
+ "symbols w/o a number"
738
+ if value != "--"
739
+ else "only leading negative signs are allowed"
740
+ )
741
+ with pytest.raises(ValueError, match=msg):
742
+ Timedelta(value)
743
+
744
+
745
+ def test_timedelta_new_npnat():
746
+ # GH#48898
747
+ nat = np.timedelta64("NaT", "h")
748
+ assert Timedelta(nat) is NaT
749
+
750
+
751
+ def test_subclass_respected():
752
+ # GH#49579
753
+ class MyCustomTimedelta(Timedelta):
754
+ pass
755
+
756
+ td = MyCustomTimedelta("1 minute")
757
+ assert isinstance(td, MyCustomTimedelta)
758
+
759
+
760
+ def test_non_nano_value():
761
+ # https://github.com/pandas-dev/pandas/issues/49076
762
+ result = Timedelta(10, unit="D").as_unit("s").value
763
+ # `.value` shows nanoseconds, even though unit is 's'
764
+ assert result == 864000000000000
765
+
766
+ # out-of-nanoseconds-bounds `.value` raises informative message
767
+ msg = (
768
+ r"Cannot convert Timedelta to nanoseconds without overflow. "
769
+ r"Use `.asm8.view\('i8'\)` to cast represent Timedelta in its "
770
+ r"own unit \(here, s\).$"
771
+ )
772
+ td = Timedelta(1_000, "D").as_unit("s") * 1_000
773
+ with pytest.raises(OverflowError, match=msg):
774
+ td.value
775
+ # check that the suggested workaround actually works
776
+ result = td.asm8.view("i8")
777
+ assert result == 86400000000
778
+
779
+
780
+ def test_parsed_unit():
781
+ td = Timedelta("1 Day")
782
+ assert td.unit == "us"
783
+
784
+ td = Timedelta("1 Day 2 hours 3 minutes 4 ns")
785
+ assert td.unit == "ns"
786
+
787
+ td = Timedelta("1 Day 2:03:04.012345")
788
+ assert td.unit == "us"
789
+
790
+ td = Timedelta("1 Day 2:03:04.012345000")
791
+ assert td.unit == "ns"
792
+
793
+ # 7 digits after the decimal
794
+ td = Timedelta("1 Day 2:03:04.0123450")
795
+ assert td.unit == "ns"
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_formats.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas import Timedelta
4
+
5
+
6
+ @pytest.mark.parametrize(
7
+ "td, expected_repr",
8
+ [
9
+ (Timedelta(10, unit="D"), "Timedelta('10 days 00:00:00')"),
10
+ (Timedelta(10, unit="s"), "Timedelta('0 days 00:00:10')"),
11
+ (Timedelta(10, unit="ms"), "Timedelta('0 days 00:00:00.010000')"),
12
+ (Timedelta(-10, unit="ms"), "Timedelta('-1 days +23:59:59.990000')"),
13
+ ],
14
+ )
15
+ def test_repr(td, expected_repr):
16
+ assert repr(td) == expected_repr
17
+
18
+
19
+ @pytest.mark.parametrize(
20
+ "td, expected_iso",
21
+ [
22
+ (
23
+ Timedelta(
24
+ days=6,
25
+ minutes=50,
26
+ seconds=3,
27
+ milliseconds=10,
28
+ microseconds=10,
29
+ nanoseconds=12,
30
+ ),
31
+ "P6DT0H50M3.010010012S",
32
+ ),
33
+ (Timedelta(days=4, hours=12, minutes=30, seconds=5), "P4DT12H30M5S"),
34
+ (Timedelta(nanoseconds=123), "P0DT0H0M0.000000123S"),
35
+ # trim nano
36
+ (Timedelta(microseconds=10), "P0DT0H0M0.00001S"),
37
+ # trim micro
38
+ (Timedelta(milliseconds=1), "P0DT0H0M0.001S"),
39
+ # don't strip every 0
40
+ (Timedelta(minutes=1), "P0DT0H1M0S"),
41
+ ],
42
+ )
43
+ def test_isoformat(td, expected_iso):
44
+ assert td.isoformat() == expected_iso
45
+
46
+
47
+ class TestReprBase:
48
+ def test_none(self):
49
+ delta_1d = Timedelta(1, unit="D")
50
+ delta_0d = Timedelta(0, unit="D")
51
+ delta_1s = Timedelta(1, unit="s")
52
+ delta_500ms = Timedelta(500, unit="ms")
53
+
54
+ drepr = lambda x: x._repr_base()
55
+ assert drepr(delta_1d) == "1 days"
56
+ assert drepr(-delta_1d) == "-1 days"
57
+ assert drepr(delta_0d) == "0 days"
58
+ assert drepr(delta_1s) == "0 days 00:00:01"
59
+ assert drepr(delta_500ms) == "0 days 00:00:00.500000"
60
+ assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
61
+ assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
62
+ assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
63
+ assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
64
+
65
+ def test_sub_day(self):
66
+ delta_1d = Timedelta(1, unit="D")
67
+ delta_0d = Timedelta(0, unit="D")
68
+ delta_1s = Timedelta(1, unit="s")
69
+ delta_500ms = Timedelta(500, unit="ms")
70
+
71
+ drepr = lambda x: x._repr_base(format="sub_day")
72
+ assert drepr(delta_1d) == "1 days"
73
+ assert drepr(-delta_1d) == "-1 days"
74
+ assert drepr(delta_0d) == "00:00:00"
75
+ assert drepr(delta_1s) == "00:00:01"
76
+ assert drepr(delta_500ms) == "00:00:00.500000"
77
+ assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
78
+ assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
79
+ assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
80
+ assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
81
+
82
+ def test_long(self):
83
+ delta_1d = Timedelta(1, unit="D")
84
+ delta_0d = Timedelta(0, unit="D")
85
+ delta_1s = Timedelta(1, unit="s")
86
+ delta_500ms = Timedelta(500, unit="ms")
87
+
88
+ drepr = lambda x: x._repr_base(format="long")
89
+ assert drepr(delta_1d) == "1 days 00:00:00"
90
+ assert drepr(-delta_1d) == "-1 days +00:00:00"
91
+ assert drepr(delta_0d) == "0 days 00:00:00"
92
+ assert drepr(delta_1s) == "0 days 00:00:01"
93
+ assert drepr(delta_500ms) == "0 days 00:00:00.500000"
94
+ assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
95
+ assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
96
+ assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
97
+ assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
98
+
99
+ def test_all(self):
100
+ delta_1d = Timedelta(1, unit="D")
101
+ delta_0d = Timedelta(0, unit="D")
102
+ delta_1ns = Timedelta(1, unit="ns")
103
+
104
+ drepr = lambda x: x._repr_base(format="all")
105
+ assert drepr(delta_1d) == "1 days 00:00:00.000000000"
106
+ assert drepr(-delta_1d) == "-1 days +00:00:00.000000000"
107
+ assert drepr(delta_0d) == "0 days 00:00:00.000000000"
108
+ assert drepr(delta_1ns) == "0 days 00:00:00.000000001"
109
+ assert drepr(-delta_1d + delta_1ns) == "-1 days +00:00:00.000000001"
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py ADDED
@@ -0,0 +1,740 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """test the scalar Timedelta"""
2
+
3
+ from datetime import timedelta
4
+ import sys
5
+
6
+ from hypothesis import (
7
+ given,
8
+ strategies as st,
9
+ )
10
+ import numpy as np
11
+ import pytest
12
+
13
+ from pandas._libs import lib
14
+ from pandas._libs.missing import NA
15
+ from pandas._libs.tslibs import (
16
+ NaT,
17
+ iNaT,
18
+ )
19
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
20
+ from pandas.errors import (
21
+ OutOfBoundsTimedelta,
22
+ Pandas4Warning,
23
+ )
24
+
25
+ from pandas import (
26
+ Timedelta,
27
+ to_timedelta,
28
+ )
29
+ import pandas._testing as tm
30
+
31
+
32
+ class TestNonNano:
33
+ @pytest.fixture(params=["s", "ms", "us"])
34
+ def unit_str(self, request):
35
+ return request.param
36
+
37
+ @pytest.fixture
38
+ def unit(self, unit_str):
39
+ # 7, 8, 9 correspond to second, millisecond, and microsecond, respectively
40
+ attr = f"NPY_FR_{unit_str}"
41
+ return getattr(NpyDatetimeUnit, attr).value
42
+
43
+ @pytest.fixture
44
+ def val(self, unit):
45
+ # microsecond that would be just out of bounds for nano
46
+ us = 9223372800000000
47
+ if unit == NpyDatetimeUnit.NPY_FR_us.value:
48
+ value = us
49
+ elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
50
+ value = us // 1000
51
+ else:
52
+ value = us // 1_000_000
53
+ return value
54
+
55
+ @pytest.fixture
56
+ def td(self, unit, val):
57
+ return Timedelta._from_value_and_reso(val, unit)
58
+
59
+ def test_from_value_and_reso(self, unit, val):
60
+ # Just checking that the fixture is giving us what we asked for
61
+ td = Timedelta._from_value_and_reso(val, unit)
62
+ assert td._value == val
63
+ assert td._creso == unit
64
+ assert td.days == 106752
65
+
66
+ def test_unary_non_nano(self, td, unit):
67
+ assert abs(td)._creso == unit
68
+ assert (-td)._creso == unit
69
+ assert (+td)._creso == unit
70
+
71
+ def test_sub_preserves_reso(self, td, unit):
72
+ res = td - td
73
+ expected = Timedelta._from_value_and_reso(0, unit)
74
+ assert res == expected
75
+ assert res._creso == unit
76
+
77
+ def test_mul_preserves_reso(self, td, unit):
78
+ # The td fixture should always be far from the implementation
79
+ # bound, so doubling does not risk overflow.
80
+ res = td * 2
81
+ assert res._value == td._value * 2
82
+ assert res._creso == unit
83
+
84
+ def test_cmp_cross_reso(self, td):
85
+ # numpy gets this wrong because of silent overflow
86
+ other = Timedelta(days=106751)
87
+ assert other < td
88
+ assert td > other
89
+ assert not other == td
90
+ assert td != other
91
+
92
+ def test_to_pytimedelta(self, td):
93
+ res = td.to_pytimedelta()
94
+ expected = timedelta(days=106752)
95
+ assert type(res) is timedelta
96
+ assert res == expected
97
+
98
+ def test_to_timedelta64(self, td, unit):
99
+ for res in [td.to_timedelta64(), td.to_numpy(), td.asm8]:
100
+ assert isinstance(res, np.timedelta64)
101
+ assert res.view("i8") == td._value
102
+ if unit == NpyDatetimeUnit.NPY_FR_s.value:
103
+ assert res.dtype == "m8[s]"
104
+ elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
105
+ assert res.dtype == "m8[ms]"
106
+ elif unit == NpyDatetimeUnit.NPY_FR_us.value:
107
+ assert res.dtype == "m8[us]"
108
+
109
+ def test_truediv_timedeltalike(self, td):
110
+ assert td / td == 1
111
+ assert (2.5 * td) / td == 2.5
112
+
113
+ other = Timedelta(td._value)
114
+ msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow."
115
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
116
+ td / other
117
+
118
+ # Timedelta(other.to_pytimedelta()) has microsecond resolution,
119
+ # so the division doesn't require casting all the way to nanos,
120
+ # so succeeds
121
+ res = other.to_pytimedelta() / td
122
+ expected = other.to_pytimedelta() / td.to_pytimedelta()
123
+ assert res == expected
124
+
125
+ # if there's no overflow, we cast to the higher reso
126
+ left = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_us.value)
127
+ right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
128
+ result = left / right
129
+ assert result == 0.001
130
+
131
+ result = right / left
132
+ assert result == 1000
133
+
134
+ def test_truediv_numeric(self, td):
135
+ assert td / np.nan is NaT
136
+
137
+ res = td / 2
138
+ assert res._value == td._value / 2
139
+ assert res._creso == td._creso
140
+
141
+ res = td / 2.0
142
+ assert res._value == td._value / 2
143
+ assert res._creso == td._creso
144
+
145
+ def test_truediv_na_type_not_supported(self, td):
146
+ msg_td_floordiv_na = (
147
+ r"unsupported operand type\(s\) for /: 'Timedelta' and 'NAType'"
148
+ )
149
+ with pytest.raises(TypeError, match=msg_td_floordiv_na):
150
+ td / NA
151
+
152
+ msg_na_floordiv_td = (
153
+ r"unsupported operand type\(s\) for /: 'NAType' and 'Timedelta'"
154
+ )
155
+ with pytest.raises(TypeError, match=msg_na_floordiv_td):
156
+ NA / td
157
+
158
+ def test_floordiv_timedeltalike(self, td):
159
+ assert td // td == 1
160
+ assert (2.5 * td) // td == 2
161
+
162
+ other = Timedelta(td._value)
163
+ msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
164
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
165
+ td // other
166
+
167
+ # Timedelta(other.to_pytimedelta()) has microsecond resolution,
168
+ # so the floordiv doesn't require casting all the way to nanos,
169
+ # so succeeds
170
+ res = other.to_pytimedelta() // td
171
+ assert res == 0
172
+
173
+ # if there's no overflow, we cast to the higher reso
174
+ left = Timedelta._from_value_and_reso(50050, NpyDatetimeUnit.NPY_FR_us.value)
175
+ right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
176
+ result = left // right
177
+ assert result == 1
178
+ result = right // left
179
+ assert result == 0
180
+
181
+ def test_floordiv_numeric(self, td):
182
+ assert td // np.nan is NaT
183
+
184
+ res = td // 2
185
+ assert res._value == td._value // 2
186
+ assert res._creso == td._creso
187
+
188
+ res = td // 2.0
189
+ assert res._value == td._value // 2
190
+ assert res._creso == td._creso
191
+
192
+ assert td // np.array(np.nan) is NaT
193
+
194
+ res = td // np.array(2)
195
+ assert res._value == td._value // 2
196
+ assert res._creso == td._creso
197
+
198
+ res = td // np.array(2.0)
199
+ assert res._value == td._value // 2
200
+ assert res._creso == td._creso
201
+
202
+ def test_floordiv_na_type_not_supported(self, td):
203
+ msg_td_floordiv_na = (
204
+ r"unsupported operand type\(s\) for //: 'Timedelta' and 'NAType'"
205
+ )
206
+ with pytest.raises(TypeError, match=msg_td_floordiv_na):
207
+ td // NA
208
+
209
+ msg_na_floordiv_td = (
210
+ r"unsupported operand type\(s\) for //: 'NAType' and 'Timedelta'"
211
+ )
212
+ with pytest.raises(TypeError, match=msg_na_floordiv_td):
213
+ NA // td
214
+
215
+ def test_addsub_mismatched_reso(self, td):
216
+ # need to cast to since td is out of bounds for ns, so
217
+ # so we would raise OverflowError without casting
218
+ other = Timedelta(days=1).as_unit("us")
219
+
220
+ # td is out of bounds for ns
221
+ result = td + other
222
+ assert result._creso == other._creso
223
+ assert result.days == td.days + 1
224
+
225
+ result = other + td
226
+ assert result._creso == other._creso
227
+ assert result.days == td.days + 1
228
+
229
+ result = td - other
230
+ assert result._creso == other._creso
231
+ assert result.days == td.days - 1
232
+
233
+ result = other - td
234
+ assert result._creso == other._creso
235
+ assert result.days == 1 - td.days
236
+
237
+ other2 = Timedelta(500)
238
+ msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
239
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
240
+ td + other2
241
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
242
+ other2 + td
243
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
244
+ td - other2
245
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
246
+ other2 - td
247
+
248
+ def test_min(self, td):
249
+ assert td.min <= td
250
+ assert td.min._creso == td._creso
251
+ assert td.min._value == NaT._value + 1
252
+
253
+ def test_max(self, td):
254
+ assert td.max >= td
255
+ assert td.max._creso == td._creso
256
+ assert td.max._value == np.iinfo(np.int64).max
257
+
258
+ def test_resolution(self, td):
259
+ expected = Timedelta._from_value_and_reso(1, td._creso)
260
+ result = td.resolution
261
+ assert result == expected
262
+ assert result._creso == expected._creso
263
+
264
+ def test_hash(self) -> None:
265
+ # GH#54037
266
+ second_resolution_max = Timedelta(0).as_unit("s").max
267
+
268
+ assert hash(second_resolution_max)
269
+
270
+
271
+ def test_timedelta_class_min_max_resolution():
272
+ # when accessed on the class (as opposed to an instance), we default
273
+ # to nanoseconds
274
+ assert Timedelta.min == Timedelta(NaT._value + 1)
275
+ assert Timedelta.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
276
+
277
+ assert Timedelta.max == Timedelta(np.iinfo(np.int64).max)
278
+ assert Timedelta.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
279
+
280
+ assert Timedelta.resolution == Timedelta(1)
281
+ assert Timedelta.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
282
+
283
+
284
+ class TestTimedeltaUnaryOps:
285
+ def test_invert(self):
286
+ td = Timedelta(10, unit="D")
287
+
288
+ msg = "bad operand type for unary ~"
289
+ with pytest.raises(TypeError, match=msg):
290
+ ~td
291
+
292
+ # check this matches pytimedelta and timedelta64
293
+ with pytest.raises(TypeError, match=msg):
294
+ ~(td.to_pytimedelta())
295
+
296
+ umsg = "ufunc 'invert' not supported for the input types"
297
+ with pytest.raises(TypeError, match=umsg):
298
+ ~(td.to_timedelta64())
299
+
300
+ def test_unary_ops(self):
301
+ td = Timedelta(10, unit="D")
302
+
303
+ # __neg__, __pos__
304
+ assert -td == Timedelta(-10, unit="D")
305
+ assert -td == Timedelta("-10D")
306
+ assert +td == Timedelta(10, unit="D")
307
+
308
+ # __abs__, __abs__(__neg__)
309
+ assert abs(td) == td
310
+ assert abs(-td) == td
311
+ assert abs(-td) == Timedelta("10D")
312
+
313
+
314
+ class TestTimedeltas:
315
+ @pytest.mark.parametrize(
316
+ "unit, value, expected",
317
+ [
318
+ ("us", 9.999, 9999),
319
+ ("ms", 9.999999, 9999999),
320
+ ("s", 9.999999999, 9999999999),
321
+ ],
322
+ )
323
+ def test_rounding_on_int_unit_construction(self, unit, value, expected):
324
+ # GH 12690
325
+ result = Timedelta(value, unit=unit)
326
+ assert result._value == expected
327
+ result = Timedelta(str(value) + unit)
328
+ assert result._value == expected
329
+
330
+ def test_total_seconds_scalar(self):
331
+ # see gh-10939
332
+ rng = Timedelta("1 days, 10:11:12.100123456")
333
+ expt = 1 * 86400 + 10 * 3600 + 11 * 60 + 12 + 100123456.0 / 1e9
334
+ tm.assert_almost_equal(rng.total_seconds(), expt)
335
+
336
+ rng = Timedelta(np.nan)
337
+ assert np.isnan(rng.total_seconds())
338
+
339
+ def test_conversion(self):
340
+ for td in [Timedelta(10, unit="D"), Timedelta("1 days, 10:11:12.012345")]:
341
+ td = td.as_unit("ns")
342
+ pydt = td.to_pytimedelta()
343
+ assert td == Timedelta(pydt)
344
+ assert td == pydt
345
+ assert isinstance(pydt, timedelta) and not isinstance(pydt, Timedelta)
346
+
347
+ assert td == np.timedelta64(td._value, "ns")
348
+ td64 = td.to_timedelta64()
349
+
350
+ assert td64 == np.timedelta64(td._value, "ns")
351
+ assert td == td64
352
+
353
+ assert isinstance(td64, np.timedelta64)
354
+
355
+ # this is NOT equal and cannot be roundtripped (because of the nanos)
356
+ td = Timedelta("1 days, 10:11:12.012345678")
357
+ assert td != td.to_pytimedelta()
358
+
359
+ def test_fields(self):
360
+ def check(value):
361
+ # that we are int
362
+ assert isinstance(value, int)
363
+
364
+ # compat to datetime.timedelta
365
+ rng = to_timedelta("1 days, 10:11:12")
366
+ assert rng.days == 1
367
+ assert rng.seconds == 10 * 3600 + 11 * 60 + 12
368
+ assert rng.microseconds == 0
369
+ assert rng.nanoseconds == 0
370
+
371
+ msg = "'Timedelta' object has no attribute '{}'"
372
+ with pytest.raises(AttributeError, match=msg.format("hours")):
373
+ rng.hours
374
+ with pytest.raises(AttributeError, match=msg.format("minutes")):
375
+ rng.minutes
376
+ with pytest.raises(AttributeError, match=msg.format("milliseconds")):
377
+ rng.milliseconds
378
+
379
+ # GH 10050
380
+ check(rng.days)
381
+ check(rng.seconds)
382
+ check(rng.microseconds)
383
+ check(rng.nanoseconds)
384
+
385
+ td = Timedelta("-1 days, 10:11:12")
386
+ assert abs(td) == Timedelta("13:48:48")
387
+ assert str(td) == "-1 days +10:11:12"
388
+ assert -td == Timedelta("0 days 13:48:48")
389
+ assert -Timedelta("-1 days, 10:11:12")._value == 49728000000
390
+ assert Timedelta("-1 days, 10:11:12")._value == -49728000000
391
+
392
+ rng = to_timedelta("-1 days, 10:11:12.100123456")
393
+ assert rng.days == -1
394
+ assert rng.seconds == 10 * 3600 + 11 * 60 + 12
395
+ assert rng.microseconds == 100 * 1000 + 123
396
+ assert rng.nanoseconds == 456
397
+ msg = "'Timedelta' object has no attribute '{}'"
398
+ with pytest.raises(AttributeError, match=msg.format("hours")):
399
+ rng.hours
400
+ with pytest.raises(AttributeError, match=msg.format("minutes")):
401
+ rng.minutes
402
+ with pytest.raises(AttributeError, match=msg.format("milliseconds")):
403
+ rng.milliseconds
404
+
405
+ # components
406
+ tup = to_timedelta(-1, "us").components
407
+ assert tup.days == -1
408
+ assert tup.hours == 23
409
+ assert tup.minutes == 59
410
+ assert tup.seconds == 59
411
+ assert tup.milliseconds == 999
412
+ assert tup.microseconds == 999
413
+ assert tup.nanoseconds == 0
414
+
415
+ # GH 10050
416
+ check(tup.days)
417
+ check(tup.hours)
418
+ check(tup.minutes)
419
+ check(tup.seconds)
420
+ check(tup.milliseconds)
421
+ check(tup.microseconds)
422
+ check(tup.nanoseconds)
423
+
424
+ tup = Timedelta("-1 days 1 us").components
425
+ assert tup.days == -2
426
+ assert tup.hours == 23
427
+ assert tup.minutes == 59
428
+ assert tup.seconds == 59
429
+ assert tup.milliseconds == 999
430
+ assert tup.microseconds == 999
431
+ assert tup.nanoseconds == 0
432
+
433
+ # TODO: this is a test of to_timedelta string parsing
434
+ def test_iso_conversion(self):
435
+ # GH #21877
436
+ expected = Timedelta(1, unit="s")
437
+ assert to_timedelta("P0DT0H0M1S") == expected
438
+
439
+ # TODO: this is a test of to_timedelta returning NaT
440
+ def test_nat_converters(self):
441
+ result = to_timedelta("nat").to_numpy()
442
+ assert result.dtype.kind == "M"
443
+ assert result.astype("int64") == iNaT
444
+
445
+ result = to_timedelta("nan").to_numpy()
446
+ assert result.dtype.kind == "M"
447
+ assert result.astype("int64") == iNaT
448
+
449
+ def test_numeric_conversions(self):
450
+ assert Timedelta(0) == np.timedelta64(0, "ns")
451
+ assert Timedelta(10) == np.timedelta64(10, "ns")
452
+ assert Timedelta(10, unit="ns") == np.timedelta64(10, "ns")
453
+
454
+ assert Timedelta(10, unit="us") == np.timedelta64(10, "us")
455
+ assert Timedelta(10, unit="ms") == np.timedelta64(10, "ms")
456
+ assert Timedelta(10, unit="s") == np.timedelta64(10, "s")
457
+ assert Timedelta(10, unit="D") == np.timedelta64(10, "D")
458
+
459
+ def test_timedelta_conversions(self):
460
+ assert Timedelta(timedelta(seconds=1)) == np.timedelta64(1, "s").astype(
461
+ "m8[ns]"
462
+ )
463
+ assert Timedelta(timedelta(microseconds=1)) == np.timedelta64(1, "us").astype(
464
+ "m8[ns]"
465
+ )
466
+ assert Timedelta(timedelta(days=1)) == np.timedelta64(1, "D").astype("m8[ns]")
467
+
468
+ def test_to_numpy_alias(self):
469
+ # GH 24653: alias .to_numpy() for scalars
470
+ td = Timedelta("10m7s")
471
+ assert td.to_timedelta64() == td.to_numpy()
472
+
473
+ # GH#44460
474
+ msg = "dtype and copy arguments are ignored"
475
+ with pytest.raises(ValueError, match=msg):
476
+ td.to_numpy("m8[s]")
477
+ with pytest.raises(ValueError, match=msg):
478
+ td.to_numpy(copy=True)
479
+
480
+ def test_identity(self):
481
+ td = Timedelta(10, unit="D")
482
+ assert isinstance(td, Timedelta)
483
+ assert isinstance(td, timedelta)
484
+
485
+ def test_short_format_converters(self):
486
+ def conv(v):
487
+ return v.astype("m8[ns]")
488
+
489
+ assert Timedelta("10") == np.timedelta64(10, "ns")
490
+ assert Timedelta("10ns") == np.timedelta64(10, "ns")
491
+ assert Timedelta("100") == np.timedelta64(100, "ns")
492
+ assert Timedelta("100ns") == np.timedelta64(100, "ns")
493
+
494
+ assert Timedelta("1000") == np.timedelta64(1000, "ns")
495
+ assert Timedelta("1000ns") == np.timedelta64(1000, "ns")
496
+
497
+ msg = "'NS' is deprecated and will be removed in a future version."
498
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
499
+ assert Timedelta("1000NS") == np.timedelta64(1000, "ns")
500
+
501
+ assert Timedelta("10us") == np.timedelta64(10000, "ns")
502
+ assert Timedelta("100us") == np.timedelta64(100000, "ns")
503
+ assert Timedelta("1000us") == np.timedelta64(1000000, "ns")
504
+ assert Timedelta("1000Us") == np.timedelta64(1000000, "ns")
505
+ assert Timedelta("1000uS") == np.timedelta64(1000000, "ns")
506
+
507
+ assert Timedelta("1ms") == np.timedelta64(1000000, "ns")
508
+ assert Timedelta("10ms") == np.timedelta64(10000000, "ns")
509
+ assert Timedelta("100ms") == np.timedelta64(100000000, "ns")
510
+ assert Timedelta("1000ms") == np.timedelta64(1000000000, "ns")
511
+
512
+ assert Timedelta("-1s") == -np.timedelta64(1000000000, "ns")
513
+ assert Timedelta("1s") == np.timedelta64(1000000000, "ns")
514
+ assert Timedelta("10s") == np.timedelta64(10000000000, "ns")
515
+ assert Timedelta("100s") == np.timedelta64(100000000000, "ns")
516
+ assert Timedelta("1000s") == np.timedelta64(1000000000000, "ns")
517
+
518
+ msg = "'d' is deprecated and will be removed in a future version."
519
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
520
+ assert Timedelta("1d") == conv(np.timedelta64(1, "D"))
521
+ assert Timedelta("-1D") == -conv(np.timedelta64(1, "D"))
522
+ assert Timedelta("1D") == conv(np.timedelta64(1, "D"))
523
+ assert Timedelta("10D") == conv(np.timedelta64(10, "D"))
524
+ assert Timedelta("100D") == conv(np.timedelta64(100, "D"))
525
+ assert Timedelta("1000D") == conv(np.timedelta64(1000, "D"))
526
+ assert Timedelta("10000D") == conv(np.timedelta64(10000, "D"))
527
+
528
+ # space
529
+ assert Timedelta(" 10000D ") == conv(np.timedelta64(10000, "D"))
530
+ assert Timedelta(" - 10000D ") == -conv(np.timedelta64(10000, "D"))
531
+
532
+ # invalid
533
+ msg = "invalid unit abbreviation"
534
+ with pytest.raises(ValueError, match=msg):
535
+ Timedelta("1foo")
536
+ msg = "unit abbreviation w/o a number"
537
+ with pytest.raises(ValueError, match=msg):
538
+ Timedelta("foo")
539
+
540
+ def test_full_format_converters(self):
541
+ def conv(v):
542
+ return v.astype("m8[ns]")
543
+
544
+ d1 = np.timedelta64(1, "D")
545
+
546
+ assert Timedelta("1days") == conv(d1)
547
+ assert Timedelta("1days,") == conv(d1)
548
+ assert Timedelta("- 1days,") == -conv(d1)
549
+
550
+ assert Timedelta("00:00:01") == conv(np.timedelta64(1, "s"))
551
+ assert Timedelta("06:00:01") == conv(np.timedelta64(6 * 3600 + 1, "s"))
552
+ assert Timedelta("06:00:01.0") == conv(np.timedelta64(6 * 3600 + 1, "s"))
553
+ assert Timedelta("06:00:01.01") == conv(
554
+ np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
555
+ )
556
+
557
+ assert Timedelta("- 1days, 00:00:01") == conv(-d1 + np.timedelta64(1, "s"))
558
+ assert Timedelta("1days, 06:00:01") == conv(
559
+ d1 + np.timedelta64(6 * 3600 + 1, "s")
560
+ )
561
+ assert Timedelta("1days, 06:00:01.01") == conv(
562
+ d1 + np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
563
+ )
564
+
565
+ # invalid
566
+ msg = "have leftover units"
567
+ with pytest.raises(ValueError, match=msg):
568
+ Timedelta("- 1days, 00")
569
+
570
+ def test_pickle(self, temp_file):
571
+ v = Timedelta("1 days 10:11:12.0123456")
572
+ v_p = tm.round_trip_pickle(v, temp_file)
573
+ assert v == v_p
574
+
575
+ def test_timedelta_hash_equality(self):
576
+ # GH 11129
577
+ v = Timedelta(1, "D")
578
+ td = timedelta(days=1)
579
+ assert hash(v) == hash(td)
580
+
581
+ d = {td: 2}
582
+ assert d[v] == 2
583
+
584
+ tds = [Timedelta(seconds=1) + Timedelta(days=n) for n in range(20)]
585
+ assert all(hash(td) == hash(td.to_pytimedelta()) for td in tds)
586
+
587
+ # python timedeltas drop ns resolution
588
+ ns_td = Timedelta(1, "ns")
589
+ assert hash(ns_td) != hash(ns_td.to_pytimedelta())
590
+
591
+ @pytest.mark.slow
592
+ @pytest.mark.xfail(
593
+ reason="pd.Timedelta violates the Python hash invariant (GH#44504).",
594
+ )
595
+ @given(
596
+ st.integers(
597
+ min_value=(-sys.maxsize - 1) // 500,
598
+ max_value=sys.maxsize // 500,
599
+ )
600
+ )
601
+ def test_hash_equality_invariance(self, half_microseconds: int) -> None:
602
+ # GH#44504
603
+
604
+ nanoseconds = half_microseconds * 500
605
+
606
+ pandas_timedelta = Timedelta(nanoseconds)
607
+ numpy_timedelta = np.timedelta64(nanoseconds)
608
+
609
+ # See: https://docs.python.org/3/glossary.html#term-hashable
610
+ # Hashable objects which compare equal must have the same hash value.
611
+ assert pandas_timedelta != numpy_timedelta or hash(pandas_timedelta) == hash(
612
+ numpy_timedelta
613
+ )
614
+
615
+ def test_implementation_limits(self):
616
+ min_td = Timedelta(Timedelta.min)
617
+ max_td = Timedelta(Timedelta.max)
618
+
619
+ # GH 12727
620
+ # timedelta limits correspond to int64 boundaries
621
+ assert min_td._value == iNaT + 1
622
+ assert max_td._value == lib.i8max
623
+
624
+ # Beyond lower limit, a NAT before the Overflow
625
+ assert (min_td - Timedelta(1, "ns")) is NaT
626
+
627
+ msg = "int too (large|big) to convert"
628
+ with pytest.raises(OverflowError, match=msg):
629
+ min_td - Timedelta(2, "ns")
630
+
631
+ with pytest.raises(OverflowError, match=msg):
632
+ max_td + Timedelta(1, "ns")
633
+
634
+ # Same tests using the internal nanosecond values
635
+ td = Timedelta(min_td._value - 1, "ns")
636
+ assert td is NaT
637
+
638
+ msg = "Cannot cast -9223372036854775809 from ns to 'ns' without overflow"
639
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
640
+ Timedelta(min_td._value - 2, "ns")
641
+
642
+ msg = "Cannot cast 9223372036854775808 from ns to 'ns' without overflow"
643
+ with pytest.raises(OutOfBoundsTimedelta, match=msg):
644
+ Timedelta(max_td._value + 1, "ns")
645
+
646
+ def test_total_seconds_precision(self):
647
+ # GH 19458
648
+ assert Timedelta("30s").total_seconds() == 30.0
649
+ assert Timedelta("0").total_seconds() == 0.0
650
+ assert Timedelta("-2s").total_seconds() == -2.0
651
+ assert Timedelta("5.324s").total_seconds() == 5.324
652
+ assert (Timedelta("30s").total_seconds() - 30.0) < 1e-20
653
+ assert (30.0 - Timedelta("30s").total_seconds()) < 1e-20
654
+
655
+ def test_resolution_string(self):
656
+ assert Timedelta(days=1).resolution_string == "D"
657
+ assert Timedelta(days=1, hours=6).resolution_string == "h"
658
+ assert Timedelta(days=1, minutes=6).resolution_string == "min"
659
+ assert Timedelta(days=1, seconds=6).resolution_string == "s"
660
+ assert Timedelta(days=1, milliseconds=6).resolution_string == "ms"
661
+ assert Timedelta(days=1, microseconds=6).resolution_string == "us"
662
+ assert Timedelta(days=1, nanoseconds=6).resolution_string == "ns"
663
+
664
+ def test_resolution_deprecated(self):
665
+ # GH#21344
666
+ td = Timedelta(days=4, hours=3)
667
+ result = td.resolution
668
+ assert result == Timedelta(microseconds=1)
669
+
670
+ # Check that the attribute is available on the class, mirroring
671
+ # the stdlib timedelta behavior
672
+ result = Timedelta.resolution
673
+ assert result == Timedelta(nanoseconds=1)
674
+
675
+ @pytest.mark.parametrize(
676
+ "unit,unit_depr",
677
+ [
678
+ ("W", "w"),
679
+ ("D", "d"),
680
+ ("min", "MIN"),
681
+ ("s", "S"),
682
+ ("h", "H"),
683
+ ("ms", "MS"),
684
+ ("us", "US"),
685
+ ],
686
+ )
687
+ def test_unit_deprecated(self, unit, unit_depr):
688
+ # GH#59051
689
+ msg = f"'{unit_depr}' is deprecated and will be removed in a future version."
690
+
691
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
692
+ result = Timedelta(1, unit_depr)
693
+ assert result == Timedelta(1, unit)
694
+
695
+
696
+ @pytest.mark.parametrize(
697
+ "value, expected",
698
+ [
699
+ (Timedelta("10s"), True),
700
+ (Timedelta("-10s"), True),
701
+ (Timedelta(10, unit="ns"), True),
702
+ (Timedelta(0, unit="ns"), False),
703
+ (Timedelta(-10, unit="ns"), True),
704
+ (Timedelta(None), True),
705
+ (NaT, True),
706
+ ],
707
+ )
708
+ def test_truthiness(value, expected):
709
+ # https://github.com/pandas-dev/pandas/issues/21484
710
+ assert bool(value) is expected
711
+
712
+
713
+ def test_timedelta_attribute_precision():
714
+ # GH 31354
715
+ td = Timedelta(1552211999999999872, unit="ns")
716
+ result = td.days * 86400
717
+ result += td.seconds
718
+ result *= 1000000
719
+ result += td.microseconds
720
+ result *= 1000
721
+ result += td.nanoseconds
722
+ expected = td._value
723
+ assert result == expected
724
+
725
+
726
+ def test_to_pytimedelta_large_values():
727
+ td = Timedelta(1152921504609987375, unit="ns")
728
+ result = td.to_pytimedelta()
729
+ expected = timedelta(days=13343, seconds=86304, microseconds=609987)
730
+ assert result == expected
731
+
732
+
733
+ def test_timedelta_week_suffix():
734
+ # GH#12691 ensure 'W' suffix works as a string passed to Timedelta
735
+ expected = Timedelta("7 days")
736
+ result = Timedelta(1, unit="W")
737
+ assert result == expected
738
+
739
+ result = Timedelta("1W")
740
+ assert result == expected
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
4
+ from pandas.errors import OutOfBoundsDatetime
5
+
6
+ from pandas import Timestamp
7
+
8
+
9
+ class TestTimestampAsUnit:
10
+ def test_as_unit(self):
11
+ ts = Timestamp("1970-01-01").as_unit("ns")
12
+ assert ts.unit == "ns"
13
+
14
+ assert ts.as_unit("ns") is ts
15
+
16
+ res = ts.as_unit("us")
17
+ assert res._value == ts._value // 1000
18
+ assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
19
+
20
+ rt = res.as_unit("ns")
21
+ assert rt._value == ts._value
22
+ assert rt._creso == ts._creso
23
+
24
+ res = ts.as_unit("ms")
25
+ assert res._value == ts._value // 1_000_000
26
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
27
+
28
+ rt = res.as_unit("ns")
29
+ assert rt._value == ts._value
30
+ assert rt._creso == ts._creso
31
+
32
+ res = ts.as_unit("s")
33
+ assert res._value == ts._value // 1_000_000_000
34
+ assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
35
+
36
+ rt = res.as_unit("ns")
37
+ assert rt._value == ts._value
38
+ assert rt._creso == ts._creso
39
+
40
+ def test_as_unit_overflows(self):
41
+ # microsecond that would be just out of bounds for nano
42
+ us = 9223372800000000
43
+ ts = Timestamp._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value, None)
44
+
45
+ msg = "Cannot cast 2262-04-12 00:00:00 to unit='ns' without overflow"
46
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
47
+ ts.as_unit("ns")
48
+
49
+ res = ts.as_unit("ms")
50
+ assert res._value == us // 1000
51
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
52
+
53
+ def test_as_unit_rounding(self):
54
+ ts = Timestamp(1_500_000) # i.e. 1500 microseconds
55
+ res = ts.as_unit("ms")
56
+
57
+ expected = Timestamp(1_000_000) # i.e. 1 millisecond
58
+ assert res == expected
59
+
60
+ assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
61
+ assert res._value == 1
62
+
63
+ with pytest.raises(ValueError, match="Cannot losslessly convert units"):
64
+ ts.as_unit("ms", round_ok=False)
65
+
66
+ def test_as_unit_non_nano(self):
67
+ # case where we are going neither to nor from nano
68
+ ts = Timestamp("1970-01-02").as_unit("ms")
69
+ assert ts.year == 1970
70
+ assert ts.month == 1
71
+ assert ts.day == 2
72
+ assert ts.hour == ts.minute == ts.second == ts.microsecond == ts.nanosecond == 0
73
+
74
+ res = ts.as_unit("s")
75
+ assert res._value == 24 * 3600
76
+ assert res.year == 1970
77
+ assert res.month == 1
78
+ assert res.day == 2
79
+ assert (
80
+ res.hour
81
+ == res.minute
82
+ == res.second
83
+ == res.microsecond
84
+ == res.nanosecond
85
+ == 0
86
+ )
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas._libs.tslibs import Timestamp
4
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
5
+
6
+
7
+ class TestTimestampNormalize:
8
+ @pytest.mark.parametrize("arg", ["2013-11-30", "2013-11-30 12:00:00"])
9
+ def test_normalize(self, tz_naive_fixture, arg, unit):
10
+ tz = tz_naive_fixture
11
+ ts = Timestamp(arg, tz=tz).as_unit(unit)
12
+ result = ts.normalize()
13
+ expected = Timestamp("2013-11-30", tz=tz)
14
+ assert result == expected
15
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
16
+
17
+ def test_normalize_pre_epoch_dates(self):
18
+ # GH: 36294
19
+ result = Timestamp("1969-01-01 09:00:00").normalize()
20
+ expected = Timestamp("1969-01-01 00:00:00")
21
+ assert result == expected
22
+
23
+ def test_normalize_overflow_raises(self):
24
+ # GH#60583
25
+ ts = Timestamp.min
26
+
27
+ msg = "Cannot normalize Timestamp without integer overflow"
28
+ with pytest.raises(ValueError, match=msg):
29
+ ts.normalize()
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import zoneinfo
3
+
4
+ from dateutil.tz import gettz
5
+ import numpy as np
6
+ import pytest
7
+
8
+ from pandas._libs.tslibs import (
9
+ OutOfBoundsDatetime,
10
+ Timestamp,
11
+ conversion,
12
+ )
13
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
14
+ from pandas.compat import WASM
15
+ import pandas.util._test_decorators as td
16
+
17
+ import pandas._testing as tm
18
+
19
+
20
+ class TestTimestampReplace:
21
+ def test_replace_out_of_pydatetime_bounds(self):
22
+ # GH#50348
23
+ ts = Timestamp("2016-01-01").as_unit("ns")
24
+
25
+ msg = "Out of bounds timestamp: 99999-01-01 00:00:00 with frequency 'ns'"
26
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
27
+ ts.replace(year=99_999)
28
+
29
+ ts = ts.as_unit("ms")
30
+ result = ts.replace(year=99_999)
31
+ assert result.year == 99_999
32
+ assert result._value == Timestamp(np.datetime64("99999-01-01", "ms"))._value
33
+
34
+ def test_replace_non_nano(self):
35
+ ts = Timestamp._from_value_and_reso(
36
+ 91514880000000000, NpyDatetimeUnit.NPY_FR_us.value, None
37
+ )
38
+ assert ts.to_pydatetime() == datetime(4869, 12, 28)
39
+
40
+ result = ts.replace(year=4900)
41
+ assert result._creso == ts._creso
42
+ assert result.to_pydatetime() == datetime(4900, 12, 28)
43
+
44
+ def test_replace_naive(self):
45
+ # GH#14621, GH#7825
46
+ ts = Timestamp("2016-01-01 09:00:00")
47
+ result = ts.replace(hour=0)
48
+ expected = Timestamp("2016-01-01 00:00:00")
49
+ assert result == expected
50
+
51
+ def test_replace_aware(self, tz_aware_fixture):
52
+ tz = tz_aware_fixture
53
+ # GH#14621, GH#7825
54
+ # replacing datetime components with and w/o presence of a timezone
55
+ ts = Timestamp("2016-01-01 09:00:00", tz=tz)
56
+ result = ts.replace(hour=0)
57
+ expected = Timestamp("2016-01-01 00:00:00", tz=tz)
58
+ assert result == expected
59
+
60
+ def test_replace_preserves_nanos(self, tz_aware_fixture):
61
+ tz = tz_aware_fixture
62
+ # GH#14621, GH#7825
63
+ ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
64
+ result = ts.replace(hour=0)
65
+ expected = Timestamp("2016-01-01 00:00:00.000000123", tz=tz)
66
+ assert result == expected
67
+
68
+ def test_replace_multiple(self, tz_aware_fixture):
69
+ tz = tz_aware_fixture
70
+ # GH#14621, GH#7825
71
+ # replacing datetime components with and w/o presence of a timezone
72
+ # test all
73
+ ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
74
+ result = ts.replace(
75
+ year=2015,
76
+ month=2,
77
+ day=2,
78
+ hour=0,
79
+ minute=5,
80
+ second=5,
81
+ microsecond=5,
82
+ nanosecond=5,
83
+ )
84
+ expected = Timestamp("2015-02-02 00:05:05.000005005", tz=tz)
85
+ assert result == expected
86
+
87
+ def test_replace_invalid_kwarg(self, tz_aware_fixture):
88
+ tz = tz_aware_fixture
89
+ # GH#14621, GH#7825
90
+ ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
91
+ msg = r"replace\(\) got an unexpected keyword argument"
92
+ with pytest.raises(TypeError, match=msg):
93
+ ts.replace(foo=5)
94
+
95
+ def test_replace_integer_args(self, tz_aware_fixture):
96
+ tz = tz_aware_fixture
97
+ # GH#14621, GH#7825
98
+ ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
99
+ msg = "value must be an integer, received <class 'float'> for hour"
100
+ with pytest.raises(ValueError, match=msg):
101
+ ts.replace(hour=0.1)
102
+
103
+ @pytest.mark.skipif(WASM, reason="tzset is not available on WASM")
104
+ def test_replace_tzinfo_equiv_tz_localize_none(self):
105
+ # GH#14621, GH#7825
106
+ # assert conversion to naive is the same as replacing tzinfo with None
107
+ ts = Timestamp("2013-11-03 01:59:59.999999-0400", tz="US/Eastern")
108
+ assert ts.tz_localize(None) == ts.replace(tzinfo=None)
109
+
110
+ @td.skip_if_windows
111
+ @pytest.mark.skipif(WASM, reason="tzset is not available on WASM")
112
+ def test_replace_tzinfo(self):
113
+ # GH#15683
114
+ dt = datetime(2016, 3, 27, 1, fold=1)
115
+ tzinfo = dt.astimezone(zoneinfo.ZoneInfo("Europe/Berlin")).tzinfo
116
+
117
+ result_dt = dt.replace(tzinfo=tzinfo)
118
+ result_pd = Timestamp(dt).replace(tzinfo=tzinfo)
119
+
120
+ # datetime.timestamp() converts in the local timezone
121
+ with tm.set_timezone("UTC"):
122
+ assert result_dt.timestamp() == result_pd.timestamp()
123
+
124
+ assert result_dt == result_pd
125
+ assert result_dt == result_pd.to_pydatetime()
126
+
127
+ result_dt = dt.replace(tzinfo=tzinfo).replace(tzinfo=None)
128
+ result_pd = Timestamp(dt).replace(tzinfo=tzinfo).replace(tzinfo=None)
129
+
130
+ # datetime.timestamp() converts in the local timezone
131
+ with tm.set_timezone("UTC"):
132
+ assert result_dt.timestamp() == result_pd.timestamp()
133
+
134
+ assert result_dt == result_pd
135
+ assert result_dt == result_pd.to_pydatetime()
136
+
137
+ @pytest.mark.parametrize(
138
+ "tz, normalize",
139
+ [
140
+ ("pytz/US/Eastern", lambda x: x.tzinfo.normalize(x)),
141
+ (gettz("US/Eastern"), lambda x: x),
142
+ ],
143
+ )
144
+ def test_replace_across_dst(self, tz, normalize):
145
+ # GH#18319 check that 1) timezone is correctly normalized and
146
+ # 2) that hour is not incorrectly changed by this normalization
147
+ if isinstance(tz, str) and tz.startswith("pytz/"):
148
+ pytz = pytest.importorskip("pytz")
149
+ tz = pytz.timezone(tz.removeprefix("pytz/"))
150
+ ts_naive = Timestamp("2017-12-03 16:03:30")
151
+ ts_aware = conversion.localize_pydatetime(ts_naive, tz)
152
+
153
+ # Preliminary sanity-check
154
+ assert ts_aware == normalize(ts_aware)
155
+
156
+ # Replace across DST boundary
157
+ ts2 = ts_aware.replace(month=6)
158
+
159
+ # Check that `replace` preserves hour literal
160
+ assert (ts2.hour, ts2.minute) == (ts_aware.hour, ts_aware.minute)
161
+
162
+ # Check that post-replace object is appropriately normalized
163
+ ts2b = normalize(ts2)
164
+ assert ts2 == ts2b
165
+
166
+ def test_replace_dst_border(self, unit):
167
+ # Gh 7825
168
+ t = Timestamp("2013-11-3", tz="America/Chicago").as_unit(unit)
169
+ result = t.replace(hour=3)
170
+ expected = Timestamp("2013-11-3 03:00:00", tz="America/Chicago")
171
+ assert result == expected
172
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
173
+
174
+ @pytest.mark.parametrize("fold", [0, 1])
175
+ @pytest.mark.parametrize("tz", ["dateutil/Europe/London", "Europe/London"])
176
+ def test_replace_dst_fold(self, fold, tz, unit):
177
+ # GH 25017
178
+ d = datetime(2019, 10, 27, 2, 30)
179
+ ts = Timestamp(d, tz=tz).as_unit(unit)
180
+ result = ts.replace(hour=1, fold=fold)
181
+ expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize(
182
+ tz, ambiguous=not fold
183
+ )
184
+ assert result == expected
185
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
186
+
187
+ @pytest.mark.parametrize("fold", [0, 1])
188
+ def test_replace_preserves_fold(self, fold):
189
+ # GH#37610. Check that replace preserves Timestamp fold property
190
+ tz = gettz("Europe/Moscow")
191
+
192
+ ts = Timestamp(
193
+ year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tzinfo=tz
194
+ )
195
+ ts_replaced = ts.replace(second=1)
196
+
197
+ assert ts_replaced.fold == fold
198
+
199
+ def test_replace_updates_unit(self):
200
+ # GH#57749
201
+ ts = Timestamp("2023-07-15 23:08:12.134567123")
202
+ ts2 = Timestamp("2023-07-15 23:08:12.000000")
203
+ assert ts2.unit == "us"
204
+ result = ts2.replace(microsecond=ts.microsecond, nanosecond=ts.nanosecond)
205
+ assert result.unit == "ns"
206
+ assert result == ts
207
+
208
+ ts3 = Timestamp("2023-07-15 23:08:12").as_unit("s")
209
+ result = ts3.replace(microsecond=ts2.microsecond)
210
+ assert result.unit == "us"
211
+ assert result == ts2
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from hypothesis import (
2
+ given,
3
+ strategies as st,
4
+ )
5
+ import numpy as np
6
+ import pytest
7
+
8
+ from pandas._libs import lib
9
+ from pandas._libs.tslibs import (
10
+ NaT,
11
+ OutOfBoundsDatetime,
12
+ Timedelta,
13
+ Timestamp,
14
+ iNaT,
15
+ to_offset,
16
+ )
17
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
18
+ from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
19
+
20
+ import pandas._testing as tm
21
+
22
+
23
+ class TestTimestampRound:
24
+ def test_round_division_by_zero_raises(self):
25
+ ts = Timestamp("2016-01-01")
26
+
27
+ msg = "Division by zero in rounding"
28
+ with pytest.raises(ValueError, match=msg):
29
+ ts.round("0ns")
30
+
31
+ @pytest.mark.parametrize(
32
+ "timestamp, freq, expected",
33
+ [
34
+ ("20130101 09:10:11", "D", "20130101"),
35
+ ("20130101 19:10:11", "D", "20130102"),
36
+ ("20130201 12:00:00", "D", "20130202"),
37
+ ("20130104 12:00:00", "D", "20130105"),
38
+ ("2000-01-05 05:09:15.13", "D", "2000-01-05 00:00:00"),
39
+ ("2000-01-05 05:09:15.13", "h", "2000-01-05 05:00:00"),
40
+ ("2000-01-05 05:09:15.13", "s", "2000-01-05 05:09:15"),
41
+ ],
42
+ )
43
+ def test_round_frequencies(self, timestamp, freq, expected):
44
+ dt = Timestamp(timestamp)
45
+ result = dt.round(freq)
46
+ expected = Timestamp(expected)
47
+ assert result == expected
48
+
49
+ def test_round_tzaware(self):
50
+ dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
51
+ result = dt.round("D")
52
+ expected = Timestamp("20130101", tz="US/Eastern")
53
+ assert result == expected
54
+
55
+ dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
56
+ result = dt.round("s")
57
+ assert result == dt
58
+
59
+ def test_round_30min(self):
60
+ # round
61
+ dt = Timestamp("20130104 12:32:00")
62
+ result = dt.round("30Min")
63
+ expected = Timestamp("20130104 12:30:00")
64
+ assert result == expected
65
+
66
+ def test_round_subsecond(self):
67
+ # GH#14440 & GH#15578
68
+ result = Timestamp("2016-10-17 12:00:00.0015").round("ms")
69
+ expected = Timestamp("2016-10-17 12:00:00.002000")
70
+ assert result == expected
71
+
72
+ result = Timestamp("2016-10-17 12:00:00.00149").round("ms")
73
+ expected = Timestamp("2016-10-17 12:00:00.001000")
74
+ assert result == expected
75
+
76
+ ts = Timestamp("2016-10-17 12:00:00.0015")
77
+ for freq in ["us", "ns"]:
78
+ assert ts == ts.round(freq)
79
+
80
+ result = Timestamp("2016-10-17 12:00:00.001501031").round("10ns")
81
+ expected = Timestamp("2016-10-17 12:00:00.001501030")
82
+ assert result == expected
83
+
84
+ def test_round_nonstandard_freq(self):
85
+ with tm.assert_produces_warning(False):
86
+ Timestamp("2016-10-17 12:00:00.001501031").round("1010ns")
87
+
88
+ def test_round_invalid_arg(self):
89
+ stamp = Timestamp("2000-01-05 05:09:15.13")
90
+ with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
91
+ stamp.round("foo")
92
+
93
+ @pytest.mark.parametrize(
94
+ "test_input, rounder, freq, expected",
95
+ [
96
+ ("2117-01-01 00:00:45", "floor", "15s", "2117-01-01 00:00:45"),
97
+ ("2117-01-01 00:00:45", "ceil", "15s", "2117-01-01 00:00:45"),
98
+ (
99
+ "2117-01-01 00:00:45.000000012",
100
+ "floor",
101
+ "10ns",
102
+ "2117-01-01 00:00:45.000000010",
103
+ ),
104
+ (
105
+ "1823-01-01 00:00:01.000000012",
106
+ "ceil",
107
+ "10ns",
108
+ "1823-01-01 00:00:01.000000020",
109
+ ),
110
+ ("1823-01-01 00:00:01", "floor", "1s", "1823-01-01 00:00:01"),
111
+ ("1823-01-01 00:00:01", "ceil", "1s", "1823-01-01 00:00:01"),
112
+ ("NaT", "floor", "1s", "NaT"),
113
+ ("NaT", "ceil", "1s", "NaT"),
114
+ ],
115
+ )
116
+ def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
117
+ dt = Timestamp(test_input)
118
+ func = getattr(dt, rounder)
119
+ result = func(freq)
120
+
121
+ if dt is NaT:
122
+ assert result is NaT
123
+ else:
124
+ expected = Timestamp(expected)
125
+ assert result == expected
126
+
127
+ @pytest.mark.parametrize(
128
+ "test_input, freq, expected",
129
+ [
130
+ ("2018-01-01 00:02:06", "2s", "2018-01-01 00:02:06"),
131
+ ("2018-01-01 00:02:00", "2min", "2018-01-01 00:02:00"),
132
+ ("2018-01-01 00:04:00", "4min", "2018-01-01 00:04:00"),
133
+ ("2018-01-01 00:15:00", "15min", "2018-01-01 00:15:00"),
134
+ ("2018-01-01 00:20:00", "20min", "2018-01-01 00:20:00"),
135
+ ("2018-01-01 03:00:00", "3h", "2018-01-01 03:00:00"),
136
+ ],
137
+ )
138
+ @pytest.mark.parametrize("rounder", ["ceil", "floor", "round"])
139
+ def test_round_minute_freq(self, test_input, freq, expected, rounder):
140
+ # Ensure timestamps that shouldn't round dont!
141
+ # GH#21262
142
+
143
+ dt = Timestamp(test_input)
144
+ expected = Timestamp(expected)
145
+ func = getattr(dt, rounder)
146
+ result = func(freq)
147
+ assert result == expected
148
+
149
+ def test_ceil(self, unit):
150
+ dt = Timestamp("20130101 09:10:11").as_unit(unit)
151
+ result = dt.ceil("D")
152
+ expected = Timestamp("20130102")
153
+ assert result == expected
154
+ assert result._creso == dt._creso
155
+
156
+ def test_floor(self, unit):
157
+ dt = Timestamp("20130101 09:10:11").as_unit(unit)
158
+ result = dt.floor("D")
159
+ expected = Timestamp("20130101")
160
+ assert result == expected
161
+ assert result._creso == dt._creso
162
+
163
+ @pytest.mark.parametrize("method", ["ceil", "round", "floor"])
164
+ def test_round_dst_border_ambiguous(self, method, unit):
165
+ # GH 18946 round near "fall back" DST
166
+ ts = Timestamp("2017-10-29 00:00:00", tz="UTC").tz_convert("Europe/Madrid")
167
+ ts = ts.as_unit(unit)
168
+ result = getattr(ts, method)("h", ambiguous=True)
169
+ assert result == ts
170
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
171
+
172
+ result = getattr(ts, method)("h", ambiguous=False)
173
+ expected = Timestamp("2017-10-29 01:00:00", tz="UTC").tz_convert(
174
+ "Europe/Madrid"
175
+ )
176
+ assert result == expected
177
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
178
+
179
+ result = getattr(ts, method)("h", ambiguous="NaT")
180
+ assert result is NaT
181
+
182
+ msg = "Cannot infer dst time"
183
+ with pytest.raises(ValueError, match=msg):
184
+ getattr(ts, method)("h", ambiguous="raise")
185
+
186
+ @pytest.mark.parametrize(
187
+ "method, ts_str, freq",
188
+ [
189
+ ["ceil", "2018-03-11 01:59:00-0600", "5min"],
190
+ ["round", "2018-03-11 01:59:00-0600", "5min"],
191
+ ["floor", "2018-03-11 03:01:00-0500", "2h"],
192
+ ],
193
+ )
194
+ def test_round_dst_border_nonexistent(self, method, ts_str, freq, unit):
195
+ # GH 23324 round near "spring forward" DST
196
+ ts = Timestamp(ts_str, tz="America/Chicago").as_unit(unit)
197
+ result = getattr(ts, method)(freq, nonexistent="shift_forward")
198
+ expected = Timestamp("2018-03-11 03:00:00", tz="America/Chicago")
199
+ assert result == expected
200
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
201
+
202
+ result = getattr(ts, method)(freq, nonexistent="NaT")
203
+ assert result is NaT
204
+
205
+ msg = "2018-03-11 02:00:00"
206
+ with pytest.raises(ValueError, match=msg):
207
+ getattr(ts, method)(freq, nonexistent="raise")
208
+
209
+ @pytest.mark.parametrize(
210
+ "timestamp",
211
+ [
212
+ "2018-01-01 0:0:0.124999360",
213
+ "2018-01-01 0:0:0.125000367",
214
+ "2018-01-01 0:0:0.125500",
215
+ "2018-01-01 0:0:0.126500",
216
+ "2018-01-01 12:00:00",
217
+ "2019-01-01 12:00:00",
218
+ ],
219
+ )
220
+ @pytest.mark.parametrize(
221
+ "freq",
222
+ [
223
+ "2ns",
224
+ "3ns",
225
+ "4ns",
226
+ "5ns",
227
+ "6ns",
228
+ "7ns",
229
+ "250ns",
230
+ "500ns",
231
+ "750ns",
232
+ "1us",
233
+ "19us",
234
+ "250us",
235
+ "500us",
236
+ "750us",
237
+ "1s",
238
+ "2s",
239
+ "3s",
240
+ "1D",
241
+ ],
242
+ )
243
+ def test_round_int64(self, timestamp, freq):
244
+ # check that all rounding modes are accurate to int64 precision
245
+ # see GH#22591
246
+ dt = Timestamp(timestamp).as_unit("ns")
247
+ unit = to_offset(freq).nanos
248
+
249
+ # test floor
250
+ result = dt.floor(freq)
251
+ assert result._value % unit == 0, f"floor not a {freq} multiple"
252
+ assert 0 <= dt._value - result._value < unit, "floor error"
253
+
254
+ # test ceil
255
+ result = dt.ceil(freq)
256
+ assert result._value % unit == 0, f"ceil not a {freq} multiple"
257
+ assert 0 <= result._value - dt._value < unit, "ceil error"
258
+
259
+ # test round
260
+ result = dt.round(freq)
261
+ assert result._value % unit == 0, f"round not a {freq} multiple"
262
+ assert abs(result._value - dt._value) <= unit // 2, "round error"
263
+ if unit % 2 == 0 and abs(result._value - dt._value) == unit // 2:
264
+ # round half to even
265
+ assert result._value // unit % 2 == 0, "round half to even error"
266
+
267
+ def test_round_implementation_bounds(self):
268
+ # See also: analogous test for Timedelta
269
+ result = Timestamp.min.ceil("s")
270
+ expected = Timestamp(1677, 9, 21, 0, 12, 44)
271
+ assert result == expected
272
+
273
+ result = Timestamp.max.floor("s")
274
+ expected = Timestamp.max - Timedelta(854775807)
275
+ assert result == expected
276
+
277
+ msg = "Cannot round 1677-09-21 00:12:43.145224193 to freq=<Second>"
278
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
279
+ Timestamp.min.floor("s")
280
+
281
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
282
+ Timestamp.min.round("s")
283
+
284
+ msg = "Cannot round 2262-04-11 23:47:16.854775807 to freq=<Second>"
285
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
286
+ Timestamp.max.ceil("s")
287
+
288
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
289
+ Timestamp.max.round("s")
290
+
291
+ @pytest.mark.slow
292
+ @given(val=st.integers(iNaT + 1, lib.i8max))
293
+ @pytest.mark.parametrize(
294
+ "method", [Timestamp.round, Timestamp.floor, Timestamp.ceil]
295
+ )
296
+ def test_round_sanity(self, val, method):
297
+ cls = Timestamp
298
+ err_cls = OutOfBoundsDatetime
299
+
300
+ val = np.int64(val)
301
+ ts = cls(val)
302
+
303
+ def checker(ts, nanos, unit):
304
+ # First check that we do raise in cases where we should
305
+ if nanos == 1:
306
+ pass
307
+ else:
308
+ div, mod = divmod(ts._value, nanos)
309
+ diff = int(nanos - mod)
310
+ lb = ts._value - mod
311
+ assert lb <= ts._value # i.e. no overflows with python ints
312
+ ub = ts._value + diff
313
+ assert ub > ts._value # i.e. no overflows with python ints
314
+
315
+ msg = "without overflow"
316
+ if mod == 0:
317
+ # We should never be raising in this
318
+ pass
319
+ elif method is cls.ceil:
320
+ if ub > cls.max._value:
321
+ with pytest.raises(err_cls, match=msg):
322
+ method(ts, unit)
323
+ return
324
+ elif method is cls.floor:
325
+ if lb < cls.min._value:
326
+ with pytest.raises(err_cls, match=msg):
327
+ method(ts, unit)
328
+ return
329
+ elif mod >= diff:
330
+ if ub > cls.max._value:
331
+ with pytest.raises(err_cls, match=msg):
332
+ method(ts, unit)
333
+ return
334
+ elif lb < cls.min._value:
335
+ with pytest.raises(err_cls, match=msg):
336
+ method(ts, unit)
337
+ return
338
+
339
+ res = method(ts, unit)
340
+
341
+ td = res - ts
342
+ diff = abs(td._value)
343
+ assert diff < nanos
344
+ assert res._value % nanos == 0
345
+
346
+ if method is cls.round:
347
+ assert diff <= nanos / 2
348
+ elif method is cls.floor:
349
+ assert res <= ts
350
+ elif method is cls.ceil:
351
+ assert res >= ts
352
+
353
+ nanos = 1
354
+ checker(ts, nanos, "ns")
355
+
356
+ nanos = 1000
357
+ checker(ts, nanos, "us")
358
+
359
+ nanos = 1_000_000
360
+ checker(ts, nanos, "ms")
361
+
362
+ nanos = 1_000_000_000
363
+ checker(ts, nanos, "s")
364
+
365
+ nanos = 60 * 1_000_000_000
366
+ checker(ts, nanos, "min")
367
+
368
+ nanos = 60 * 60 * 1_000_000_000
369
+ checker(ts, nanos, "h")
370
+
371
+ nanos = 24 * 60 * 60 * 1_000_000_000
372
+ checker(ts, nanos, "D")
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NB: This is for the Timestamp.timestamp *method* specifically, not
2
+ # the Timestamp class in general.
3
+ from datetime import timezone
4
+
5
+ import pytest
6
+
7
+ from pandas._libs.tslibs import Timestamp
8
+ from pandas.compat import WASM
9
+ import pandas.util._test_decorators as td
10
+
11
+ import pandas._testing as tm
12
+
13
+
14
+ class TestTimestampMethod:
15
+ @td.skip_if_windows
16
+ @pytest.mark.skipif(WASM, reason="tzset is not available on WASM")
17
+ def test_timestamp(self, fixed_now_ts):
18
+ # GH#17329
19
+ # tz-naive --> treat it as if it were UTC for purposes of timestamp()
20
+ ts = fixed_now_ts
21
+ uts = ts.replace(tzinfo=timezone.utc)
22
+ assert ts.timestamp() == uts.timestamp()
23
+
24
+ tsc = Timestamp("2014-10-11 11:00:01.12345678", tz="US/Central")
25
+ utsc = tsc.tz_convert("UTC")
26
+
27
+ # utsc is a different representation of the same time
28
+ assert tsc.timestamp() == utsc.timestamp()
29
+
30
+ # datetime.timestamp() converts in the local timezone
31
+ with tm.set_timezone("UTC"):
32
+ # should agree with datetime.timestamp method
33
+ dt = ts.to_pydatetime()
34
+ assert dt.timestamp() == ts.timestamp()
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pandas import Timestamp
2
+
3
+
4
+ class TestTimestampToJulianDate:
5
+ def test_compare_1700(self):
6
+ ts = Timestamp("1700-06-23")
7
+ res = ts.to_julian_date()
8
+ assert res == 2_342_145.5
9
+
10
+ def test_compare_2000(self):
11
+ ts = Timestamp("2000-04-12")
12
+ res = ts.to_julian_date()
13
+ assert res == 2_451_646.5
14
+
15
+ def test_compare_2100(self):
16
+ ts = Timestamp("2100-08-12")
17
+ res = ts.to_julian_date()
18
+ assert res == 2_488_292.5
19
+
20
+ def test_compare_hour01(self):
21
+ ts = Timestamp("2000-08-12T01:00:00")
22
+ res = ts.to_julian_date()
23
+ assert res == 2_451_768.5416666666666666
24
+
25
+ def test_compare_hour13(self):
26
+ ts = Timestamp("2000-08-12T13:00:00")
27
+ res = ts.to_julian_date()
28
+ assert res == 2_451_769.0416666666666666
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ datetime,
3
+ timedelta,
4
+ )
5
+
6
+ import pytest
7
+
8
+ from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
9
+ import pandas.util._test_decorators as td
10
+
11
+ from pandas import Timestamp
12
+ import pandas._testing as tm
13
+
14
+
15
+ class TestTimestampToPyDatetime:
16
+ def test_to_pydatetime_fold(self):
17
+ # GH#45087
18
+ tzstr = "dateutil/usr/share/zoneinfo/America/Chicago"
19
+ ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, tz=tzstr)
20
+ dt = ts.to_pydatetime()
21
+ assert dt.fold == 1
22
+
23
+ def test_to_pydatetime_nonzero_nano(self):
24
+ ts = Timestamp("2011-01-01 9:00:00.123456789")
25
+
26
+ # Warn the user of data loss (nanoseconds).
27
+ msg = "Discarding nonzero nanoseconds in conversion"
28
+ with tm.assert_produces_warning(UserWarning, match=msg):
29
+ expected = datetime(2011, 1, 1, 9, 0, 0, 123456)
30
+ result = ts.to_pydatetime()
31
+ assert result == expected
32
+
33
+ def test_timestamp_to_datetime(self):
34
+ stamp = Timestamp("20090415", tz="US/Eastern")
35
+ dtval = stamp.to_pydatetime()
36
+ assert stamp == dtval
37
+ assert stamp.tzinfo == dtval.tzinfo
38
+
39
+ def test_timestamp_to_pydatetime_dateutil(self):
40
+ stamp = Timestamp("20090415", tz="dateutil/US/Eastern")
41
+ dtval = stamp.to_pydatetime()
42
+ assert stamp == dtval
43
+ assert stamp.tzinfo == dtval.tzinfo
44
+
45
+ def test_timestamp_to_pydatetime_explicit_pytz(self):
46
+ pytz = pytest.importorskip("pytz")
47
+ stamp = Timestamp("20090415", tz=pytz.timezone("US/Eastern"))
48
+ dtval = stamp.to_pydatetime()
49
+ assert stamp == dtval
50
+ assert stamp.tzinfo == dtval.tzinfo
51
+
52
+ @td.skip_if_windows
53
+ def test_timestamp_to_pydatetime_explicit_dateutil(self):
54
+ stamp = Timestamp("20090415", tz=gettz("US/Eastern"))
55
+ dtval = stamp.to_pydatetime()
56
+ assert stamp == dtval
57
+ assert stamp.tzinfo == dtval.tzinfo
58
+
59
+ def test_to_pydatetime_bijective(self):
60
+ # Ensure that converting to datetime and back only loses precision
61
+ # by going from nanoseconds to microseconds.
62
+ exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning
63
+ with tm.assert_produces_warning(exp_warning):
64
+ pydt_max = Timestamp.max.to_pydatetime()
65
+
66
+ assert (
67
+ Timestamp(pydt_max).as_unit("ns")._value / 1000
68
+ == Timestamp.max._value / 1000
69
+ )
70
+
71
+ exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning
72
+ with tm.assert_produces_warning(exp_warning):
73
+ pydt_min = Timestamp.min.to_pydatetime()
74
+
75
+ # The next assertion can be enabled once GH#39221 is merged
76
+ # assert pydt_min < Timestamp.min # this is bc nanos are dropped
77
+ tdus = timedelta(microseconds=1)
78
+ assert pydt_min + tdus > Timestamp.min
79
+
80
+ assert (
81
+ Timestamp(pydt_min + tdus).as_unit("ns")._value / 1000
82
+ == Timestamp.min._value / 1000
83
+ )
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dateutil
2
+ import pytest
3
+
4
+ from pandas._libs.tslibs import timezones
5
+ import pandas.util._test_decorators as td
6
+
7
+ from pandas import Timestamp
8
+
9
+
10
+ class TestTimestampTZConvert:
11
+ @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
12
+ def test_astimezone(self, tzstr):
13
+ # astimezone is an alias for tz_convert, so keep it with
14
+ # the tz_convert tests
15
+ utcdate = Timestamp("3/11/2012 22:00", tz="UTC")
16
+ expected = utcdate.tz_convert(tzstr)
17
+ result = utcdate.astimezone(tzstr)
18
+ assert expected == result
19
+ assert isinstance(result, Timestamp)
20
+
21
+ @pytest.mark.parametrize(
22
+ "stamp",
23
+ [
24
+ "2014-02-01 09:00",
25
+ "2014-07-08 09:00",
26
+ "2014-11-01 17:00",
27
+ "2014-11-05 00:00",
28
+ ],
29
+ )
30
+ def test_tz_convert_roundtrip(self, stamp, tz_aware_fixture):
31
+ tz = tz_aware_fixture
32
+
33
+ ts = Timestamp(stamp, tz="UTC")
34
+ converted = ts.tz_convert(tz)
35
+
36
+ reset = converted.tz_convert(None)
37
+ assert reset == Timestamp(stamp)
38
+ assert reset.tzinfo is None
39
+ assert reset == converted.tz_convert("UTC").tz_localize(None)
40
+
41
+ @td.skip_if_windows
42
+ def test_tz_convert_utc_with_system_utc(self):
43
+ # from system utc to real utc
44
+ ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
45
+ # check that the time hasn't changed.
46
+ assert ts == ts.tz_convert(dateutil.tz.tzutc())
47
+
48
+ # from system utc to real utc
49
+ ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
50
+ # check that the time hasn't changed.
51
+ assert ts == ts.tz_convert(dateutil.tz.tzutc())
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import timedelta
2
+ import re
3
+ import zoneinfo
4
+
5
+ from dateutil.tz import gettz
6
+ import pytest
7
+
8
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
9
+ from pandas.errors import OutOfBoundsDatetime
10
+
11
+ from pandas import (
12
+ NaT,
13
+ Timestamp,
14
+ )
15
+
16
+
17
+ def _distant_date_only_for_zoneinfo(year, tz):
18
+ if int(year) >= 2200:
19
+ if isinstance(tz, zoneinfo.ZoneInfo):
20
+ return
21
+ elif isinstance(tz, str) and not tz.startswith(("dateutil/", "pytz/")):
22
+ # zoneinfo tz passed as string
23
+ return
24
+ else:
25
+ pytest.skip("ZoneInfo required for year >= 2200")
26
+
27
+
28
+ class TestTimestampTZLocalize:
29
+ def test_tz_localize_pushes_out_of_bounds(self):
30
+ # GH#12677
31
+ # tz_localize that pushes away from the boundary is OK
32
+ pytz = pytest.importorskip("pytz")
33
+ msg = (
34
+ f"Converting {Timestamp.min.strftime('%Y-%m-%d %H:%M:%S')} "
35
+ f"underflows past {Timestamp.min}"
36
+ )
37
+ pac = Timestamp.min.tz_localize(pytz.timezone("US/Pacific"))
38
+ assert pac._value > Timestamp.min._value
39
+ pac.tz_convert("Asia/Tokyo") # tz_convert doesn't change value
40
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
41
+ Timestamp.min.tz_localize(pytz.timezone("Asia/Tokyo"))
42
+
43
+ # tz_localize that pushes away from the boundary is OK
44
+ msg = (
45
+ f"Converting {Timestamp.max.strftime('%Y-%m-%d %H:%M:%S')} "
46
+ f"overflows past {Timestamp.max}"
47
+ )
48
+ tokyo = Timestamp.max.tz_localize(pytz.timezone("Asia/Tokyo"))
49
+ assert tokyo._value < Timestamp.max._value
50
+ tokyo.tz_convert("US/Pacific") # tz_convert doesn't change value
51
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
52
+ Timestamp.max.tz_localize(pytz.timezone("US/Pacific"))
53
+
54
+ @pytest.mark.parametrize(
55
+ "tz",
56
+ [zoneinfo.ZoneInfo("US/Central"), "dateutil/US/Central", "pytz/US/Central"],
57
+ )
58
+ def test_tz_localize_ambiguous_bool(self, unit, tz):
59
+ # make sure that we are correctly accepting bool values as ambiguous
60
+ # GH#14402
61
+ if isinstance(tz, str) and tz.startswith("pytz/"):
62
+ pytz = pytest.importorskip("pytz")
63
+ tz = pytz.timezone(tz.removeprefix("pytz/"))
64
+ ts = Timestamp("2015-11-01 01:00:03").as_unit(unit)
65
+ expected0 = Timestamp("2015-11-01 01:00:03-0500", tz=tz)
66
+ expected1 = Timestamp("2015-11-01 01:00:03-0600", tz=tz)
67
+
68
+ msg = "Cannot infer dst time from 2015-11-01 01:00:03"
69
+ with pytest.raises(ValueError, match=msg):
70
+ ts.tz_localize(tz)
71
+
72
+ result = ts.tz_localize(tz, ambiguous=True)
73
+ assert result == expected0
74
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
75
+
76
+ result = ts.tz_localize(tz, ambiguous=False)
77
+ assert result == expected1
78
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
79
+
80
+ @pytest.mark.parametrize("year", ["2014", "2200"])
81
+ def test_tz_localize_ambiguous(self, year):
82
+ ts = Timestamp(f"{year}-11-02 01:00").as_unit("s")
83
+ ts_dst = ts.tz_localize("US/Eastern", ambiguous=True)
84
+ ts_no_dst = ts.tz_localize("US/Eastern", ambiguous=False)
85
+
86
+ assert ts_no_dst._value - ts_dst._value == 3600
87
+ msg = re.escape(
88
+ "'ambiguous' parameter must be one of: "
89
+ "True, False, 'NaT', 'raise' (default)"
90
+ )
91
+ with pytest.raises(ValueError, match=msg):
92
+ ts.tz_localize("US/Eastern", ambiguous="infer")
93
+
94
+ def test_tz_localize_invalid(self):
95
+ # GH#8025
96
+ msg = "Cannot localize tz-aware Timestamp, use tz_convert for conversions"
97
+ with pytest.raises(TypeError, match=msg):
98
+ Timestamp("2011-01-01", tz="US/Eastern").tz_localize("Asia/Tokyo")
99
+
100
+ msg = "Cannot convert tz-naive Timestamp, use tz_localize to localize"
101
+ with pytest.raises(TypeError, match=msg):
102
+ Timestamp("2011-01-01").tz_convert("Asia/Tokyo")
103
+
104
+ @pytest.mark.parametrize("year", ["2015", "2201"])
105
+ @pytest.mark.parametrize(
106
+ "stamp, tz",
107
+ [
108
+ ("2015-03-08 02:00", "US/Eastern"),
109
+ ("2015-03-08 02:30", "US/Pacific"),
110
+ ("2015-03-29 02:00", "Europe/Paris"),
111
+ ("2015-03-29 02:30", "Europe/Belgrade"),
112
+ ],
113
+ )
114
+ def test_tz_localize_nonexistent(self, stamp, tz, year):
115
+ # GH#13057
116
+ stamp = stamp.replace("2015", year)
117
+ ts = Timestamp(stamp)
118
+ with pytest.raises(ValueError, match=stamp):
119
+ ts.tz_localize(tz)
120
+ # GH 22644
121
+ with pytest.raises(ValueError, match=stamp):
122
+ ts.tz_localize(tz, nonexistent="raise")
123
+ assert ts.tz_localize(tz, nonexistent="NaT") is NaT
124
+
125
+ @pytest.mark.parametrize(
126
+ "stamp, tz, forward_expected, backward_expected",
127
+ [
128
+ (
129
+ "2015-03-29 02:00:00",
130
+ "Europe/Warsaw",
131
+ "2015-03-29 03:00:00",
132
+ "2015-03-29 01:59:59.999999",
133
+ ), # utc+1 -> utc+2
134
+ (
135
+ "2023-03-12 02:00:00",
136
+ "America/Los_Angeles",
137
+ "2023-03-12 03:00:00",
138
+ "2023-03-12 01:59:59.999999",
139
+ ), # utc-8 -> utc-7
140
+ (
141
+ "2023-03-26 01:00:00",
142
+ "Europe/London",
143
+ "2023-03-26 02:00:00",
144
+ "2023-03-26 00:59:59.999999",
145
+ ), # utc+0 -> utc+1
146
+ (
147
+ "2023-03-26 00:00:00",
148
+ "Atlantic/Azores",
149
+ "2023-03-26 01:00:00",
150
+ "2023-03-25 23:59:59.999999",
151
+ ), # utc-1 -> utc+0
152
+ ],
153
+ )
154
+ def test_tz_localize_nonexistent_shift(
155
+ self, stamp, tz, forward_expected, backward_expected
156
+ ):
157
+ ts = Timestamp(stamp)
158
+ forward_ts = ts.tz_localize(tz, nonexistent="shift_forward")
159
+ assert forward_ts == Timestamp(forward_expected, tz=tz)
160
+ backward_ts = ts.tz_localize(tz, nonexistent="shift_backward")
161
+ assert backward_ts == Timestamp(backward_expected, tz=tz)
162
+
163
+ def test_tz_localize_ambiguous_raise(self):
164
+ # GH#13057
165
+ ts = Timestamp("2015-11-1 01:00")
166
+ msg = "Cannot infer dst time from 2015-11-01 01:00:00,"
167
+ with pytest.raises(ValueError, match=msg):
168
+ ts.tz_localize("US/Pacific", ambiguous="raise")
169
+
170
+ def test_tz_localize_nonexistent_invalid_arg(self, warsaw):
171
+ # GH 22644
172
+ tz = warsaw
173
+ ts = Timestamp("2015-03-29 02:00:00")
174
+ msg = (
175
+ "The nonexistent argument must be one of 'raise', 'NaT', "
176
+ "'shift_forward', 'shift_backward' or a timedelta object"
177
+ )
178
+ with pytest.raises(ValueError, match=msg):
179
+ ts.tz_localize(tz, nonexistent="foo")
180
+
181
+ @pytest.mark.parametrize(
182
+ "stamp",
183
+ [
184
+ "2014-02-01 09:00",
185
+ "2014-07-08 09:00",
186
+ "2014-11-01 17:00",
187
+ "2014-11-05 00:00",
188
+ ],
189
+ )
190
+ def test_tz_localize_roundtrip(self, stamp, tz_aware_fixture):
191
+ tz = tz_aware_fixture
192
+ ts = Timestamp(stamp)
193
+ localized = ts.tz_localize(tz)
194
+ assert localized == Timestamp(stamp, tz=tz)
195
+
196
+ msg = "Cannot localize tz-aware Timestamp"
197
+ with pytest.raises(TypeError, match=msg):
198
+ localized.tz_localize(tz)
199
+
200
+ reset = localized.tz_localize(None)
201
+ assert reset == ts
202
+ assert reset.tzinfo is None
203
+
204
+ def test_tz_localize_ambiguous_compat(self):
205
+ # validate that pytz and dateutil are compat for dst
206
+ # when the transition happens
207
+ pytz = pytest.importorskip("pytz")
208
+ naive = Timestamp("2013-10-27 01:00:00")
209
+ assert naive.unit == "us"
210
+
211
+ pytz_zone = pytz.timezone("Europe/London")
212
+ dateutil_zone = "dateutil/Europe/London"
213
+ result_pytz = naive.tz_localize(pytz_zone, ambiguous=False)
214
+ result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=False)
215
+ assert result_pytz._value == result_dateutil._value
216
+ assert result_pytz._value == 1_382_835_600 * 10**6
217
+
218
+ # fixed ambiguous behavior
219
+ # see gh-14621, GH#45087
220
+ assert result_pytz.to_pydatetime().tzname() == "GMT"
221
+ assert result_dateutil.to_pydatetime().tzname() == "GMT"
222
+ assert str(result_pytz) == str(result_dateutil)
223
+
224
+ # 1 hour difference
225
+ result_pytz = naive.tz_localize(pytz_zone, ambiguous=True)
226
+ result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=True)
227
+ assert result_pytz._value == result_dateutil._value
228
+ assert result_pytz._value == 1_382_832_000 * 10**6
229
+
230
+ # see gh-14621
231
+ assert str(result_pytz) == str(result_dateutil)
232
+ assert (
233
+ result_pytz.to_pydatetime().tzname()
234
+ == result_dateutil.to_pydatetime().tzname()
235
+ )
236
+
237
+ @pytest.mark.parametrize(
238
+ "tz",
239
+ [
240
+ "pytz/US/Eastern",
241
+ gettz("US/Eastern"),
242
+ zoneinfo.ZoneInfo("US/Eastern"),
243
+ "dateutil/US/Eastern",
244
+ ],
245
+ )
246
+ def test_timestamp_tz_localize(self, tz):
247
+ if isinstance(tz, str) and tz.startswith("pytz/"):
248
+ pytz = pytest.importorskip("pytz")
249
+ tz = pytz.timezone(tz.removeprefix("pytz/"))
250
+ stamp = Timestamp("3/11/2012 04:00")
251
+
252
+ result = stamp.tz_localize(tz)
253
+ expected = Timestamp("3/11/2012 04:00", tz=tz)
254
+ assert result.hour == expected.hour
255
+ assert result == expected
256
+
257
+ @pytest.mark.parametrize("year", ["2018", "2204"])
258
+ @pytest.mark.parametrize(
259
+ "start_ts, tz, end_ts, shift",
260
+ [
261
+ ["2018-03-25 02:20:00", "Europe/Warsaw", "2018-03-25 03:00:00", "forward"],
262
+ [
263
+ "2018-03-25 02:20:00",
264
+ "Europe/Warsaw",
265
+ "2018-03-25 01:59:59.999999999",
266
+ "backward",
267
+ ],
268
+ [
269
+ "2018-03-25 02:20:00",
270
+ "Europe/Warsaw",
271
+ "2018-03-25 03:20:00",
272
+ timedelta(hours=1),
273
+ ],
274
+ [
275
+ "2018-03-25 02:20:00",
276
+ "Europe/Warsaw",
277
+ "2018-03-25 01:20:00",
278
+ timedelta(hours=-1),
279
+ ],
280
+ ["2018-03-11 02:33:00", "US/Pacific", "2018-03-11 03:00:00", "forward"],
281
+ [
282
+ "2018-03-11 02:33:00",
283
+ "US/Pacific",
284
+ "2018-03-11 01:59:59.999999999",
285
+ "backward",
286
+ ],
287
+ [
288
+ "2018-03-11 02:33:00",
289
+ "US/Pacific",
290
+ "2018-03-11 03:33:00",
291
+ timedelta(hours=1),
292
+ ],
293
+ [
294
+ "2018-03-11 02:33:00",
295
+ "US/Pacific",
296
+ "2018-03-11 01:33:00",
297
+ timedelta(hours=-1),
298
+ ],
299
+ ],
300
+ )
301
+ @pytest.mark.parametrize("tz_type", ["", "dateutil/"])
302
+ def test_timestamp_tz_localize_nonexistent_shift(
303
+ self, start_ts, tz, end_ts, shift, year, tz_type, unit
304
+ ):
305
+ # GH 8917, 24466
306
+ if year == "2204" and tz_type == "dateutil/":
307
+ pytest.skip("ZoneInfo required for year >= 2200")
308
+ start_ts = start_ts.replace("2018", year)
309
+ end_ts = end_ts.replace("2018", year)
310
+ tz = tz_type + tz
311
+ if isinstance(shift, str):
312
+ shift = "shift_" + shift
313
+ ts = Timestamp(start_ts).as_unit(unit)
314
+ result = ts.tz_localize(tz, nonexistent=shift)
315
+ expected = Timestamp(end_ts).tz_localize(tz)
316
+
317
+ if unit == "us":
318
+ assert result == expected.replace(nanosecond=0)
319
+ elif unit == "ms":
320
+ micros = expected.microsecond - expected.microsecond % 1000
321
+ assert result == expected.replace(microsecond=micros, nanosecond=0)
322
+ elif unit == "s":
323
+ assert result == expected.replace(microsecond=0, nanosecond=0)
324
+ else:
325
+ assert result == expected
326
+ assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
327
+
328
+ @pytest.mark.parametrize("year", ["2015", "2201"])
329
+ @pytest.mark.parametrize("offset", [-1, 1])
330
+ def test_timestamp_tz_localize_nonexistent_shift_invalid(
331
+ self, offset, year, warsaw
332
+ ):
333
+ # GH 8917, 24466
334
+ tz = warsaw
335
+ _distant_date_only_for_zoneinfo(year, tz)
336
+ ts = Timestamp(f"{year}-03-29 02:20:00")
337
+ msg = "The provided timedelta will relocalize on a nonexistent time"
338
+ with pytest.raises(ValueError, match=msg):
339
+ ts.tz_localize(tz, nonexistent=timedelta(seconds=offset))
340
+
341
+ @pytest.mark.parametrize("year", ["2015", "2201"])
342
+ def test_timestamp_tz_localize_nonexistent_NaT(self, year, warsaw, unit):
343
+ # GH 8917
344
+ tz = warsaw
345
+ _distant_date_only_for_zoneinfo(year, tz)
346
+ ts = Timestamp(f"{year}-03-29 02:20:00").as_unit(unit)
347
+ result = ts.tz_localize(tz, nonexistent="NaT")
348
+ assert result is NaT
349
+
350
+ @pytest.mark.parametrize("year", ["2015", "2201"])
351
+ def test_timestamp_tz_localize_nonexistent_raise(self, year, warsaw, unit):
352
+ # GH 8917
353
+ tz = warsaw
354
+ _distant_date_only_for_zoneinfo(year, tz)
355
+ ts = Timestamp(f"{year}-03-29 02:20:00").as_unit(unit)
356
+ msg = f"{year}-03-29 02:20:00"
357
+ with pytest.raises(ValueError, match=msg):
358
+ ts.tz_localize(tz, nonexistent="raise")
359
+ msg = (
360
+ "The nonexistent argument must be one of 'raise', 'NaT', "
361
+ "'shift_forward', 'shift_backward' or a timedelta object"
362
+ )
363
+ with pytest.raises(ValueError, match=msg):
364
+ ts.tz_localize(tz, nonexistent="foo")
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ datetime,
3
+ timedelta,
4
+ timezone,
5
+ )
6
+
7
+ from dateutil.tz import gettz
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from pandas._libs.tslibs import (
12
+ OutOfBoundsDatetime,
13
+ OutOfBoundsTimedelta,
14
+ Timedelta,
15
+ Timestamp,
16
+ offsets,
17
+ to_offset,
18
+ )
19
+
20
+ import pandas._testing as tm
21
+
22
+
23
+ class TestTimestampArithmetic:
24
+ def test_overflow_offset(self):
25
+ # no overflow expected
26
+
27
+ stamp = Timestamp("2000/1/1")
28
+ offset_no_overflow = to_offset("D") * 100
29
+
30
+ expected = Timestamp("2000/04/10")
31
+ assert stamp + offset_no_overflow == expected
32
+
33
+ assert offset_no_overflow + stamp == expected
34
+
35
+ expected = Timestamp("1999/09/23")
36
+ assert stamp - offset_no_overflow == expected
37
+
38
+ def test_overflow_offset_raises(self):
39
+ # xref https://github.com/statsmodels/statsmodels/issues/3374
40
+ # ends up multiplying really large numbers which overflow
41
+
42
+ stamp = Timestamp("2017-01-13 00:00:00").as_unit("ns")
43
+ offset_overflow = 20169940 * offsets.Day(1)
44
+ lmsg2 = r"Cannot cast -?20169940 days \+?00:00:00 to unit='ns' without overflow"
45
+
46
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
47
+ stamp + offset_overflow
48
+
49
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
50
+ offset_overflow + stamp
51
+
52
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
53
+ stamp - offset_overflow
54
+
55
+ # xref https://github.com/pandas-dev/pandas/issues/14080
56
+ # used to crash, so check for proper overflow exception
57
+
58
+ stamp = Timestamp("2000/1/1").as_unit("ns")
59
+ offset_overflow = to_offset("D") * 100**5
60
+
61
+ lmsg3 = (
62
+ r"Cannot cast -?10000000000 days \+?00:00:00 to unit='ns' without overflow"
63
+ )
64
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
65
+ stamp + offset_overflow
66
+
67
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
68
+ offset_overflow + stamp
69
+
70
+ with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
71
+ stamp - offset_overflow
72
+
73
+ def test_overflow_timestamp_raises(self):
74
+ # https://github.com/pandas-dev/pandas/issues/31774
75
+ msg = "Result is too large"
76
+ a = Timestamp("2101-01-01 00:00:00").as_unit("ns")
77
+ b = Timestamp("1688-01-01 00:00:00").as_unit("ns")
78
+
79
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
80
+ a - b
81
+
82
+ # but we're OK for timestamp and datetime.datetime
83
+ assert (a - b.to_pydatetime()) == (a.to_pydatetime() - b)
84
+
85
+ def test_delta_preserve_nanos(self):
86
+ val = Timestamp(1337299200000000123)
87
+ result = val + timedelta(1)
88
+ assert result.nanosecond == val.nanosecond
89
+
90
+ def test_rsub_dtscalars(self, tz_naive_fixture):
91
+ # In particular, check that datetime64 - Timestamp works GH#28286
92
+ td = Timedelta(1235345642000)
93
+ ts = Timestamp("2021-01-01", tz=tz_naive_fixture)
94
+ other = ts + td
95
+
96
+ assert other - ts == td
97
+ assert other.to_pydatetime() - ts == td
98
+ if tz_naive_fixture is None:
99
+ assert other.to_datetime64() - ts == td
100
+ else:
101
+ msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
102
+ with pytest.raises(TypeError, match=msg):
103
+ other.to_datetime64() - ts
104
+
105
+ def test_timestamp_sub_datetime(self):
106
+ dt = datetime(2013, 10, 12)
107
+ ts = Timestamp(datetime(2013, 10, 13))
108
+ assert (ts - dt).days == 1
109
+ assert (dt - ts).days == -1
110
+
111
+ def test_subtract_tzaware_datetime(self):
112
+ t1 = Timestamp("2020-10-22T22:00:00+00:00")
113
+ t2 = datetime(2020, 10, 22, 22, tzinfo=timezone.utc)
114
+
115
+ result = t1 - t2
116
+
117
+ assert isinstance(result, Timedelta)
118
+ assert result == Timedelta("0 days")
119
+
120
+ def test_subtract_timestamp_from_different_timezone(self):
121
+ t1 = Timestamp("20130101").tz_localize("US/Eastern")
122
+ t2 = Timestamp("20130101").tz_localize("CET")
123
+
124
+ result = t1 - t2
125
+
126
+ assert isinstance(result, Timedelta)
127
+ assert result == Timedelta("0 days 06:00:00")
128
+
129
+ def test_subtracting_involving_datetime_with_different_tz(self):
130
+ t1 = datetime(2013, 1, 1, tzinfo=timezone(timedelta(hours=-5)))
131
+ t2 = Timestamp("20130101").tz_localize("CET")
132
+
133
+ result = t1 - t2
134
+
135
+ assert isinstance(result, Timedelta)
136
+ assert result == Timedelta("0 days 06:00:00")
137
+
138
+ result = t2 - t1
139
+ assert isinstance(result, Timedelta)
140
+ assert result == Timedelta("-1 days +18:00:00")
141
+
142
+ def test_subtracting_different_timezones(self, tz_aware_fixture):
143
+ t_raw = Timestamp("20130101")
144
+ t_UTC = t_raw.tz_localize("UTC")
145
+ t_diff = t_UTC.tz_convert(tz_aware_fixture) + Timedelta("0 days 05:00:00")
146
+
147
+ result = t_diff - t_UTC
148
+
149
+ assert isinstance(result, Timedelta)
150
+ assert result == Timedelta("0 days 05:00:00")
151
+
152
+ def test_addition_subtraction_types(self):
153
+ # Assert on the types resulting from Timestamp +/- various date/time
154
+ # objects
155
+ dt = datetime(2014, 3, 4)
156
+ td = timedelta(seconds=1)
157
+ ts = Timestamp(dt)
158
+
159
+ msg = "Addition/subtraction of integers"
160
+ with pytest.raises(TypeError, match=msg):
161
+ # GH#22535 add/sub with integers is deprecated
162
+ ts + 1
163
+ with pytest.raises(TypeError, match=msg):
164
+ ts - 1
165
+
166
+ # Timestamp + datetime not supported, though subtraction is supported
167
+ # and yields timedelta more tests in tseries/base/tests/test_base.py
168
+ assert type(ts - dt) == Timedelta
169
+ assert type(ts + td) == Timestamp
170
+ assert type(ts - td) == Timestamp
171
+
172
+ # Timestamp +/- datetime64 not supported, so not tested (could possibly
173
+ # assert error raised?)
174
+ td64 = np.timedelta64(1, "D")
175
+ assert type(ts + td64) == Timestamp
176
+ assert type(ts - td64) == Timestamp
177
+
178
+ @pytest.mark.parametrize(
179
+ "td", [Timedelta(hours=3), np.timedelta64(3, "h"), timedelta(hours=3)]
180
+ )
181
+ def test_radd_tdscalar(self, td, fixed_now_ts):
182
+ # GH#24775 timedelta64+Timestamp should not raise
183
+ ts = fixed_now_ts
184
+ assert td + ts == ts + td
185
+
186
+ @pytest.mark.parametrize(
187
+ "other,expected_difference",
188
+ [
189
+ (np.timedelta64(-123, "ns"), -123),
190
+ (np.timedelta64(1234567898, "ns"), 1234567898),
191
+ (np.timedelta64(-123, "us"), -123000),
192
+ (np.timedelta64(-123, "ms"), -123000000),
193
+ ],
194
+ )
195
+ def test_timestamp_add_timedelta64_unit(self, other, expected_difference):
196
+ now = datetime.now(timezone.utc)
197
+ ts = Timestamp(now).as_unit("ns")
198
+ result = ts + other
199
+ valdiff = result._value - ts._value
200
+ assert valdiff == expected_difference
201
+
202
+ ts2 = Timestamp(now)
203
+ assert ts2 + other == result
204
+
205
+ @pytest.mark.parametrize(
206
+ "ts",
207
+ [
208
+ Timestamp("1776-07-04"),
209
+ Timestamp("1776-07-04", tz="UTC"),
210
+ ],
211
+ )
212
+ @pytest.mark.parametrize(
213
+ "other",
214
+ [
215
+ 1,
216
+ np.int64(1),
217
+ np.array([1, 2], dtype=np.int32),
218
+ np.array([3, 4], dtype=np.uint64),
219
+ ],
220
+ )
221
+ def test_add_int_with_freq(self, ts, other):
222
+ msg = "Addition/subtraction of integers and integer-arrays"
223
+ with pytest.raises(TypeError, match=msg):
224
+ ts + other
225
+ with pytest.raises(TypeError, match=msg):
226
+ other + ts
227
+
228
+ with pytest.raises(TypeError, match=msg):
229
+ ts - other
230
+
231
+ msg = "unsupported operand type"
232
+ with pytest.raises(TypeError, match=msg):
233
+ other - ts
234
+
235
+ @pytest.mark.parametrize("shape", [(6,), (2, 3)])
236
+ def test_addsub_m8ndarray(self, shape):
237
+ # GH#33296
238
+ ts = Timestamp("2020-04-04 15:45").as_unit("ns")
239
+ other = np.arange(6).astype("m8[h]").reshape(shape)
240
+
241
+ result = ts + other
242
+
243
+ ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
244
+ expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
245
+ tm.assert_numpy_array_equal(result, expected)
246
+
247
+ result = other + ts
248
+ tm.assert_numpy_array_equal(result, expected)
249
+
250
+ result = ts - other
251
+ ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
252
+ expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
253
+ tm.assert_numpy_array_equal(result, expected)
254
+
255
+ msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
256
+ with pytest.raises(TypeError, match=msg):
257
+ other - ts
258
+
259
+ @pytest.mark.parametrize("shape", [(6,), (2, 3)])
260
+ def test_addsub_m8ndarray_tzaware(self, shape):
261
+ # GH#33296
262
+ ts = Timestamp("2020-04-04 15:45", tz="US/Pacific")
263
+
264
+ other = np.arange(6).astype("m8[h]").reshape(shape)
265
+
266
+ result = ts + other
267
+
268
+ ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
269
+ expected = np.array(ex_stamps).reshape(shape)
270
+ tm.assert_numpy_array_equal(result, expected)
271
+
272
+ result = other + ts
273
+ tm.assert_numpy_array_equal(result, expected)
274
+
275
+ result = ts - other
276
+ ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
277
+ expected = np.array(ex_stamps).reshape(shape)
278
+ tm.assert_numpy_array_equal(result, expected)
279
+
280
+ msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
281
+ with pytest.raises(TypeError, match=msg):
282
+ other - ts
283
+
284
+ def test_subtract_different_utc_objects(self, utc_fixture, utc_fixture2):
285
+ # GH 32619
286
+ dt = datetime(2021, 1, 1)
287
+ ts1 = Timestamp(dt, tz=utc_fixture)
288
+ ts2 = Timestamp(dt, tz=utc_fixture2)
289
+ result = ts1 - ts2
290
+ expected = Timedelta(0)
291
+ assert result == expected
292
+
293
+ @pytest.mark.parametrize(
294
+ "tz",
295
+ [
296
+ "pytz/US/Eastern",
297
+ gettz("US/Eastern"),
298
+ "US/Eastern",
299
+ "dateutil/US/Eastern",
300
+ ],
301
+ )
302
+ def test_timestamp_add_timedelta_push_over_dst_boundary(self, tz):
303
+ # GH#1389
304
+ if isinstance(tz, str) and tz.startswith("pytz/"):
305
+ pytz = pytest.importorskip("pytz")
306
+ tz = pytz.timezone(tz.removeprefix("pytz/"))
307
+ # 4 hours before DST transition
308
+ stamp = Timestamp("3/10/2012 22:00", tz=tz)
309
+
310
+ result = stamp + timedelta(hours=6)
311
+
312
+ # spring forward, + "7" hours
313
+ expected = Timestamp("3/11/2012 05:00", tz=tz)
314
+
315
+ assert result == expected
316
+
317
+ def test_timestamp_dst_transition(self):
318
+ # GH 60084
319
+ dt_str = "2023-11-05 01:00-08:00"
320
+ tz_str = "America/Los_Angeles"
321
+
322
+ ts1 = Timestamp(dt_str, tz=tz_str)
323
+ ts2 = ts1 + Timedelta(hours=0)
324
+
325
+ assert ts1 == ts2
326
+ assert hash(ts1) == hash(ts2)
327
+
328
+
329
+ class SubDatetime(datetime):
330
+ pass
331
+
332
+
333
+ @pytest.mark.parametrize(
334
+ "lh,rh",
335
+ [
336
+ (SubDatetime(2000, 1, 1), Timedelta(hours=1)),
337
+ (Timedelta(hours=1), SubDatetime(2000, 1, 1)),
338
+ ],
339
+ )
340
+ def test_dt_subclass_add_timedelta(lh, rh):
341
+ # GH#25851
342
+ # ensure that subclassed datetime works for
343
+ # Timedelta operations
344
+ result = lh + rh
345
+ expected = SubDatetime(2000, 1, 1, 1)
346
+ assert result == expected
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ datetime,
3
+ timedelta,
4
+ )
5
+ import operator
6
+
7
+ import numpy as np
8
+ import pytest
9
+
10
+ from pandas import Timestamp
11
+ import pandas._testing as tm
12
+
13
+
14
+ class TestTimestampComparison:
15
+ def test_compare_non_nano_dt64(self):
16
+ # don't raise when converting dt64 to Timestamp in __richcmp__
17
+ dt = np.datetime64("1066-10-14")
18
+ ts = Timestamp(dt)
19
+
20
+ assert dt == ts
21
+
22
+ def test_comparison_dt64_ndarray(self):
23
+ ts = Timestamp("2021-01-01")
24
+ ts2 = Timestamp("2019-04-05")
25
+ arr = np.array([[ts.asm8, ts2.asm8]], dtype="M8[ns]")
26
+
27
+ result = ts == arr
28
+ expected = np.array([[True, False]], dtype=bool)
29
+ tm.assert_numpy_array_equal(result, expected)
30
+
31
+ result = arr == ts
32
+ tm.assert_numpy_array_equal(result, expected)
33
+
34
+ result = ts != arr
35
+ tm.assert_numpy_array_equal(result, ~expected)
36
+
37
+ result = arr != ts
38
+ tm.assert_numpy_array_equal(result, ~expected)
39
+
40
+ result = ts2 < arr
41
+ tm.assert_numpy_array_equal(result, expected)
42
+
43
+ result = arr < ts2
44
+ tm.assert_numpy_array_equal(result, np.array([[False, False]], dtype=bool))
45
+
46
+ result = ts2 <= arr
47
+ tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
48
+
49
+ result = arr <= ts2
50
+ tm.assert_numpy_array_equal(result, ~expected)
51
+
52
+ result = ts >= arr
53
+ tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
54
+
55
+ result = arr >= ts
56
+ tm.assert_numpy_array_equal(result, np.array([[True, False]], dtype=bool))
57
+
58
+ @pytest.mark.parametrize("reverse", [True, False])
59
+ def test_comparison_dt64_ndarray_tzaware(self, reverse, comparison_op):
60
+ ts = Timestamp("2021-01-01 00:00:00.00000", tz="UTC")
61
+ arr = np.array([ts.asm8, ts.asm8], dtype="M8[ns]")
62
+
63
+ left, right = ts, arr
64
+ if reverse:
65
+ left, right = arr, ts
66
+
67
+ if comparison_op is operator.eq:
68
+ expected = np.array([False, False], dtype=bool)
69
+ result = comparison_op(left, right)
70
+ tm.assert_numpy_array_equal(result, expected)
71
+ elif comparison_op is operator.ne:
72
+ expected = np.array([True, True], dtype=bool)
73
+ result = comparison_op(left, right)
74
+ tm.assert_numpy_array_equal(result, expected)
75
+ else:
76
+ msg = "Cannot compare tz-naive and tz-aware timestamps"
77
+ with pytest.raises(TypeError, match=msg):
78
+ comparison_op(left, right)
79
+
80
+ def test_comparison_object_array(self):
81
+ # GH#15183
82
+ ts = Timestamp("2011-01-03 00:00:00-0500", tz="US/Eastern")
83
+ other = Timestamp("2011-01-01 00:00:00-0500", tz="US/Eastern")
84
+ naive = Timestamp("2011-01-01 00:00:00")
85
+
86
+ arr = np.array([other, ts], dtype=object)
87
+ res = arr == ts
88
+ expected = np.array([False, True], dtype=bool)
89
+ assert (res == expected).all()
90
+
91
+ # 2D case
92
+ arr = np.array([[other, ts], [ts, other]], dtype=object)
93
+ res = arr != ts
94
+ expected = np.array([[True, False], [False, True]], dtype=bool)
95
+ assert res.shape == expected.shape
96
+ assert (res == expected).all()
97
+
98
+ # tzaware mismatch
99
+ arr = np.array([naive], dtype=object)
100
+ msg = "Cannot compare tz-naive and tz-aware timestamps"
101
+ with pytest.raises(TypeError, match=msg):
102
+ arr < ts
103
+
104
+ def test_comparison(self):
105
+ # 5-18-2012 00:00:00.000
106
+ stamp = 1337299200000000000
107
+
108
+ val = Timestamp(stamp)
109
+
110
+ assert val == val
111
+ assert not val != val
112
+ assert not val < val
113
+ assert val <= val
114
+ assert not val > val
115
+ assert val >= val
116
+
117
+ other = datetime(2012, 5, 18)
118
+ assert val == other
119
+ assert not val != other
120
+ assert not val < other
121
+ assert val <= other
122
+ assert not val > other
123
+ assert val >= other
124
+
125
+ other = Timestamp(stamp + 100)
126
+
127
+ assert val != other
128
+ assert val != other
129
+ assert val < other
130
+ assert val <= other
131
+ assert other > val
132
+ assert other >= val
133
+
134
+ def test_compare_invalid(self):
135
+ # GH#8058
136
+ val = Timestamp("20130101 12:01:02")
137
+ assert not val == "foo"
138
+ assert not val == 10.0
139
+ assert not val == 1
140
+ assert not val == []
141
+ assert not val == {"foo": 1}
142
+ assert not val == np.float64(1)
143
+ assert not val == np.int64(1)
144
+
145
+ assert val != "foo"
146
+ assert val != 10.0
147
+ assert val != 1
148
+ assert val != []
149
+ assert val != {"foo": 1}
150
+ assert val != np.float64(1)
151
+ assert val != np.int64(1)
152
+
153
+ @pytest.mark.parametrize("tz", [None, "US/Pacific"])
154
+ def test_compare_date(self, tz):
155
+ # GH#36131 comparing Timestamp with date object is deprecated
156
+ ts = Timestamp("2021-01-01 00:00:00.00000", tz=tz)
157
+ dt = ts.to_pydatetime().date()
158
+ # in 2.0 we disallow comparing pydate objects with Timestamps,
159
+ # following the stdlib datetime behavior.
160
+
161
+ msg = "Cannot compare Timestamp with datetime.date"
162
+ for left, right in [(ts, dt), (dt, ts)]:
163
+ assert not left == right
164
+ assert left != right
165
+
166
+ with pytest.raises(TypeError, match=msg):
167
+ left < right
168
+ with pytest.raises(TypeError, match=msg):
169
+ left <= right
170
+ with pytest.raises(TypeError, match=msg):
171
+ left > right
172
+ with pytest.raises(TypeError, match=msg):
173
+ left >= right
174
+
175
+ def test_cant_compare_tz_naive_w_aware(self, utc_fixture):
176
+ # see GH#1404
177
+ a = Timestamp("3/12/2012")
178
+ b = Timestamp("3/12/2012", tz=utc_fixture)
179
+
180
+ msg = "Cannot compare tz-naive and tz-aware timestamps"
181
+ assert not a == b
182
+ assert a != b
183
+ with pytest.raises(TypeError, match=msg):
184
+ a < b
185
+ with pytest.raises(TypeError, match=msg):
186
+ a <= b
187
+ with pytest.raises(TypeError, match=msg):
188
+ a > b
189
+ with pytest.raises(TypeError, match=msg):
190
+ a >= b
191
+
192
+ assert not b == a
193
+ assert b != a
194
+ with pytest.raises(TypeError, match=msg):
195
+ b < a
196
+ with pytest.raises(TypeError, match=msg):
197
+ b <= a
198
+ with pytest.raises(TypeError, match=msg):
199
+ b > a
200
+ with pytest.raises(TypeError, match=msg):
201
+ b >= a
202
+
203
+ assert not a == b.to_pydatetime()
204
+ assert not a.to_pydatetime() == b
205
+
206
+ def test_timestamp_compare_scalars(self):
207
+ # case where ndim == 0
208
+ lhs = np.datetime64(datetime(2013, 12, 6))
209
+ rhs = Timestamp("now")
210
+ nat = Timestamp("nat")
211
+
212
+ ops = {"gt": "lt", "lt": "gt", "ge": "le", "le": "ge", "eq": "eq", "ne": "ne"}
213
+
214
+ for left, right in ops.items():
215
+ left_f = getattr(operator, left)
216
+ right_f = getattr(operator, right)
217
+ expected = left_f(lhs, rhs)
218
+
219
+ result = right_f(rhs, lhs)
220
+ assert result == expected
221
+
222
+ expected = left_f(rhs, nat)
223
+ result = right_f(nat, rhs)
224
+ assert result == expected
225
+
226
+ def test_timestamp_compare_with_early_datetime(self):
227
+ # e.g. datetime.min
228
+ stamp = Timestamp("2012-01-01")
229
+
230
+ assert not stamp == datetime.min
231
+ assert not stamp == datetime(1600, 1, 1)
232
+ assert not stamp == datetime(2700, 1, 1)
233
+ assert stamp != datetime.min
234
+ assert stamp != datetime(1600, 1, 1)
235
+ assert stamp != datetime(2700, 1, 1)
236
+ assert stamp > datetime(1600, 1, 1)
237
+ assert stamp >= datetime(1600, 1, 1)
238
+ assert stamp < datetime(2700, 1, 1)
239
+ assert stamp <= datetime(2700, 1, 1)
240
+
241
+ other = Timestamp.min.to_pydatetime(warn=False)
242
+ assert other - timedelta(microseconds=1) < Timestamp.min
243
+
244
+ def test_timestamp_compare_oob_dt64(self):
245
+ us = np.timedelta64(1, "us")
246
+ other = np.datetime64(Timestamp.min).astype("M8[us]")
247
+
248
+ # This may change if the implementation bound is dropped to match
249
+ # DatetimeArray/DatetimeIndex GH#24124
250
+ assert Timestamp.min > other
251
+ # Note: numpy gets the reversed comparison wrong
252
+
253
+ other = np.datetime64(Timestamp.max).astype("M8[us]")
254
+ assert Timestamp.max > other # not actually OOB
255
+ assert other < Timestamp.max
256
+
257
+ assert Timestamp.max < other + us
258
+ # Note: numpy gets the reversed comparison wrong
259
+
260
+ # GH-42794
261
+ other = datetime(9999, 9, 9)
262
+ assert Timestamp.min < other
263
+ assert other > Timestamp.min
264
+ assert Timestamp.max < other
265
+ assert other > Timestamp.max
266
+
267
+ other = datetime(1, 1, 1)
268
+ assert Timestamp.max > other
269
+ assert other < Timestamp.max
270
+ assert Timestamp.min > other
271
+ assert other < Timestamp.min
272
+
273
+ def test_compare_zerodim_array(self, fixed_now_ts):
274
+ # GH#26916
275
+ ts = fixed_now_ts
276
+ dt64 = np.datetime64("2016-01-01", "ns")
277
+ arr = np.array(dt64)
278
+ assert arr.ndim == 0
279
+
280
+ result = arr < ts
281
+ assert result is np.bool_(True)
282
+ result = arr > ts
283
+ assert result is np.bool_(False)
284
+
285
+
286
+ def test_rich_comparison_with_unsupported_type():
287
+ # Comparisons with unsupported objects should return NotImplemented
288
+ # (it previously raised TypeError, see #24011)
289
+
290
+ class Inf:
291
+ def __lt__(self, o):
292
+ return False
293
+
294
+ def __le__(self, o):
295
+ return isinstance(o, Inf)
296
+
297
+ def __gt__(self, o):
298
+ return not isinstance(o, Inf)
299
+
300
+ def __ge__(self, o):
301
+ return True
302
+
303
+ def __eq__(self, other) -> bool:
304
+ return isinstance(other, Inf)
305
+
306
+ inf = Inf()
307
+ timestamp = Timestamp("2018-11-30")
308
+
309
+ for left, right in [(inf, timestamp), (timestamp, inf)]:
310
+ assert left > right or left < right
311
+ assert left >= right or left <= right
312
+ assert not left == right
313
+ assert left != right
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_constructors.py ADDED
@@ -0,0 +1,1107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import calendar
2
+ from datetime import (
3
+ date,
4
+ datetime,
5
+ timedelta,
6
+ timezone,
7
+ )
8
+ import zoneinfo
9
+
10
+ import dateutil.tz
11
+ from dateutil.tz import (
12
+ gettz,
13
+ tzoffset,
14
+ tzutc,
15
+ )
16
+ import numpy as np
17
+ import pytest
18
+
19
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
20
+ from pandas.compat import PY314
21
+ from pandas.errors import (
22
+ OutOfBoundsDatetime,
23
+ Pandas4Warning,
24
+ )
25
+
26
+ from pandas import (
27
+ NA,
28
+ NaT,
29
+ Period,
30
+ Timedelta,
31
+ Timestamp,
32
+ )
33
+ import pandas._testing as tm
34
+
35
+
36
+ class TestTimestampConstructorUnitKeyword:
37
+ @pytest.mark.parametrize("typ", [int, float])
38
+ def test_constructor_int_float_with_YM_unit(self, typ):
39
+ # GH#47266 avoid the conversions in cast_from_unit
40
+ val = typ(150)
41
+
42
+ ts = Timestamp(val, unit="Y")
43
+ expected = Timestamp("2120-01-01")
44
+ assert ts == expected
45
+
46
+ ts = Timestamp(val, unit="M")
47
+ expected = Timestamp("1982-07-01")
48
+ assert ts == expected
49
+
50
+ @pytest.mark.parametrize("typ", [int, float])
51
+ def test_construct_from_int_float_with_unit_out_of_bound_raises(self, typ):
52
+ # GH#50870 make sure we get an OutOfBoundsDatetime instead of OverflowError
53
+ val = typ(150000000000000)
54
+
55
+ msg = f"cannot convert input {int(val)} with the unit 'D'"
56
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
57
+ Timestamp(val, unit="D")
58
+
59
+ def test_constructor_float_not_round_with_YM_unit_raises(self):
60
+ # GH#47267 avoid the conversions in cast_from-unit
61
+
62
+ msg = "Conversion of non-round float with unit=[MY] is ambiguous"
63
+ with pytest.raises(ValueError, match=msg):
64
+ Timestamp(150.5, unit="Y")
65
+
66
+ with pytest.raises(ValueError, match=msg):
67
+ Timestamp(150.5, unit="M")
68
+
69
+ @pytest.mark.parametrize(
70
+ "value, check_kwargs",
71
+ [
72
+ [946688461000000000, {}],
73
+ [946688461000000000 / 1000, {"unit": "us"}],
74
+ [946688461000000000 / 1_000_000, {"unit": "ms"}],
75
+ [946688461000000000 / 1_000_000_000, {"unit": "s"}],
76
+ [10957, {"unit": "D", "h": 0}],
77
+ [
78
+ (946688461000000000 + 500000) / 1000000000,
79
+ {"unit": "s", "us": 499, "ns": 964},
80
+ ],
81
+ [
82
+ (946688461000000000 + 500000000) / 1000000000,
83
+ {"unit": "s", "us": 500000},
84
+ ],
85
+ [(946688461000000000 + 500000) / 1000000, {"unit": "ms", "us": 500}],
86
+ [(946688461000000000 + 500000) / 1000, {"unit": "us", "us": 500}],
87
+ [(946688461000000000 + 500000000) / 1000000, {"unit": "ms", "us": 500000}],
88
+ [946688461000000000 / 1000.0 + 5, {"unit": "us", "us": 5}],
89
+ [946688461000000000 / 1000.0 + 5000, {"unit": "us", "us": 5000}],
90
+ [946688461000000000 / 1000000.0 + 0.5, {"unit": "ms", "us": 500}],
91
+ [946688461000000000 / 1000000.0 + 0.005, {"unit": "ms", "us": 5, "ns": 5}],
92
+ [946688461000000000 / 1000000000.0 + 0.5, {"unit": "s", "us": 500000}],
93
+ [10957 + 0.5, {"unit": "D", "h": 12}],
94
+ ],
95
+ )
96
+ def test_construct_with_unit(self, value, check_kwargs):
97
+ def check(value, unit=None, h=1, s=1, us=0, ns=0):
98
+ stamp = Timestamp(value, unit=unit)
99
+ assert stamp.year == 2000
100
+ assert stamp.month == 1
101
+ assert stamp.day == 1
102
+ assert stamp.hour == h
103
+ if unit != "D":
104
+ assert stamp.minute == 1
105
+ assert stamp.second == s
106
+ assert stamp.microsecond == us
107
+ else:
108
+ assert stamp.minute == 0
109
+ assert stamp.second == 0
110
+ assert stamp.microsecond == 0
111
+ assert stamp.nanosecond == ns
112
+
113
+ check(value, **check_kwargs)
114
+
115
+
116
+ class TestTimestampConstructorFoldKeyword:
117
+ def test_timestamp_constructor_invalid_fold_raise(self):
118
+ # Test for GH#25057
119
+ # Valid fold values are only [None, 0, 1]
120
+ msg = "Valid values for the fold argument are None, 0, or 1."
121
+ with pytest.raises(ValueError, match=msg):
122
+ Timestamp(123, fold=2)
123
+
124
+ def test_timestamp_constructor_pytz_fold_raise(self):
125
+ # Test for GH#25057
126
+ # pytz doesn't support fold. Check that we raise
127
+ # if fold is passed with pytz
128
+ pytz = pytest.importorskip("pytz")
129
+ msg = "pytz timezones do not support fold. Please use dateutil timezones."
130
+ tz = pytz.timezone("Europe/London")
131
+ with pytest.raises(ValueError, match=msg):
132
+ Timestamp(datetime(2019, 10, 27, 0, 30, 0, 0), tz=tz, fold=0)
133
+
134
+ @pytest.mark.parametrize("fold", [0, 1])
135
+ @pytest.mark.parametrize(
136
+ "ts_input",
137
+ [
138
+ 1572136200000000000,
139
+ 1572136200000000000.0,
140
+ np.datetime64(1572136200000000000, "ns"),
141
+ "2019-10-27 01:30:00+01:00",
142
+ datetime(2019, 10, 27, 0, 30, 0, 0, tzinfo=timezone.utc),
143
+ ],
144
+ )
145
+ def test_timestamp_constructor_fold_conflict(self, ts_input, fold):
146
+ # Test for GH#25057
147
+ # Check that we raise on fold conflict
148
+ msg = (
149
+ "Cannot pass fold with possibly unambiguous input: int, float, "
150
+ "numpy.datetime64, str, or timezone-aware datetime-like. "
151
+ "Pass naive datetime-like or build Timestamp from components."
152
+ )
153
+ with pytest.raises(ValueError, match=msg):
154
+ Timestamp(ts_input=ts_input, fold=fold)
155
+
156
+ @pytest.mark.parametrize("tz", ["dateutil/Europe/London", None])
157
+ @pytest.mark.parametrize("fold", [0, 1])
158
+ def test_timestamp_constructor_retain_fold(self, tz, fold):
159
+ # Test for GH#25057
160
+ # Check that we retain fold
161
+ ts = Timestamp(year=2019, month=10, day=27, hour=1, minute=30, tz=tz, fold=fold)
162
+ result = ts.fold
163
+ expected = fold
164
+ assert result == expected
165
+
166
+ @pytest.mark.parametrize(
167
+ "tz",
168
+ [
169
+ "dateutil/Europe/London",
170
+ zoneinfo.ZoneInfo("Europe/London"),
171
+ ],
172
+ )
173
+ @pytest.mark.parametrize(
174
+ "ts_input,fold_out",
175
+ [
176
+ (1572136200000000000, 0),
177
+ (1572139800000000000, 1),
178
+ ("2019-10-27 01:30:00+01:00", 0),
179
+ ("2019-10-27 01:30:00+00:00", 1),
180
+ (datetime(2019, 10, 27, 1, 30, 0, 0, fold=0), 0),
181
+ (datetime(2019, 10, 27, 1, 30, 0, 0, fold=1), 1),
182
+ ],
183
+ )
184
+ def test_timestamp_constructor_infer_fold_from_value(self, tz, ts_input, fold_out):
185
+ # Test for GH#25057
186
+ # Check that we infer fold correctly based on timestamps since utc
187
+ # or strings
188
+ ts = Timestamp(ts_input, tz=tz)
189
+ result = ts.fold
190
+ expected = fold_out
191
+ assert result == expected
192
+
193
+ @pytest.mark.parametrize("tz", ["dateutil/Europe/London"])
194
+ @pytest.mark.parametrize(
195
+ "fold,value_out",
196
+ [
197
+ (0, 1572136200000000),
198
+ (1, 1572139800000000),
199
+ ],
200
+ )
201
+ def test_timestamp_constructor_adjust_value_for_fold(self, tz, fold, value_out):
202
+ # Test for GH#25057
203
+ # Check that we adjust value for fold correctly
204
+ # based on timestamps since utc
205
+ ts_input = datetime(2019, 10, 27, 1, 30)
206
+ ts = Timestamp(ts_input, tz=tz, fold=fold)
207
+ result = ts._value
208
+ expected = value_out
209
+ assert result == expected
210
+
211
+
212
+ class TestTimestampConstructorPositionalAndKeywordSupport:
213
+ def test_constructor_positional(self):
214
+ # see GH#10758
215
+ msg = "'NoneType' object cannot be interpreted as an integer"
216
+ with pytest.raises(TypeError, match=msg):
217
+ Timestamp(2000, 1)
218
+
219
+ msg = "month must be in 1..12"
220
+ with pytest.raises(ValueError, match=msg):
221
+ Timestamp(2000, 0, 1)
222
+ with pytest.raises(ValueError, match=msg):
223
+ Timestamp(2000, 13, 1)
224
+
225
+ if PY314:
226
+ msg = "must be in range 1..31 for month 1 in year 2000"
227
+ else:
228
+ msg = "day is out of range for month"
229
+ with pytest.raises(ValueError, match=msg):
230
+ Timestamp(2000, 1, 0)
231
+ with pytest.raises(ValueError, match=msg):
232
+ Timestamp(2000, 1, 32)
233
+
234
+ # see gh-11630
235
+ assert repr(Timestamp(2015, 11, 12)) == repr(Timestamp("20151112"))
236
+ assert repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)) == repr(
237
+ Timestamp("2015-11-12 01:02:03.999999")
238
+ )
239
+
240
+ def test_constructor_keyword(self):
241
+ # GH#10758
242
+ msg = "function missing required argument 'day'|Required argument 'day'"
243
+ with pytest.raises(TypeError, match=msg):
244
+ Timestamp(year=2000, month=1)
245
+
246
+ msg = "month must be in 1..12"
247
+ with pytest.raises(ValueError, match=msg):
248
+ Timestamp(year=2000, month=0, day=1)
249
+ with pytest.raises(ValueError, match=msg):
250
+ Timestamp(year=2000, month=13, day=1)
251
+
252
+ if PY314:
253
+ msg = "must be in range 1..31 for month 1 in year 2000"
254
+ else:
255
+ msg = "day is out of range for month"
256
+ with pytest.raises(ValueError, match=msg):
257
+ Timestamp(year=2000, month=1, day=0)
258
+ with pytest.raises(ValueError, match=msg):
259
+ Timestamp(year=2000, month=1, day=32)
260
+
261
+ assert repr(Timestamp(year=2015, month=11, day=12)) == repr(
262
+ Timestamp("20151112")
263
+ )
264
+
265
+ assert repr(
266
+ Timestamp(
267
+ year=2015,
268
+ month=11,
269
+ day=12,
270
+ hour=1,
271
+ minute=2,
272
+ second=3,
273
+ microsecond=999999,
274
+ )
275
+ ) == repr(Timestamp("2015-11-12 01:02:03.999999"))
276
+
277
+ @pytest.mark.parametrize(
278
+ "arg",
279
+ [
280
+ "year",
281
+ "month",
282
+ "day",
283
+ "hour",
284
+ "minute",
285
+ "second",
286
+ "microsecond",
287
+ "nanosecond",
288
+ ],
289
+ )
290
+ def test_invalid_date_kwarg_with_string_input(self, arg):
291
+ kwarg = {arg: 1}
292
+ msg = "Cannot pass a date attribute keyword argument"
293
+ with pytest.raises(ValueError, match=msg):
294
+ Timestamp("2010-10-10 12:59:59.999999999", **kwarg)
295
+
296
+ @pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}])
297
+ def test_constructor_missing_keyword(self, kwargs):
298
+ # GH#31200
299
+
300
+ # The exact error message of datetime() depends on its version
301
+ msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)"
302
+ msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found"
303
+ msg = "|".join([msg1, msg2])
304
+
305
+ with pytest.raises(TypeError, match=msg):
306
+ Timestamp(**kwargs)
307
+
308
+ def test_constructor_positional_with_tzinfo(self):
309
+ # GH#31929
310
+ ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc)
311
+ expected = Timestamp("2020-12-31", tzinfo=timezone.utc)
312
+ assert ts == expected
313
+
314
+ @pytest.mark.parametrize("kwd", ["nanosecond", "microsecond", "second", "minute"])
315
+ def test_constructor_positional_keyword_mixed_with_tzinfo(self, kwd, request):
316
+ # TODO: if we passed microsecond with a keyword we would mess up
317
+ # xref GH#45307
318
+ if kwd != "nanosecond":
319
+ # nanosecond is keyword-only as of 2.0, others are not
320
+ mark = pytest.mark.xfail(reason="GH#45307")
321
+ request.applymarker(mark)
322
+
323
+ kwargs = {kwd: 4}
324
+ ts = Timestamp(2020, 12, 31, tzinfo=timezone.utc, **kwargs)
325
+
326
+ td_kwargs = {kwd + "s": 4}
327
+ td = Timedelta(**td_kwargs)
328
+ expected = Timestamp("2020-12-31", tz=timezone.utc) + td
329
+ assert ts == expected
330
+
331
+
332
+ class TestTimestampClassMethodConstructors:
333
+ # Timestamp constructors other than __new__
334
+
335
+ def test_utcnow_deprecated(self):
336
+ # GH#56680
337
+ msg = "Timestamp.utcnow is deprecated"
338
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
339
+ Timestamp.utcnow()
340
+
341
+ def test_utcfromtimestamp_deprecated(self):
342
+ # GH#56680
343
+ msg = "Timestamp.utcfromtimestamp is deprecated"
344
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
345
+ Timestamp.utcfromtimestamp(43)
346
+
347
+ def test_constructor_strptime(self):
348
+ # GH#25016
349
+ # Test support for Timestamp.strptime
350
+ fmt = "%Y%m%d-%H%M%S-%f%z"
351
+ ts = "20190129-235348-000001+0000"
352
+ msg = r"Timestamp.strptime\(\) is not implemented"
353
+ with pytest.raises(NotImplementedError, match=msg):
354
+ Timestamp.strptime(ts, fmt)
355
+
356
+ def test_constructor_fromisocalendar(self):
357
+ # GH#30395
358
+ expected_timestamp = Timestamp("2000-01-03 00:00:00")
359
+ expected_stdlib = datetime.fromisocalendar(2000, 1, 1)
360
+ result = Timestamp.fromisocalendar(2000, 1, 1)
361
+ assert result == expected_timestamp
362
+ assert result == expected_stdlib
363
+ assert isinstance(result, Timestamp)
364
+
365
+ def test_constructor_fromordinal(self):
366
+ base = datetime(2000, 1, 1)
367
+
368
+ ts = Timestamp.fromordinal(base.toordinal())
369
+ assert base == ts
370
+ assert base.toordinal() == ts.toordinal()
371
+
372
+ ts = Timestamp.fromordinal(base.toordinal(), tz="US/Eastern")
373
+ assert Timestamp("2000-01-01", tz="US/Eastern") == ts
374
+ assert base.toordinal() == ts.toordinal()
375
+
376
+ # GH#3042
377
+ dt = datetime(2011, 4, 16, 0, 0)
378
+ ts = Timestamp.fromordinal(dt.toordinal())
379
+ assert ts.to_pydatetime() == dt
380
+
381
+ # with a tzinfo
382
+ stamp = Timestamp("2011-4-16", tz="US/Eastern")
383
+ dt_tz = stamp.to_pydatetime()
384
+ ts = Timestamp.fromordinal(dt_tz.toordinal(), tz="US/Eastern")
385
+ assert ts.to_pydatetime() == dt_tz
386
+
387
+ def test_now(self):
388
+ # GH#9000
389
+ ts_from_string = Timestamp("now")
390
+ ts_from_method = Timestamp.now()
391
+ ts_datetime = datetime.now()
392
+
393
+ ts_from_string_tz = Timestamp("now", tz="US/Eastern")
394
+ ts_from_method_tz = Timestamp.now(tz="US/Eastern")
395
+
396
+ # Check that the delta between the times is less than 1s (arbitrarily
397
+ # small)
398
+ delta = Timedelta(seconds=1)
399
+ assert abs(ts_from_method - ts_from_string) < delta
400
+ assert abs(ts_datetime - ts_from_method) < delta
401
+ assert abs(ts_from_method_tz - ts_from_string_tz) < delta
402
+ assert (
403
+ abs(
404
+ ts_from_string_tz.tz_localize(None)
405
+ - ts_from_method_tz.tz_localize(None)
406
+ )
407
+ < delta
408
+ )
409
+
410
+ def test_today(self):
411
+ ts_from_string = Timestamp("today")
412
+ ts_from_method = Timestamp.today()
413
+ ts_datetime = datetime.today()
414
+
415
+ ts_from_string_tz = Timestamp("today", tz="US/Eastern")
416
+ ts_from_method_tz = Timestamp.today(tz="US/Eastern")
417
+
418
+ # Check that the delta between the times is less than 1s (arbitrarily
419
+ # small)
420
+ delta = Timedelta(seconds=1)
421
+ assert abs(ts_from_method - ts_from_string) < delta
422
+ assert abs(ts_datetime - ts_from_method) < delta
423
+ assert abs(ts_from_method_tz - ts_from_string_tz) < delta
424
+ assert (
425
+ abs(
426
+ ts_from_string_tz.tz_localize(None)
427
+ - ts_from_method_tz.tz_localize(None)
428
+ )
429
+ < delta
430
+ )
431
+
432
+
433
+ class TestTimestampResolutionInference:
434
+ def test_construct_from_time_unit(self):
435
+ # GH#54097 only passing a time component, no date
436
+ ts = Timestamp("01:01:01.111")
437
+ assert ts.unit == "us"
438
+
439
+ def test_constructor_str_infer_reso(self):
440
+ # non-iso8601 path
441
+
442
+ # _parse_delimited_date path
443
+ ts = Timestamp("01/30/2023")
444
+ assert ts.unit == "us"
445
+
446
+ # _parse_dateabbr_string path
447
+ ts = Timestamp("2015Q1")
448
+ assert ts.unit == "us"
449
+
450
+ # dateutil_parse path
451
+ ts = Timestamp("2016-01-01 1:30:01 PM")
452
+ assert ts.unit == "us"
453
+
454
+ ts = Timestamp("2016 June 3 15:25:01.345")
455
+ assert ts.unit == "us"
456
+
457
+ ts = Timestamp("300-01-01")
458
+ assert ts.unit == "us"
459
+
460
+ ts = Timestamp("300 June 1:30:01.300")
461
+ assert ts.unit == "us"
462
+
463
+ # dateutil path -> don't drop trailing zeros
464
+ ts = Timestamp("01-01-2013T00:00:00.000000000+0000")
465
+ assert ts.unit == "ns"
466
+
467
+ ts = Timestamp("2016/01/02 03:04:05.001000 UTC")
468
+ assert ts.unit == "us"
469
+
470
+ # higher-than-nanosecond -> we drop the trailing bits
471
+ ts = Timestamp("01-01-2013T00:00:00.000000002100+0000")
472
+ assert ts == Timestamp("01-01-2013T00:00:00.000000002+0000")
473
+ assert ts.unit == "ns"
474
+
475
+ # GH#56208 minute reso through the ISO8601 path with tz offset
476
+ ts = Timestamp("2020-01-01 00:00+00:00")
477
+ assert ts.unit == "us"
478
+
479
+ ts = Timestamp("2020-01-01 00+00:00")
480
+ assert ts.unit == "us"
481
+
482
+ @pytest.mark.parametrize("method", ["now", "today"])
483
+ def test_now_today_unit(self, method):
484
+ # GH#55879
485
+ ts_from_method = getattr(Timestamp, method)()
486
+ ts_from_string = Timestamp(method)
487
+ assert ts_from_method.unit == ts_from_string.unit == "us"
488
+
489
+
490
+ class TestTimestampConstructors:
491
+ def test_disallow_dt64_with_weird_unit(self):
492
+ # GH#25611
493
+ dt64 = np.datetime64(1, "500m")
494
+ msg = "np.datetime64 objects with units containing a multiplier"
495
+ with pytest.raises(ValueError, match=msg):
496
+ Timestamp(dt64)
497
+
498
+ def test_weekday_but_no_day_raises(self):
499
+ # GH#52659
500
+ msg = "Parsing datetimes with weekday but no day information is not supported"
501
+ with pytest.raises(ValueError, match=msg):
502
+ Timestamp("2023 Sept Thu")
503
+
504
+ def test_construct_from_string_invalid_raises(self):
505
+ # dateutil (weirdly) parses "200622-12-31" as
506
+ # datetime(2022, 6, 20, 12, 0, tzinfo=tzoffset(None, -111600)
507
+ # which besides being mis-parsed, is a tzoffset that will cause
508
+ # str(ts) to raise ValueError. Ensure we raise in the constructor
509
+ # instead.
510
+ # see test_to_datetime_malformed_raise for analogous to_datetime test
511
+ with pytest.raises(ValueError, match="gives an invalid tzoffset"):
512
+ Timestamp("200622-12-31")
513
+
514
+ def test_constructor_from_iso8601_str_with_offset_reso(self):
515
+ # GH#49737
516
+ ts = Timestamp("2016-01-01 04:05:06-01:00")
517
+ assert ts.unit == "us"
518
+
519
+ ts = Timestamp("2016-01-01 04:05:06.000-01:00")
520
+ assert ts.unit == "us"
521
+
522
+ ts = Timestamp("2016-01-01 04:05:06.000000-01:00")
523
+ assert ts.unit == "us"
524
+
525
+ ts = Timestamp("2016-01-01 04:05:06.000000001-01:00")
526
+ assert ts.unit == "ns"
527
+
528
+ def test_constructor_from_date_second_reso(self):
529
+ # GH#49034 constructing from a pydate object gets lowest supported
530
+ # reso, i.e. seconds
531
+ obj = date(2012, 9, 1)
532
+ ts = Timestamp(obj)
533
+ assert ts.unit == "s"
534
+
535
+ def test_constructor_datetime64_with_tz(self):
536
+ # GH#42288, GH#24559
537
+ dt = np.datetime64("1970-01-01 05:00:00")
538
+ tzstr = "UTC+05:00"
539
+
540
+ # pre-2.0 this interpreted dt as a UTC time. in 2.0 this is treated
541
+ # as a wall-time, consistent with DatetimeIndex behavior
542
+ ts = Timestamp(dt, tz=tzstr)
543
+
544
+ alt = Timestamp(dt).tz_localize(tzstr)
545
+ assert ts == alt
546
+ assert ts.hour == 5
547
+
548
+ def test_constructor(self):
549
+ base_str = "2014-07-01 09:00"
550
+ base_dt = datetime(2014, 7, 1, 9)
551
+ base_expected = 1_404_205_200_000_000_000
552
+
553
+ # confirm base representation is correct
554
+ assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
555
+
556
+ tests = [
557
+ (base_str, base_dt, base_expected),
558
+ (
559
+ "2014-07-01 10:00",
560
+ datetime(2014, 7, 1, 10),
561
+ base_expected + 3600 * 1_000_000_000,
562
+ ),
563
+ (
564
+ "2014-07-01 09:00:00.000008000",
565
+ datetime(2014, 7, 1, 9, 0, 0, 8),
566
+ base_expected + 8000,
567
+ ),
568
+ (
569
+ "2014-07-01 09:00:00.000000005",
570
+ Timestamp("2014-07-01 09:00:00.000000005"),
571
+ base_expected + 5,
572
+ ),
573
+ ]
574
+
575
+ timezones = [
576
+ (None, 0),
577
+ ("UTC", 0),
578
+ (timezone.utc, 0),
579
+ ("Asia/Tokyo", 9),
580
+ ("US/Eastern", -4),
581
+ ("dateutil/US/Pacific", -7),
582
+ (timezone(timedelta(hours=-3)), -3),
583
+ (dateutil.tz.tzoffset(None, 18000), 5),
584
+ ]
585
+
586
+ for date_str, date_obj, expected in tests:
587
+ for result in [Timestamp(date_str), Timestamp(date_obj)]:
588
+ result = result.as_unit("ns") # test originally written before non-nano
589
+ # only with timestring
590
+ assert result.as_unit("ns")._value == expected
591
+
592
+ # re-creation shouldn't affect to internal value
593
+ result = Timestamp(result)
594
+ assert result.as_unit("ns")._value == expected
595
+
596
+ # with timezone
597
+ for tz, offset in timezones:
598
+ for result in [Timestamp(date_str, tz=tz), Timestamp(date_obj, tz=tz)]:
599
+ result = result.as_unit(
600
+ "ns"
601
+ ) # test originally written before non-nano
602
+ expected_tz = expected - offset * 3600 * 1_000_000_000
603
+ assert result.as_unit("ns")._value == expected_tz
604
+
605
+ # should preserve tz
606
+ result = Timestamp(result)
607
+ assert result.as_unit("ns")._value == expected_tz
608
+
609
+ # should convert to UTC
610
+ if tz is not None:
611
+ result = Timestamp(result).tz_convert("UTC")
612
+ else:
613
+ result = Timestamp(result, tz="UTC")
614
+ expected_utc = expected - offset * 3600 * 1_000_000_000
615
+ assert result.as_unit("ns")._value == expected_utc
616
+
617
+ def test_constructor_with_stringoffset(self):
618
+ # GH 7833
619
+ base_str = "2014-07-01 11:00:00+02:00"
620
+ base_dt = datetime(2014, 7, 1, 9)
621
+ base_expected = 1_404_205_200_000_000_000
622
+
623
+ # confirm base representation is correct
624
+ assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
625
+
626
+ tests = [
627
+ (base_str, base_expected),
628
+ ("2014-07-01 12:00:00+02:00", base_expected + 3600 * 1_000_000_000),
629
+ ("2014-07-01 11:00:00.000008000+02:00", base_expected + 8000),
630
+ ("2014-07-01 11:00:00.000000005+02:00", base_expected + 5),
631
+ ]
632
+
633
+ timezones = [
634
+ ("UTC", 0),
635
+ (timezone.utc, 0),
636
+ ("Asia/Tokyo", 9),
637
+ ("US/Eastern", -4),
638
+ ("dateutil/US/Pacific", -7),
639
+ (timezone(timedelta(hours=-3)), -3),
640
+ (dateutil.tz.tzoffset(None, 18000), 5),
641
+ ]
642
+
643
+ for date_str, expected in tests:
644
+ for result in [Timestamp(date_str)]:
645
+ # only with timestring
646
+ assert result.as_unit("ns")._value == expected
647
+
648
+ # re-creation shouldn't affect to internal value
649
+ result = Timestamp(result)
650
+ assert result.as_unit("ns")._value == expected
651
+
652
+ # with timezone
653
+ for tz, offset in timezones:
654
+ result = Timestamp(date_str, tz=tz)
655
+ expected_tz = expected
656
+ assert result.as_unit("ns")._value == expected_tz
657
+
658
+ # should preserve tz
659
+ result = Timestamp(result)
660
+ assert result.as_unit("ns")._value == expected_tz
661
+
662
+ # should convert to UTC
663
+ result = Timestamp(result).tz_convert("UTC")
664
+ expected_utc = expected
665
+ assert result.as_unit("ns")._value == expected_utc
666
+
667
+ # This should be 2013-11-01 05:00 in UTC
668
+ # converted to Chicago tz
669
+ result = Timestamp("2013-11-01 00:00:00-0500", tz="America/Chicago")
670
+ assert result._value == Timestamp("2013-11-01 05:00")._value
671
+ expected = "Timestamp('2013-11-01 00:00:00-0500', tz='America/Chicago')"
672
+ assert repr(result) == expected
673
+ assert result == eval(repr(result))
674
+
675
+ # This should be 2013-11-01 05:00 in UTC
676
+ # converted to Tokyo tz (+09:00)
677
+ result = Timestamp("2013-11-01 00:00:00-0500", tz="Asia/Tokyo")
678
+ assert result._value == Timestamp("2013-11-01 05:00")._value
679
+ expected = "Timestamp('2013-11-01 14:00:00+0900', tz='Asia/Tokyo')"
680
+ assert repr(result) == expected
681
+ assert result == eval(repr(result))
682
+
683
+ # GH11708
684
+ # This should be 2015-11-18 10:00 in UTC
685
+ # converted to Asia/Katmandu
686
+ result = Timestamp("2015-11-18 15:45:00+05:45", tz="Asia/Katmandu")
687
+ assert result._value == Timestamp("2015-11-18 10:00")._value
688
+ expected = "Timestamp('2015-11-18 15:45:00+0545', tz='Asia/Katmandu')"
689
+ assert repr(result) == expected
690
+ assert result == eval(repr(result))
691
+
692
+ # This should be 2015-11-18 10:00 in UTC
693
+ # converted to Asia/Kolkata
694
+ result = Timestamp("2015-11-18 15:30:00+05:30", tz="Asia/Kolkata")
695
+ assert result._value == Timestamp("2015-11-18 10:00")._value
696
+ expected = "Timestamp('2015-11-18 15:30:00+0530', tz='Asia/Kolkata')"
697
+ assert repr(result) == expected
698
+ assert result == eval(repr(result))
699
+
700
+ def test_constructor_invalid(self):
701
+ msg = "Cannot convert input"
702
+ with pytest.raises(TypeError, match=msg):
703
+ Timestamp(slice(2))
704
+ msg = "Cannot convert Period"
705
+ with pytest.raises(ValueError, match=msg):
706
+ Timestamp(Period("1000-01-01"))
707
+
708
+ def test_constructor_invalid_tz(self):
709
+ # GH#17690
710
+ msg = (
711
+ "Argument 'tzinfo' has incorrect type "
712
+ r"\(expected datetime.tzinfo, got str\)"
713
+ )
714
+ with pytest.raises(TypeError, match=msg):
715
+ Timestamp("2017-10-22", tzinfo="US/Eastern")
716
+
717
+ msg = "at most one of"
718
+ with pytest.raises(ValueError, match=msg):
719
+ Timestamp("2017-10-22", tzinfo=timezone.utc, tz="UTC")
720
+
721
+ msg = "Cannot pass a date attribute keyword argument when passing a date string"
722
+ with pytest.raises(ValueError, match=msg):
723
+ # GH#5168
724
+ # case where user tries to pass tz as an arg, not kwarg, gets
725
+ # interpreted as `year`
726
+ Timestamp("2012-01-01", "US/Pacific")
727
+
728
+ def test_constructor_tz_or_tzinfo(self):
729
+ # GH#17943, GH#17690, GH#5168
730
+ stamps = [
731
+ Timestamp(year=2017, month=10, day=22, tz="UTC"),
732
+ Timestamp(year=2017, month=10, day=22, tzinfo=timezone.utc),
733
+ Timestamp(year=2017, month=10, day=22, tz=timezone.utc),
734
+ Timestamp(datetime(2017, 10, 22), tzinfo=timezone.utc),
735
+ Timestamp(datetime(2017, 10, 22), tz="UTC"),
736
+ Timestamp(datetime(2017, 10, 22), tz=timezone.utc),
737
+ ]
738
+ assert all(ts == stamps[0] for ts in stamps)
739
+
740
+ @pytest.mark.parametrize(
741
+ "result",
742
+ [
743
+ Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), nanosecond=1),
744
+ Timestamp(
745
+ year=2000,
746
+ month=1,
747
+ day=2,
748
+ hour=3,
749
+ minute=4,
750
+ second=5,
751
+ microsecond=6,
752
+ nanosecond=1,
753
+ ),
754
+ Timestamp(
755
+ year=2000,
756
+ month=1,
757
+ day=2,
758
+ hour=3,
759
+ minute=4,
760
+ second=5,
761
+ microsecond=6,
762
+ nanosecond=1,
763
+ tz="UTC",
764
+ ),
765
+ Timestamp(2000, 1, 2, 3, 4, 5, 6, None, nanosecond=1),
766
+ Timestamp(2000, 1, 2, 3, 4, 5, 6, tz=timezone.utc, nanosecond=1),
767
+ ],
768
+ )
769
+ def test_constructor_nanosecond(self, result):
770
+ # GH 18898
771
+ # As of 2.0 (GH 49416), nanosecond should not be accepted positionally
772
+ expected = Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), tz=result.tz)
773
+ expected = expected + Timedelta(nanoseconds=1)
774
+ assert result == expected
775
+
776
+ @pytest.mark.parametrize("z", ["Z0", "Z00"])
777
+ def test_constructor_invalid_Z0_isostring(self, z):
778
+ # GH 8910
779
+ msg = f"Unknown datetime string format, unable to parse: 2014-11-02 01:00{z}"
780
+ with pytest.raises(ValueError, match=msg):
781
+ Timestamp(f"2014-11-02 01:00{z}")
782
+
783
+ def test_out_of_bounds_integer_value(self):
784
+ # GH#26651 check that we raise OutOfBoundsDatetime, not OverflowError
785
+ msg = str(Timestamp.max._value * 2)
786
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
787
+ Timestamp(Timestamp.max._value * 2)
788
+ msg = str(Timestamp.min._value * 2)
789
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
790
+ Timestamp(Timestamp.min._value * 2)
791
+
792
+ def test_out_of_bounds_value(self):
793
+ one_us = np.timedelta64(1, "ns").astype("timedelta64[us]")
794
+
795
+ # By definition we can't go out of bounds in [ns], so we
796
+ # convert the datetime64s to [us] so we can go out of bounds
797
+ min_ts_us = np.datetime64(Timestamp.min).astype("M8[us]") + one_us
798
+ max_ts_us = np.datetime64(Timestamp.max).astype("M8[us]")
799
+
800
+ # No error for the min/max datetimes
801
+ Timestamp(min_ts_us)
802
+ Timestamp(max_ts_us)
803
+
804
+ # We used to raise on these before supporting non-nano
805
+ us_val = NpyDatetimeUnit.NPY_FR_us.value
806
+ assert Timestamp(min_ts_us - one_us)._creso == us_val
807
+ assert Timestamp(max_ts_us + one_us)._creso == us_val
808
+
809
+ # https://github.com/numpy/numpy/issues/22346 for why
810
+ # we can't use the same construction as above with minute resolution
811
+
812
+ # too_low, too_high are the _just_ outside the range of M8[s]
813
+ too_low = np.datetime64("-292277022657-01-27T08:29", "m")
814
+ too_high = np.datetime64("292277026596-12-04T15:31", "m")
815
+
816
+ msg = "Out of bounds"
817
+ # One us less than the minimum is an error
818
+ with pytest.raises(ValueError, match=msg):
819
+ Timestamp(too_low)
820
+
821
+ # One us more than the maximum is an error
822
+ with pytest.raises(ValueError, match=msg):
823
+ Timestamp(too_high)
824
+
825
+ def test_out_of_bounds_string(self):
826
+ msg = "Cannot cast .* to unit='ns' without overflow"
827
+ with pytest.raises(ValueError, match=msg):
828
+ Timestamp("1676-01-01").as_unit("ns")
829
+ with pytest.raises(ValueError, match=msg):
830
+ Timestamp("2263-01-01").as_unit("ns")
831
+
832
+ ts = Timestamp("2263-01-01")
833
+ assert ts.unit == "us"
834
+
835
+ ts = Timestamp("1676-01-01")
836
+ assert ts.unit == "us"
837
+
838
+ def test_barely_out_of_bounds(self):
839
+ # GH#19529
840
+ # GH#19382 close enough to bounds that dropping nanos would result
841
+ # in an in-bounds datetime
842
+ msg = "Out of bounds nanosecond timestamp: 2262-04-11 23:47:16"
843
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
844
+ Timestamp("2262-04-11 23:47:16.854775808")
845
+
846
+ def test_bounds_with_different_units(self):
847
+ out_of_bounds_dates = ("1677-09-21", "2262-04-12")
848
+
849
+ time_units = ("D", "h", "m", "s", "ms", "us")
850
+
851
+ for date_string in out_of_bounds_dates:
852
+ for unit in time_units:
853
+ dt64 = np.datetime64(date_string, unit)
854
+ ts = Timestamp(dt64)
855
+ if unit in ["s", "ms", "us"]:
856
+ # We can preserve the input unit
857
+ assert ts._value == dt64.view("i8")
858
+ else:
859
+ # we chose the closest unit that we _do_ support
860
+ assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
861
+
862
+ # With more extreme cases, we can't even fit inside second resolution
863
+ info = np.iinfo(np.int64)
864
+ msg = "Out of bounds second timestamp:"
865
+ for value in [info.min + 1, info.max]:
866
+ for unit in ["D", "h", "m"]:
867
+ dt64 = np.datetime64(value, unit)
868
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
869
+ Timestamp(dt64)
870
+
871
+ in_bounds_dates = ("1677-09-23", "2262-04-11")
872
+
873
+ for date_string in in_bounds_dates:
874
+ for unit in time_units:
875
+ dt64 = np.datetime64(date_string, unit)
876
+ Timestamp(dt64)
877
+
878
+ @pytest.mark.parametrize("arg", ["001-01-01", "0001-01-01"])
879
+ def test_out_of_bounds_string_consistency(self, arg):
880
+ # GH 15829
881
+ msg = "Cannot cast 0001-01-01 00:00:00 to unit='ns' without overflow"
882
+ with pytest.raises(OutOfBoundsDatetime, match=msg):
883
+ Timestamp(arg).as_unit("ns")
884
+
885
+ ts = Timestamp(arg)
886
+ assert ts.unit == "us"
887
+ assert ts.year == ts.month == ts.day == 1
888
+
889
+ def test_min_valid(self):
890
+ # Ensure that Timestamp.min is a valid Timestamp
891
+ Timestamp(Timestamp.min)
892
+
893
+ def test_max_valid(self):
894
+ # Ensure that Timestamp.max is a valid Timestamp
895
+ Timestamp(Timestamp.max)
896
+
897
+ @pytest.mark.parametrize("offset", ["+0300", "+0200"])
898
+ def test_construct_timestamp_near_dst(self, offset):
899
+ # GH 20854
900
+ expected = Timestamp(f"2016-10-30 03:00:00{offset}", tz="Europe/Helsinki")
901
+ result = Timestamp(expected).tz_convert("Europe/Helsinki")
902
+ assert result == expected
903
+
904
+ @pytest.mark.parametrize(
905
+ "arg", ["2013/01/01 00:00:00+09:00", "2013-01-01 00:00:00+09:00"]
906
+ )
907
+ def test_construct_with_different_string_format(self, arg):
908
+ # GH 12064
909
+ result = Timestamp(arg)
910
+ expected = Timestamp(datetime(2013, 1, 1), tz=timezone(timedelta(hours=9)))
911
+ assert result == expected
912
+
913
+ @pytest.mark.parametrize("box", [datetime, Timestamp])
914
+ def test_raise_tz_and_tzinfo_in_datetime_input(self, box):
915
+ # GH 23579
916
+ kwargs = {"year": 2018, "month": 1, "day": 1, "tzinfo": timezone.utc}
917
+ msg = "Cannot pass a datetime or Timestamp"
918
+ with pytest.raises(ValueError, match=msg):
919
+ Timestamp(box(**kwargs), tz="US/Pacific")
920
+ msg = "Cannot pass a datetime or Timestamp"
921
+ with pytest.raises(ValueError, match=msg):
922
+ Timestamp(box(**kwargs), tzinfo=zoneinfo.ZoneInfo("US/Pacific"))
923
+
924
+ def test_dont_convert_dateutil_utc_to_default_utc(self):
925
+ result = Timestamp(datetime(2018, 1, 1), tz=tzutc())
926
+ expected = Timestamp(datetime(2018, 1, 1)).tz_localize(tzutc())
927
+ assert result == expected
928
+
929
+ def test_constructor_subclassed_datetime(self):
930
+ # GH 25851
931
+ # ensure that subclassed datetime works for
932
+ # Timestamp creation
933
+ class SubDatetime(datetime):
934
+ pass
935
+
936
+ data = SubDatetime(2000, 1, 1)
937
+ result = Timestamp(data)
938
+ expected = Timestamp(2000, 1, 1)
939
+ assert result == expected
940
+
941
+ def test_timestamp_constructor_tz_utc(self):
942
+ utc_stamp = Timestamp("3/11/2012 05:00", tz="utc")
943
+ assert utc_stamp.tzinfo is timezone.utc
944
+ assert utc_stamp.hour == 5
945
+
946
+ utc_stamp = Timestamp("3/11/2012 05:00").tz_localize("utc")
947
+ assert utc_stamp.hour == 5
948
+
949
+ def test_timestamp_to_datetime_tzoffset(self):
950
+ tzinfo = tzoffset(None, 7200)
951
+ expected = Timestamp("3/11/2012 04:00", tz=tzinfo)
952
+ result = Timestamp(expected.to_pydatetime())
953
+ assert expected == result
954
+
955
+ def test_timestamp_constructor_near_dst_boundary(self):
956
+ # GH#11481 & GH#15777
957
+ # Naive string timestamps were being localized incorrectly
958
+ # with tz_convert_from_utc_single instead of tz_localize_to_utc
959
+
960
+ for tz in ["Europe/Brussels", "Europe/Prague"]:
961
+ result = Timestamp("2015-10-25 01:00", tz=tz)
962
+ expected = Timestamp("2015-10-25 01:00").tz_localize(tz)
963
+ assert result == expected
964
+
965
+ msg = "Cannot infer dst time from 2015-10-25 02:00:00"
966
+ with pytest.raises(ValueError, match=msg):
967
+ Timestamp("2015-10-25 02:00", tz=tz)
968
+
969
+ result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
970
+ expected = Timestamp("2017-03-26 01:00").tz_localize("Europe/Paris")
971
+ assert result == expected
972
+
973
+ msg = "2017-03-26 02:00"
974
+ with pytest.raises(ValueError, match=msg):
975
+ Timestamp("2017-03-26 02:00", tz="Europe/Paris")
976
+
977
+ # GH#11708
978
+ naive = Timestamp("2015-11-18 10:00:00")
979
+ result = naive.tz_localize("UTC").tz_convert("Asia/Kolkata")
980
+ expected = Timestamp("2015-11-18 15:30:00+0530", tz="Asia/Kolkata")
981
+ assert result == expected
982
+
983
+ # GH#15823
984
+ result = Timestamp("2017-03-26 00:00", tz="Europe/Paris")
985
+ expected = Timestamp("2017-03-26 00:00:00+0100", tz="Europe/Paris")
986
+ assert result == expected
987
+
988
+ result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
989
+ expected = Timestamp("2017-03-26 01:00:00+0100", tz="Europe/Paris")
990
+ assert result == expected
991
+
992
+ msg = "2017-03-26 02:00"
993
+ with pytest.raises(ValueError, match=msg):
994
+ Timestamp("2017-03-26 02:00", tz="Europe/Paris")
995
+
996
+ result = Timestamp("2017-03-26 02:00:00+0100", tz="Europe/Paris")
997
+ naive = Timestamp(result.as_unit("ns")._value)
998
+ expected = naive.tz_localize("UTC").tz_convert("Europe/Paris")
999
+ assert result == expected
1000
+
1001
+ result = Timestamp("2017-03-26 03:00", tz="Europe/Paris")
1002
+ expected = Timestamp("2017-03-26 03:00:00+0200", tz="Europe/Paris")
1003
+ assert result == expected
1004
+
1005
+ @pytest.mark.parametrize(
1006
+ "tz",
1007
+ [
1008
+ "pytz/US/Eastern",
1009
+ gettz("US/Eastern"),
1010
+ "US/Eastern",
1011
+ "dateutil/US/Eastern",
1012
+ ],
1013
+ )
1014
+ def test_timestamp_constructed_by_date_and_tz(self, tz):
1015
+ # GH#2993, Timestamp cannot be constructed by datetime.date
1016
+ # and tz correctly
1017
+ if isinstance(tz, str) and tz.startswith("pytz/"):
1018
+ pytz = pytest.importorskip("pytz")
1019
+ tz = pytz.timezone(tz.removeprefix("pytz/"))
1020
+ result = Timestamp(date(2012, 3, 11), tz=tz)
1021
+
1022
+ expected = Timestamp("3/11/2012", tz=tz)
1023
+ assert result.hour == expected.hour
1024
+ assert result == expected
1025
+
1026
+ def test_explicit_tz_none(self):
1027
+ # GH#48688
1028
+ msg = "Passed data is timezone-aware, incompatible with 'tz=None'"
1029
+ with pytest.raises(ValueError, match=msg):
1030
+ Timestamp(datetime(2022, 1, 1, tzinfo=timezone.utc), tz=None)
1031
+
1032
+ with pytest.raises(ValueError, match=msg):
1033
+ Timestamp("2022-01-01 00:00:00", tzinfo=timezone.utc, tz=None)
1034
+
1035
+ with pytest.raises(ValueError, match=msg):
1036
+ Timestamp("2022-01-01 00:00:00-0400", tz=None)
1037
+
1038
+
1039
+ def test_constructor_ambiguous_dst():
1040
+ # GH 24329
1041
+ # Make sure that calling Timestamp constructor
1042
+ # on Timestamp created from ambiguous time
1043
+ # doesn't change Timestamp.value
1044
+ ts = Timestamp(1382835600000000000, tz="dateutil/Europe/London")
1045
+ expected = ts._value
1046
+ result = Timestamp(ts)._value
1047
+ assert result == expected
1048
+
1049
+
1050
+ @pytest.mark.parametrize("epoch", [1552211999999999872, 1552211999999999999])
1051
+ def test_constructor_before_dst_switch(epoch):
1052
+ # GH 31043
1053
+ # Make sure that calling Timestamp constructor
1054
+ # on time just before DST switch doesn't lead to
1055
+ # nonexistent time or value change
1056
+ ts = Timestamp(epoch, tz="dateutil/America/Los_Angeles")
1057
+ result = ts.tz.dst(ts)
1058
+ expected = timedelta(seconds=0)
1059
+ assert Timestamp(ts)._value == epoch
1060
+ assert result == expected
1061
+
1062
+
1063
+ def test_timestamp_constructor_identity():
1064
+ # Test for #30543
1065
+ expected = Timestamp("2017-01-01T12")
1066
+ result = Timestamp(expected)
1067
+ assert result is expected
1068
+
1069
+
1070
+ @pytest.mark.parametrize("nano", [-1, 1000])
1071
+ def test_timestamp_nano_range(nano):
1072
+ # GH 48255
1073
+ with pytest.raises(ValueError, match="nanosecond must be in 0..999"):
1074
+ Timestamp(year=2022, month=1, day=1, nanosecond=nano)
1075
+
1076
+
1077
+ def test_non_nano_value():
1078
+ # https://github.com/pandas-dev/pandas/issues/49076
1079
+ msg = "The 'unit' keyword is only used when"
1080
+ with tm.assert_produces_warning(UserWarning, match=msg):
1081
+ result = Timestamp("1800-01-01", unit="s").value
1082
+ # `.value` shows nanoseconds, even though unit is 's'
1083
+ assert result == -5364662400000000000
1084
+
1085
+ # out-of-nanoseconds-bounds `.value` raises informative message
1086
+ msg = (
1087
+ r"Cannot convert Timestamp to nanoseconds without overflow. "
1088
+ r"Use `.asm8.view\('i8'\)` to cast represent Timestamp in its "
1089
+ r"own unit \(here, us\).$"
1090
+ )
1091
+ ts = Timestamp("0300-01-01")
1092
+ assert ts.unit == "us"
1093
+ with pytest.raises(OverflowError, match=msg):
1094
+ ts.value
1095
+ # check that the suggested workaround actually works
1096
+ result = ts.asm8.view("i8")
1097
+ assert result == -52_700_112_000 * 10**6
1098
+
1099
+
1100
+ @pytest.mark.parametrize(
1101
+ "na_value", [None, np.nan, np.datetime64("NaT", "ns"), NaT, NA]
1102
+ )
1103
+ def test_timestamp_constructor_na_value(na_value):
1104
+ # GH45481
1105
+ result = Timestamp(na_value)
1106
+ expected = NaT
1107
+ assert result is expected
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_formats.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import (
2
+ datetime,
3
+ timezone,
4
+ )
5
+ import pprint
6
+
7
+ import dateutil.tz
8
+ import pytest
9
+
10
+ from pandas.compat import WASM
11
+
12
+ from pandas import Timestamp
13
+
14
+ ts_no_ns = Timestamp(
15
+ year=2019,
16
+ month=5,
17
+ day=18,
18
+ hour=15,
19
+ minute=17,
20
+ second=8,
21
+ microsecond=132263,
22
+ )
23
+ ts_no_ns_year1 = Timestamp(
24
+ year=1,
25
+ month=5,
26
+ day=18,
27
+ hour=15,
28
+ minute=17,
29
+ second=8,
30
+ microsecond=132263,
31
+ )
32
+ ts_ns = Timestamp(
33
+ year=2019,
34
+ month=5,
35
+ day=18,
36
+ hour=15,
37
+ minute=17,
38
+ second=8,
39
+ microsecond=132263,
40
+ nanosecond=123,
41
+ )
42
+ ts_ns_tz = Timestamp(
43
+ year=2019,
44
+ month=5,
45
+ day=18,
46
+ hour=15,
47
+ minute=17,
48
+ second=8,
49
+ microsecond=132263,
50
+ nanosecond=123,
51
+ tz="UTC",
52
+ )
53
+ ts_no_us = Timestamp(
54
+ year=2019,
55
+ month=5,
56
+ day=18,
57
+ hour=15,
58
+ minute=17,
59
+ second=8,
60
+ microsecond=0,
61
+ nanosecond=123,
62
+ )
63
+
64
+
65
+ @pytest.mark.parametrize(
66
+ "ts, timespec, expected_iso",
67
+ [
68
+ (ts_no_ns, "auto", "2019-05-18T15:17:08.132263"),
69
+ (ts_no_ns, "seconds", "2019-05-18T15:17:08"),
70
+ (ts_no_ns, "nanoseconds", "2019-05-18T15:17:08.132263000"),
71
+ (ts_no_ns_year1, "seconds", "0001-05-18T15:17:08"),
72
+ (ts_no_ns_year1, "nanoseconds", "0001-05-18T15:17:08.132263000"),
73
+ (ts_ns, "auto", "2019-05-18T15:17:08.132263123"),
74
+ (ts_ns, "hours", "2019-05-18T15"),
75
+ (ts_ns, "minutes", "2019-05-18T15:17"),
76
+ (ts_ns, "seconds", "2019-05-18T15:17:08"),
77
+ (ts_ns, "milliseconds", "2019-05-18T15:17:08.132"),
78
+ (ts_ns, "microseconds", "2019-05-18T15:17:08.132263"),
79
+ (ts_ns, "nanoseconds", "2019-05-18T15:17:08.132263123"),
80
+ (ts_ns_tz, "auto", "2019-05-18T15:17:08.132263123+00:00"),
81
+ (ts_ns_tz, "hours", "2019-05-18T15+00:00"),
82
+ (ts_ns_tz, "minutes", "2019-05-18T15:17+00:00"),
83
+ (ts_ns_tz, "seconds", "2019-05-18T15:17:08+00:00"),
84
+ (ts_ns_tz, "milliseconds", "2019-05-18T15:17:08.132+00:00"),
85
+ (ts_ns_tz, "microseconds", "2019-05-18T15:17:08.132263+00:00"),
86
+ (ts_ns_tz, "nanoseconds", "2019-05-18T15:17:08.132263123+00:00"),
87
+ (ts_no_us, "auto", "2019-05-18T15:17:08.000000123"),
88
+ ],
89
+ )
90
+ def test_isoformat(ts, timespec, expected_iso):
91
+ assert ts.isoformat(timespec=timespec) == expected_iso
92
+
93
+
94
+ class TestTimestampRendering:
95
+ @pytest.mark.parametrize(
96
+ "tz", ["UTC", "Asia/Tokyo", "US/Eastern", "dateutil/America/Los_Angeles"]
97
+ )
98
+ @pytest.mark.parametrize("freq", ["D", "M", "S", "N"])
99
+ @pytest.mark.parametrize(
100
+ "date", ["2014-03-07", "2014-01-01 09:00", "2014-01-01 00:00:00.000000001"]
101
+ )
102
+ @pytest.mark.skipif(WASM, reason="tzset is not available on WASM")
103
+ def test_repr(self, date, freq, tz):
104
+ # avoid to match with timezone name
105
+ freq_repr = f"'{freq}'"
106
+ if tz.startswith("dateutil"):
107
+ tz_repr = tz.replace("dateutil", "")
108
+ else:
109
+ tz_repr = tz
110
+
111
+ date_only = Timestamp(date)
112
+ assert date in repr(date_only)
113
+ assert tz_repr not in repr(date_only)
114
+ assert freq_repr not in repr(date_only)
115
+ assert date_only == eval(repr(date_only))
116
+
117
+ date_tz = Timestamp(date, tz=tz)
118
+ assert date in repr(date_tz)
119
+ assert tz_repr in repr(date_tz)
120
+ assert freq_repr not in repr(date_tz)
121
+ assert date_tz == eval(repr(date_tz))
122
+
123
+ def test_repr_utcoffset(self):
124
+ # This can cause the tz field to be populated, but it's redundant to
125
+ # include this information in the date-string.
126
+ date_with_utc_offset = Timestamp("2014-03-13 00:00:00-0400")
127
+ assert "2014-03-13 00:00:00-0400" in repr(date_with_utc_offset)
128
+ assert "tzoffset" not in repr(date_with_utc_offset)
129
+ assert "UTC-04:00" in repr(date_with_utc_offset)
130
+ expr = repr(date_with_utc_offset)
131
+ assert date_with_utc_offset == eval(expr)
132
+
133
+ def test_timestamp_repr_pre1900(self):
134
+ # pre-1900
135
+ stamp = Timestamp("1850-01-01", tz="US/Eastern")
136
+ repr(stamp)
137
+
138
+ iso8601 = "1850-01-01 01:23:45.012345"
139
+ stamp = Timestamp(iso8601, tz="US/Eastern")
140
+ result = repr(stamp)
141
+ assert iso8601 in result
142
+
143
+ def test_pprint(self):
144
+ # GH#12622
145
+ nested_obj = {"foo": 1, "bar": [{"w": {"a": Timestamp("2011-01-01")}}] * 10}
146
+ result = pprint.pformat(nested_obj, width=50)
147
+ expected = r"""{'bar': [{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
148
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
149
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
150
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
151
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
152
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
153
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
154
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
155
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}},
156
+ {'w': {'a': Timestamp('2011-01-01 00:00:00')}}],
157
+ 'foo': 1}"""
158
+ assert result == expected
159
+
160
+ def test_to_timestamp_repr_is_code(self):
161
+ zs = [
162
+ Timestamp("99-04-17 00:00:00", tz="UTC"),
163
+ Timestamp("2001-04-17 00:00:00", tz="UTC"),
164
+ Timestamp("2001-04-17 00:00:00", tz="America/Los_Angeles"),
165
+ Timestamp("2001-04-17 00:00:00", tz=None),
166
+ ]
167
+ for z in zs:
168
+ assert eval(repr(z)) == z
169
+
170
+ def test_repr_matches_pydatetime_no_tz(self):
171
+ dt_date = datetime(2013, 1, 2)
172
+ assert str(dt_date) == str(Timestamp(dt_date))
173
+
174
+ dt_datetime = datetime(2013, 1, 2, 12, 1, 3)
175
+ assert str(dt_datetime) == str(Timestamp(dt_datetime))
176
+
177
+ dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45)
178
+ assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
179
+
180
+ ts_nanos_only = Timestamp(200)
181
+ assert str(ts_nanos_only) == "1970-01-01 00:00:00.000000200"
182
+
183
+ ts_nanos_micros = Timestamp(1200)
184
+ assert str(ts_nanos_micros) == "1970-01-01 00:00:00.000001200"
185
+
186
+ def test_repr_matches_pydatetime_tz_stdlib(self):
187
+ dt_date = datetime(2013, 1, 2, tzinfo=timezone.utc)
188
+ assert str(dt_date) == str(Timestamp(dt_date))
189
+
190
+ dt_datetime = datetime(2013, 1, 2, 12, 1, 3, tzinfo=timezone.utc)
191
+ assert str(dt_datetime) == str(Timestamp(dt_datetime))
192
+
193
+ dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45, tzinfo=timezone.utc)
194
+ assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
195
+
196
+ def test_repr_matches_pydatetime_tz_dateutil(self):
197
+ utc = dateutil.tz.tzutc()
198
+
199
+ dt_date = datetime(2013, 1, 2, tzinfo=utc)
200
+ assert str(dt_date) == str(Timestamp(dt_date))
201
+
202
+ dt_datetime = datetime(2013, 1, 2, 12, 1, 3, tzinfo=utc)
203
+ assert str(dt_datetime) == str(Timestamp(dt_datetime))
204
+
205
+ dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45, tzinfo=utc)
206
+ assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py ADDED
@@ -0,0 +1,938 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """test the scalar Timestamp"""
2
+
3
+ import calendar
4
+ from datetime import (
5
+ datetime,
6
+ timedelta,
7
+ timezone,
8
+ )
9
+ import locale
10
+ import time
11
+ import unicodedata
12
+ import zoneinfo
13
+
14
+ from dateutil.tz import (
15
+ tzlocal,
16
+ tzutc,
17
+ )
18
+ from hypothesis import (
19
+ given,
20
+ strategies as st,
21
+ )
22
+ import numpy as np
23
+ import pytest
24
+
25
+ from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
26
+ from pandas._libs.tslibs.timezones import (
27
+ dateutil_gettz as gettz,
28
+ get_timezone,
29
+ maybe_get_tz,
30
+ tz_compare,
31
+ )
32
+ from pandas.compat import IS64
33
+ from pandas.compat.numpy import (
34
+ is_numpy_dev,
35
+ np_version_gt2_5,
36
+ )
37
+ from pandas.errors import Pandas4Warning
38
+
39
+ from pandas import (
40
+ NaT,
41
+ Timedelta,
42
+ Timestamp,
43
+ )
44
+ import pandas._testing as tm
45
+
46
+ from pandas.tseries import offsets
47
+ from pandas.tseries.frequencies import to_offset
48
+
49
+
50
+ class TestTimestampProperties:
51
+ def test_properties_business(self):
52
+ freq = to_offset("B")
53
+
54
+ ts = Timestamp("2017-10-01")
55
+ assert ts.dayofweek == 6
56
+ assert ts.day_of_week == 6
57
+ assert ts.is_month_start # not a weekday
58
+ assert not freq.is_month_start(ts)
59
+ assert freq.is_month_start(ts + Timedelta(days=1))
60
+ assert not freq.is_quarter_start(ts)
61
+ assert freq.is_quarter_start(ts + Timedelta(days=1))
62
+
63
+ ts = Timestamp("2017-09-30")
64
+ assert ts.dayofweek == 5
65
+ assert ts.day_of_week == 5
66
+ assert ts.is_month_end
67
+ assert not freq.is_month_end(ts)
68
+ assert freq.is_month_end(ts - Timedelta(days=1))
69
+ assert ts.is_quarter_end
70
+ assert not freq.is_quarter_end(ts)
71
+ assert freq.is_quarter_end(ts - Timedelta(days=1))
72
+
73
+ @pytest.mark.parametrize(
74
+ "attr, expected",
75
+ [
76
+ ["year", 2014],
77
+ ["month", 12],
78
+ ["day", 31],
79
+ ["hour", 23],
80
+ ["minute", 59],
81
+ ["second", 0],
82
+ ["microsecond", 0],
83
+ ["nanosecond", 0],
84
+ ["dayofweek", 2],
85
+ ["day_of_week", 2],
86
+ ["quarter", 4],
87
+ ["dayofyear", 365],
88
+ ["day_of_year", 365],
89
+ ["week", 1],
90
+ ["daysinmonth", 31],
91
+ ],
92
+ )
93
+ @pytest.mark.parametrize("tz", [None, "US/Eastern"])
94
+ def test_fields(self, attr, expected, tz):
95
+ # GH 10050
96
+ # GH 13303
97
+ ts = Timestamp("2014-12-31 23:59:00", tz=tz)
98
+ result = getattr(ts, attr)
99
+ # that we are int like
100
+ assert isinstance(result, int)
101
+ assert result == expected
102
+
103
+ @pytest.mark.parametrize("tz", [None, "US/Eastern"])
104
+ def test_millisecond_raises(self, tz):
105
+ ts = Timestamp("2014-12-31 23:59:00", tz=tz)
106
+ msg = "'Timestamp' object has no attribute 'millisecond'"
107
+ with pytest.raises(AttributeError, match=msg):
108
+ ts.millisecond
109
+
110
+ @pytest.mark.parametrize(
111
+ "start", ["is_month_start", "is_quarter_start", "is_year_start"]
112
+ )
113
+ @pytest.mark.parametrize("tz", [None, "US/Eastern"])
114
+ def test_is_start(self, start, tz):
115
+ ts = Timestamp("2014-01-01 00:00:00", tz=tz)
116
+ assert getattr(ts, start)
117
+
118
+ @pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"])
119
+ @pytest.mark.parametrize("tz", [None, "US/Eastern"])
120
+ def test_is_end(self, end, tz):
121
+ ts = Timestamp("2014-12-31 23:59:59", tz=tz)
122
+ assert getattr(ts, end)
123
+
124
+ # GH 12806
125
+ @pytest.mark.parametrize("tz", [None, "EST"])
126
+ @pytest.mark.parametrize(
127
+ "time_locale",
128
+ [None, *tm.get_locales()],
129
+ )
130
+ def test_names(self, tz, time_locale):
131
+ # GH 17354
132
+ # Test .day_name(), .month_name
133
+ data = Timestamp("2017-08-28 23:00:00", tz=tz)
134
+ if time_locale is None:
135
+ expected_day = "Monday"
136
+ expected_month = "August"
137
+ else:
138
+ with tm.set_locale(time_locale, locale.LC_TIME):
139
+ expected_day = calendar.day_name[0].capitalize()
140
+ expected_month = calendar.month_name[8].capitalize()
141
+
142
+ result_day = data.day_name(time_locale)
143
+ result_month = data.month_name(time_locale)
144
+
145
+ # Work around https://github.com/pandas-dev/pandas/issues/22342
146
+ # different normalizations
147
+ expected_day = unicodedata.normalize("NFD", expected_day)
148
+ expected_month = unicodedata.normalize("NFD", expected_month)
149
+
150
+ result_day = unicodedata.normalize("NFD", result_day)
151
+ result_month = unicodedata.normalize("NFD", result_month)
152
+
153
+ assert result_day == expected_day
154
+ assert result_month == expected_month
155
+
156
+ # Test NaT
157
+ nan_ts = Timestamp(NaT)
158
+ assert np.isnan(nan_ts.day_name(time_locale))
159
+ assert np.isnan(nan_ts.month_name(time_locale))
160
+
161
+ def test_is_leap_year(self, tz_naive_fixture):
162
+ tz = tz_naive_fixture
163
+ if not IS64 and tz == tzlocal():
164
+ # https://github.com/dateutil/dateutil/issues/197
165
+ pytest.skip(
166
+ "tzlocal() on a 32 bit platform causes internal overflow errors"
167
+ )
168
+ # GH 13727
169
+ dt = Timestamp("2000-01-01 00:00:00", tz=tz)
170
+ assert dt.is_leap_year
171
+ assert isinstance(dt.is_leap_year, bool)
172
+
173
+ dt = Timestamp("1999-01-01 00:00:00", tz=tz)
174
+ assert not dt.is_leap_year
175
+
176
+ dt = Timestamp("2004-01-01 00:00:00", tz=tz)
177
+ assert dt.is_leap_year
178
+
179
+ dt = Timestamp("2100-01-01 00:00:00", tz=tz)
180
+ assert not dt.is_leap_year
181
+
182
+ def test_woy_boundary(self):
183
+ # make sure weeks at year boundaries are correct
184
+ d = datetime(2013, 12, 31)
185
+ result = Timestamp(d).week
186
+ expected = 1 # ISO standard
187
+ assert result == expected
188
+
189
+ d = datetime(2008, 12, 28)
190
+ result = Timestamp(d).week
191
+ expected = 52 # ISO standard
192
+ assert result == expected
193
+
194
+ d = datetime(2009, 12, 31)
195
+ result = Timestamp(d).week
196
+ expected = 53 # ISO standard
197
+ assert result == expected
198
+
199
+ d = datetime(2010, 1, 1)
200
+ result = Timestamp(d).week
201
+ expected = 53 # ISO standard
202
+ assert result == expected
203
+
204
+ d = datetime(2010, 1, 3)
205
+ result = Timestamp(d).week
206
+ expected = 53 # ISO standard
207
+ assert result == expected
208
+
209
+ result = np.array(
210
+ [
211
+ Timestamp(datetime(*args)).week
212
+ for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)]
213
+ ]
214
+ )
215
+ assert (result == [52, 52, 53, 53]).all()
216
+
217
+ def test_resolution(self):
218
+ # GH#21336, GH#21365
219
+ dt = Timestamp("2100-01-01 00:00:00.000000000")
220
+ assert dt.resolution == Timedelta(nanoseconds=1)
221
+
222
+ # Check that the attribute is available on the class, mirroring
223
+ # the stdlib datetime behavior
224
+ assert Timestamp.resolution == Timedelta(nanoseconds=1)
225
+
226
+ assert dt.as_unit("us").resolution == Timedelta(microseconds=1)
227
+ assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
228
+ assert dt.as_unit("s").resolution == Timedelta(seconds=1)
229
+
230
+ @pytest.mark.parametrize(
231
+ "date_string, expected",
232
+ [
233
+ ("0000-2-29", 1),
234
+ ("0000-3-1", 2),
235
+ ("1582-10-14", 3),
236
+ ("-0040-1-1", 4),
237
+ ("2023-06-18", 6),
238
+ ],
239
+ )
240
+ def test_dow_historic(self, date_string, expected):
241
+ # GH 53738
242
+ ts = Timestamp(date_string)
243
+ dow = ts.weekday()
244
+ assert dow == expected
245
+
246
+ @pytest.mark.slow
247
+ @given(
248
+ ts=st.datetimes(),
249
+ sign=st.sampled_from(["-", ""]),
250
+ )
251
+ def test_dow_parametric(self, ts, sign):
252
+ # GH 53738
253
+ ts = (
254
+ f"{sign}{str(ts.year).zfill(4)}"
255
+ f"-{str(ts.month).zfill(2)}"
256
+ f"-{str(ts.day).zfill(2)}"
257
+ )
258
+ result = Timestamp(ts).weekday()
259
+ expected = (
260
+ (np.datetime64(ts) - np.datetime64("1970-01-01")).astype("int64") - 4
261
+ ) % 7
262
+ assert result == expected
263
+
264
+
265
+ class TestTimestamp:
266
+ @pytest.mark.parametrize("tz", [None, zoneinfo.ZoneInfo("US/Pacific")])
267
+ def test_disallow_setting_tz(self, tz):
268
+ # GH#3746
269
+ ts = Timestamp("2010")
270
+ msg = "Cannot directly set timezone"
271
+ with pytest.raises(AttributeError, match=msg):
272
+ ts.tz = tz
273
+
274
+ def test_default_to_stdlib_utc(self):
275
+ msg = "Timestamp.utcnow is deprecated"
276
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
277
+ assert Timestamp.utcnow().tz is timezone.utc
278
+ assert Timestamp.now("UTC").tz is timezone.utc
279
+ assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc
280
+
281
+ def test_tz(self):
282
+ tstr = "2014-02-01 09:00"
283
+ ts = Timestamp(tstr)
284
+ local = ts.tz_localize("Asia/Tokyo")
285
+ assert local.hour == 9
286
+ assert local == Timestamp(tstr, tz="Asia/Tokyo")
287
+ conv = local.tz_convert("US/Eastern")
288
+ assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern")
289
+ assert conv.hour == 19
290
+
291
+ # preserves nanosecond
292
+ ts = Timestamp(tstr) + offsets.Nano(5)
293
+ local = ts.tz_localize("Asia/Tokyo")
294
+ assert local.hour == 9
295
+ assert local.nanosecond == 5
296
+ conv = local.tz_convert("US/Eastern")
297
+ assert conv.nanosecond == 5
298
+ assert conv.hour == 19
299
+
300
+ def test_utc_z_designator(self):
301
+ assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is timezone.utc
302
+
303
+ def test_asm8(self):
304
+ ns = [Timestamp.min._value, Timestamp.max._value, 1000]
305
+
306
+ for n in ns:
307
+ assert (
308
+ Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n
309
+ )
310
+
311
+ assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8")
312
+
313
+ def test_class_ops(self):
314
+ def compare(x, y):
315
+ assert int((Timestamp(x)._value - Timestamp(y)._value) / 1e9) == 0
316
+
317
+ compare(Timestamp.now(), datetime.now())
318
+ compare(Timestamp.now("UTC"), datetime.now(timezone.utc))
319
+ compare(Timestamp.now("UTC"), datetime.now(tzutc()))
320
+ msg = "Timestamp.utcnow is deprecated"
321
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
322
+ compare(Timestamp.utcnow(), datetime.now(timezone.utc))
323
+ compare(Timestamp.today(), datetime.today())
324
+ current_time = calendar.timegm(datetime.now().utctimetuple())
325
+
326
+ msg = "Timestamp.utcfromtimestamp is deprecated"
327
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
328
+ ts_utc = Timestamp.utcfromtimestamp(current_time)
329
+ assert ts_utc.timestamp() == current_time
330
+ compare(
331
+ Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
332
+ )
333
+ compare(
334
+ # Support tz kwarg in Timestamp.fromtimestamp
335
+ Timestamp.fromtimestamp(current_time, "UTC"),
336
+ datetime.fromtimestamp(current_time, timezone.utc),
337
+ )
338
+ compare(
339
+ # Support tz kwarg in Timestamp.fromtimestamp
340
+ Timestamp.fromtimestamp(current_time, tz="UTC"),
341
+ datetime.fromtimestamp(current_time, timezone.utc),
342
+ )
343
+
344
+ date_component = datetime.now(timezone.utc)
345
+ time_component = (date_component + timedelta(minutes=10)).time()
346
+ compare(
347
+ Timestamp.combine(date_component, time_component),
348
+ datetime.combine(date_component, time_component),
349
+ )
350
+
351
+ def test_basics_nanos(self):
352
+ val = np.int64(946_684_800_000_000_000).view("M8[ns]")
353
+ stamp = Timestamp(val.view("i8") + 500)
354
+ assert stamp.year == 2000
355
+ assert stamp.month == 1
356
+ assert stamp.microsecond == 0
357
+ assert stamp.nanosecond == 500
358
+
359
+ # GH 14415
360
+ val = np.iinfo(np.int64).min + 80_000_000_000_000
361
+ stamp = Timestamp(val)
362
+ assert stamp.year == 1677
363
+ assert stamp.month == 9
364
+ assert stamp.day == 21
365
+ assert stamp.microsecond == 145224
366
+ assert stamp.nanosecond == 192
367
+
368
+ def test_roundtrip(self):
369
+ # test value to string and back conversions
370
+ # further test accessors
371
+ base = Timestamp("20140101 00:00:00").as_unit("ns")
372
+
373
+ result = Timestamp(base._value + Timedelta("5ms").value)
374
+ assert result == Timestamp(f"{base}.005000")
375
+ assert result.microsecond == 5000
376
+
377
+ result = Timestamp(base._value + Timedelta("5us").value)
378
+ assert result == Timestamp(f"{base}.000005")
379
+ assert result.microsecond == 5
380
+
381
+ result = Timestamp(base._value + Timedelta("5ns")._value)
382
+ assert result == Timestamp(f"{base}.000000005")
383
+ assert result.nanosecond == 5
384
+ assert result.microsecond == 0
385
+
386
+ result = Timestamp(base._value + Timedelta("6ms 5us").value)
387
+ assert result == Timestamp(f"{base}.006005")
388
+ assert result.microsecond == 5 + 6 * 1000
389
+
390
+ result = Timestamp(base._value + Timedelta("200ms 5us").value)
391
+ assert result == Timestamp(f"{base}.200005")
392
+ assert result.microsecond == 5 + 200 * 1000
393
+
394
+ def test_hash_equivalent(self):
395
+ d = {datetime(2011, 1, 1): 5}
396
+ stamp = Timestamp(datetime(2011, 1, 1))
397
+ assert d[stamp] == 5
398
+
399
+ @pytest.mark.parametrize(
400
+ "timezone, year, month, day, hour",
401
+ [["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
402
+ )
403
+ def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour):
404
+ # see gh-33931
405
+ test_timezone = gettz(timezone)
406
+ transition_1 = Timestamp(
407
+ year=year,
408
+ month=month,
409
+ day=day,
410
+ hour=hour,
411
+ minute=0,
412
+ fold=0,
413
+ tzinfo=test_timezone,
414
+ )
415
+ transition_2 = Timestamp(
416
+ year=year,
417
+ month=month,
418
+ day=day,
419
+ hour=hour,
420
+ minute=0,
421
+ fold=1,
422
+ tzinfo=test_timezone,
423
+ )
424
+ assert hash(transition_1) == hash(transition_2)
425
+
426
+
427
+ class TestTimestampNsOperations:
428
+ def test_nanosecond_string_parsing(self):
429
+ ts = Timestamp("2013-05-01 07:15:45.123456789")
430
+ # GH 7878
431
+ expected_repr = "2013-05-01 07:15:45.123456789"
432
+ expected_value = 1_367_392_545_123_456_789
433
+ assert ts._value == expected_value
434
+ assert expected_repr in repr(ts)
435
+
436
+ ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo")
437
+ assert ts._value == expected_value - 9 * 3600 * 1_000_000_000
438
+ assert expected_repr in repr(ts)
439
+
440
+ ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC")
441
+ assert ts._value == expected_value
442
+ assert expected_repr in repr(ts)
443
+
444
+ ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern")
445
+ assert ts._value == expected_value + 4 * 3600 * 1_000_000_000
446
+ assert expected_repr in repr(ts)
447
+
448
+ # GH 10041
449
+ ts = Timestamp("20130501T071545.123456789")
450
+ assert ts._value == expected_value
451
+ assert expected_repr in repr(ts)
452
+
453
+ def test_nanosecond_timestamp(self):
454
+ # GH 7610
455
+ expected = 1_293_840_000_000_000_005
456
+ t = Timestamp("2011-01-01") + offsets.Nano(5)
457
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
458
+ assert t._value == expected
459
+ assert t.nanosecond == 5
460
+
461
+ t = Timestamp(t)
462
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
463
+ assert t._value == expected
464
+ assert t.nanosecond == 5
465
+
466
+ t = Timestamp("2011-01-01 00:00:00.000000005")
467
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
468
+ assert t._value == expected
469
+ assert t.nanosecond == 5
470
+
471
+ expected = 1_293_840_000_000_000_010
472
+ t = t + offsets.Nano(5)
473
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
474
+ assert t._value == expected
475
+ assert t.nanosecond == 10
476
+
477
+ t = Timestamp(t)
478
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
479
+ assert t._value == expected
480
+ assert t.nanosecond == 10
481
+
482
+ t = Timestamp("2011-01-01 00:00:00.000000010")
483
+ assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
484
+ assert t._value == expected
485
+ assert t.nanosecond == 10
486
+
487
+
488
+ class TestTimestampConversion:
489
+ def test_conversion(self):
490
+ # GH#9255
491
+ ts = Timestamp("2000-01-01").as_unit("ns")
492
+
493
+ result = ts.to_pydatetime()
494
+ expected = datetime(2000, 1, 1)
495
+ assert result == expected
496
+ assert type(result) == type(expected)
497
+
498
+ result = ts.to_datetime64()
499
+ expected = np.datetime64(ts._value, "ns")
500
+ assert result == expected
501
+ assert type(result) == type(expected)
502
+ assert result.dtype == expected.dtype
503
+
504
+ def test_to_period_tz_warning(self):
505
+ # GH#21333 make sure a warning is issued when timezone
506
+ # info is lost
507
+ ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern")
508
+ with tm.assert_produces_warning(UserWarning, match="drop timezone information"):
509
+ ts.to_period("D")
510
+
511
+ def test_to_numpy_alias(self):
512
+ # GH 24653: alias .to_numpy() for scalars
513
+ ts = Timestamp(datetime.now())
514
+ assert ts.to_datetime64() == ts.to_numpy()
515
+
516
+ # GH#44460
517
+ msg = "dtype and copy arguments are ignored"
518
+ with pytest.raises(ValueError, match=msg):
519
+ ts.to_numpy("M8[s]")
520
+ with pytest.raises(ValueError, match=msg):
521
+ ts.to_numpy(copy=True)
522
+
523
+
524
+ class TestNonNano:
525
+ @pytest.fixture(params=["s", "ms", "us"])
526
+ def reso(self, request):
527
+ return request.param
528
+
529
+ @pytest.fixture
530
+ def dt64(self, reso):
531
+ # cases that are in-bounds for nanosecond, so we can compare against
532
+ # the existing implementation.
533
+ return np.datetime64("2016-01-01", reso)
534
+
535
+ @pytest.fixture
536
+ def ts(self, dt64):
537
+ return Timestamp._from_dt64(dt64)
538
+
539
+ @pytest.fixture
540
+ def ts_tz(self, ts, tz_aware_fixture):
541
+ tz = maybe_get_tz(tz_aware_fixture)
542
+ return Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
543
+
544
+ def test_non_nano_construction(self, dt64, ts, reso):
545
+ assert ts._value == dt64.view("i8")
546
+
547
+ if reso == "s":
548
+ assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
549
+ elif reso == "ms":
550
+ assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value
551
+ elif reso == "us":
552
+ assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value
553
+
554
+ def test_non_nano_fields(self, dt64, ts):
555
+ alt = Timestamp(dt64)
556
+
557
+ assert ts.year == alt.year
558
+ assert ts.month == alt.month
559
+ assert ts.day == alt.day
560
+ assert ts.hour == ts.minute == ts.second == ts.microsecond == 0
561
+ assert ts.nanosecond == 0
562
+
563
+ assert ts.to_julian_date() == alt.to_julian_date()
564
+ assert ts.weekday() == alt.weekday()
565
+ assert ts.isoweekday() == alt.isoweekday()
566
+
567
+ def test_start_end_fields(self, ts):
568
+ assert ts.is_year_start
569
+ assert ts.is_quarter_start
570
+ assert ts.is_month_start
571
+ assert not ts.is_year_end
572
+ assert not ts.is_month_end
573
+ assert not ts.is_month_end
574
+
575
+ # 2016-01-01 is a Friday, so is year/quarter/month start with this freq
576
+ assert ts.is_year_start
577
+ assert ts.is_quarter_start
578
+ assert ts.is_month_start
579
+ assert not ts.is_year_end
580
+ assert not ts.is_month_end
581
+ assert not ts.is_month_end
582
+
583
+ def test_day_name(self, dt64, ts):
584
+ alt = Timestamp(dt64)
585
+ assert ts.day_name() == alt.day_name()
586
+
587
+ def test_month_name(self, dt64, ts):
588
+ alt = Timestamp(dt64)
589
+ assert ts.month_name() == alt.month_name()
590
+
591
+ def test_tz_convert(self, ts):
592
+ ts = Timestamp._from_value_and_reso(ts._value, ts._creso, timezone.utc)
593
+
594
+ tz = zoneinfo.ZoneInfo("US/Pacific")
595
+ result = ts.tz_convert(tz)
596
+
597
+ assert isinstance(result, Timestamp)
598
+ assert result._creso == ts._creso
599
+ assert tz_compare(result.tz, tz)
600
+
601
+ def test_repr(self, dt64, ts):
602
+ alt = Timestamp(dt64)
603
+
604
+ assert str(ts) == str(alt)
605
+ assert repr(ts) == repr(alt)
606
+
607
+ def test_comparison(self, dt64, ts):
608
+ alt = Timestamp(dt64)
609
+
610
+ assert ts == dt64
611
+ assert dt64 == ts
612
+ assert ts == alt
613
+ assert alt == ts
614
+
615
+ assert not ts != dt64
616
+ assert not dt64 != ts
617
+ assert not ts != alt
618
+ assert not alt != ts
619
+
620
+ assert not ts < dt64
621
+ assert not dt64 < ts
622
+ assert not ts < alt
623
+ assert not alt < ts
624
+
625
+ assert not ts > dt64
626
+ assert not dt64 > ts
627
+ assert not ts > alt
628
+ assert not alt > ts
629
+
630
+ assert ts >= dt64
631
+ assert dt64 >= ts
632
+ assert ts >= alt
633
+ assert alt >= ts
634
+
635
+ assert ts <= dt64
636
+ assert dt64 <= ts
637
+ assert ts <= alt
638
+ assert alt <= ts
639
+
640
+ def test_cmp_cross_reso(self):
641
+ # numpy gets this wrong because of silent overflow
642
+ dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns]
643
+ ts = Timestamp._from_dt64(dt64)
644
+
645
+ # subtracting 3600*24 gives a datetime64 that _can_ fit inside the
646
+ # nanosecond implementation bounds.
647
+ other = Timestamp(dt64 - np.timedelta64(3600 * 24, "s")).as_unit("ns")
648
+ assert other < ts
649
+ if not is_numpy_dev and not np_version_gt2_5:
650
+ assert other.asm8 > ts.asm8 # <- numpy gets this wrong
651
+ assert ts > other
652
+ if not is_numpy_dev and not np_version_gt2_5:
653
+ assert ts.asm8 < other.asm8 # <- numpy gets this wrong
654
+ assert not other == ts
655
+ assert ts != other
656
+
657
+ @pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong")
658
+ def test_cmp_cross_reso_reversed_dt64(self):
659
+ dt64 = np.datetime64(106752, "D") # won't fit in M8[ns]
660
+ ts = Timestamp._from_dt64(dt64)
661
+ other = Timestamp(dt64 - 1)
662
+
663
+ assert other.asm8 < ts
664
+
665
+ def test_pickle(self, ts, tz_aware_fixture, temp_file):
666
+ tz = tz_aware_fixture
667
+ tz = maybe_get_tz(tz)
668
+ ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
669
+ rt = tm.round_trip_pickle(ts, temp_file)
670
+ assert rt._creso == ts._creso
671
+ assert rt == ts
672
+
673
+ def test_normalize(self, dt64, ts):
674
+ alt = Timestamp(dt64)
675
+ result = ts.normalize()
676
+ assert result._creso == ts._creso
677
+ assert result == alt.normalize()
678
+
679
+ def test_asm8(self, dt64, ts):
680
+ rt = ts.asm8
681
+ assert rt == dt64
682
+ assert rt.dtype == dt64.dtype
683
+
684
+ def test_to_numpy(self, dt64, ts):
685
+ res = ts.to_numpy()
686
+ assert res == dt64
687
+ assert res.dtype == dt64.dtype
688
+
689
+ def test_to_datetime64(self, dt64, ts):
690
+ res = ts.to_datetime64()
691
+ assert res == dt64
692
+ assert res.dtype == dt64.dtype
693
+
694
+ def test_timestamp(self, dt64, ts):
695
+ alt = Timestamp(dt64)
696
+ assert ts.timestamp() == alt.timestamp()
697
+
698
+ def test_to_period(self, dt64, ts):
699
+ alt = Timestamp(dt64)
700
+ assert ts.to_period("D") == alt.to_period("D")
701
+
702
+ @pytest.mark.parametrize(
703
+ "td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")]
704
+ )
705
+ def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
706
+ exp_reso = max(ts._creso, Timedelta(td)._creso)
707
+
708
+ result = ts - td
709
+ expected = Timestamp(dt64) - td
710
+ assert isinstance(result, Timestamp)
711
+ assert result._creso == exp_reso
712
+ assert result == expected
713
+
714
+ result = ts + td
715
+ expected = Timestamp(dt64) + td
716
+ assert isinstance(result, Timestamp)
717
+ assert result._creso == exp_reso
718
+ assert result == expected
719
+
720
+ result = td + ts
721
+ expected = td + Timestamp(dt64)
722
+ assert isinstance(result, Timestamp)
723
+ assert result._creso == exp_reso
724
+ assert result == expected
725
+
726
+ def test_addsub_offset(self, ts_tz):
727
+ # specifically non-Tick offset
728
+ off = offsets.YearEnd(1)
729
+ result = ts_tz + off
730
+
731
+ assert isinstance(result, Timestamp)
732
+ assert result._creso == ts_tz._creso
733
+ if ts_tz.month == 12 and ts_tz.day == 31:
734
+ assert result.year == ts_tz.year + 1
735
+ else:
736
+ assert result.year == ts_tz.year
737
+ assert result.day == 31
738
+ assert result.month == 12
739
+ assert tz_compare(result.tz, ts_tz.tz)
740
+
741
+ result = ts_tz - off
742
+
743
+ assert isinstance(result, Timestamp)
744
+ assert result._creso == ts_tz._creso
745
+ assert result.year == ts_tz.year - 1
746
+ assert result.day == 31
747
+ assert result.month == 12
748
+ assert tz_compare(result.tz, ts_tz.tz)
749
+
750
+ def test_sub_datetimelike_mismatched_reso(self, ts_tz):
751
+ # case with non-lossy rounding
752
+ ts = ts_tz
753
+
754
+ # choose a unit for `other` that doesn't match ts_tz's;
755
+ # this construction ensures we get cases with other._creso < ts._creso
756
+ # and cases with other._creso > ts._creso
757
+ unit = {
758
+ NpyDatetimeUnit.NPY_FR_us.value: "ms",
759
+ NpyDatetimeUnit.NPY_FR_ms.value: "s",
760
+ NpyDatetimeUnit.NPY_FR_s.value: "us",
761
+ }[ts._creso]
762
+ other = ts.as_unit(unit)
763
+ assert other._creso != ts._creso
764
+
765
+ result = ts - other
766
+ assert isinstance(result, Timedelta)
767
+ assert result._value == 0
768
+ assert result._creso == max(ts._creso, other._creso)
769
+
770
+ result = other - ts
771
+ assert isinstance(result, Timedelta)
772
+ assert result._value == 0
773
+ assert result._creso == max(ts._creso, other._creso)
774
+
775
+ if ts._creso < other._creso:
776
+ # Case where rounding is lossy
777
+ other2 = other + Timedelta._from_value_and_reso(1, other._creso)
778
+ exp = ts.as_unit(other.unit) - other2
779
+
780
+ res = ts - other2
781
+ assert res == exp
782
+ assert res._creso == max(ts._creso, other._creso)
783
+
784
+ res = other2 - ts
785
+ assert res == -exp
786
+ assert res._creso == max(ts._creso, other._creso)
787
+ else:
788
+ ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
789
+ exp = ts2 - other.as_unit(ts2.unit)
790
+
791
+ res = ts2 - other
792
+ assert res == exp
793
+ assert res._creso == max(ts._creso, other._creso)
794
+ res = other - ts2
795
+ assert res == -exp
796
+ assert res._creso == max(ts._creso, other._creso)
797
+
798
+ def test_sub_timedeltalike_mismatched_reso(self, ts_tz):
799
+ # case with non-lossy rounding
800
+ ts = ts_tz
801
+
802
+ # choose a unit for `other` that doesn't match ts_tz's;
803
+ # this construction ensures we get cases with other._creso < ts._creso
804
+ # and cases with other._creso > ts._creso
805
+ unit = {
806
+ NpyDatetimeUnit.NPY_FR_us.value: "ms",
807
+ NpyDatetimeUnit.NPY_FR_ms.value: "s",
808
+ NpyDatetimeUnit.NPY_FR_s.value: "us",
809
+ }[ts._creso]
810
+ other = Timedelta(0).as_unit(unit)
811
+ assert other._creso != ts._creso
812
+
813
+ result = ts + other
814
+ assert isinstance(result, Timestamp)
815
+ assert result == ts
816
+ assert result._creso == max(ts._creso, other._creso)
817
+
818
+ result = other + ts
819
+ assert isinstance(result, Timestamp)
820
+ assert result == ts
821
+ assert result._creso == max(ts._creso, other._creso)
822
+
823
+ if ts._creso < other._creso:
824
+ # Case where rounding is lossy
825
+ other2 = other + Timedelta._from_value_and_reso(1, other._creso)
826
+ exp = ts.as_unit(other.unit) + other2
827
+ res = ts + other2
828
+ assert res == exp
829
+ assert res._creso == max(ts._creso, other._creso)
830
+ res = other2 + ts
831
+ assert res == exp
832
+ assert res._creso == max(ts._creso, other._creso)
833
+ else:
834
+ ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
835
+ exp = ts2 + other.as_unit(ts2.unit)
836
+
837
+ res = ts2 + other
838
+ assert res == exp
839
+ assert res._creso == max(ts._creso, other._creso)
840
+ res = other + ts2
841
+ assert res == exp
842
+ assert res._creso == max(ts._creso, other._creso)
843
+
844
+ def test_addition_doesnt_downcast_reso(self):
845
+ # https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413
846
+ ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us")
847
+ td = Timedelta(microseconds=1).as_unit("us")
848
+ res = ts + td
849
+ assert res._creso == ts._creso
850
+
851
+ def test_sub_timedelta64_mismatched_reso(self, ts_tz):
852
+ ts = ts_tz
853
+
854
+ res = ts + np.timedelta64(1, "ns")
855
+ exp = ts.as_unit("ns") + np.timedelta64(1, "ns")
856
+ assert exp == res
857
+ assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value
858
+
859
+ def test_min(self, ts):
860
+ assert ts.min <= ts
861
+ assert ts.min._creso == ts._creso
862
+ assert ts.min._value == NaT._value + 1
863
+
864
+ def test_max(self, ts):
865
+ assert ts.max >= ts
866
+ assert ts.max._creso == ts._creso
867
+ assert ts.max._value == np.iinfo(np.int64).max
868
+
869
+ def test_resolution(self, ts):
870
+ expected = Timedelta._from_value_and_reso(1, ts._creso)
871
+ result = ts.resolution
872
+ assert result == expected
873
+ assert result._creso == expected._creso
874
+
875
+ def test_out_of_ns_bounds(self):
876
+ # https://github.com/pandas-dev/pandas/issues/51060
877
+ result = Timestamp(-52700112000, unit="s")
878
+ assert result == Timestamp("0300-01-01")
879
+ assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s")
880
+
881
+
882
+ def test_timestamp_class_min_max_resolution():
883
+ # when accessed on the class (as opposed to an instance), we default
884
+ # to nanoseconds
885
+ assert Timestamp.min == Timestamp(NaT._value + 1)
886
+ assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
887
+
888
+ assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
889
+ assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
890
+
891
+ assert Timestamp.resolution == Timedelta(1)
892
+ assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
893
+
894
+
895
+ def test_delimited_date():
896
+ # https://github.com/pandas-dev/pandas/issues/50231
897
+ with tm.assert_produces_warning(None):
898
+ result = Timestamp("13-01-2000")
899
+ expected = Timestamp(2000, 1, 13)
900
+ assert result == expected
901
+
902
+
903
+ def test_utctimetuple():
904
+ # GH 32174
905
+ ts = Timestamp("2000-01-01", tz="UTC")
906
+ result = ts.utctimetuple()
907
+ expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0))
908
+ assert result == expected
909
+
910
+
911
+ def test_negative_dates():
912
+ # https://github.com/pandas-dev/pandas/issues/50787
913
+ ts = Timestamp("-2000-01-01")
914
+ msg = (
915
+ " not yet supported on Timestamps which are outside the range of "
916
+ "Python's standard library. For now, please call the components you need "
917
+ r"\(such as `.year` and `.month`\) and construct your string from there.$"
918
+ )
919
+ func = "^strftime"
920
+ with pytest.raises(NotImplementedError, match=func + msg):
921
+ ts.strftime("%Y")
922
+
923
+ msg = (
924
+ " not yet supported on Timestamps which "
925
+ "are outside the range of Python's standard library. "
926
+ )
927
+ func = "^date"
928
+ with pytest.raises(NotImplementedError, match=func + msg):
929
+ ts.date()
930
+ func = "^isocalendar"
931
+ with pytest.raises(NotImplementedError, match=func + msg):
932
+ ts.isocalendar()
933
+ func = "^timetuple"
934
+ with pytest.raises(NotImplementedError, match=func + msg):
935
+ ts.timetuple()
936
+ func = "^toordinal"
937
+ with pytest.raises(NotImplementedError, match=func + msg):
938
+ ts.toordinal()
.venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timezones.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Tests for Timestamp timezone-related methods
3
+ """
4
+
5
+ from datetime import datetime
6
+
7
+ from pandas._libs.tslibs import timezones
8
+
9
+ from pandas import Timestamp
10
+
11
+
12
+ class TestTimestampTZOperations:
13
+ # ------------------------------------------------------------------
14
+
15
+ def test_timestamp_timetz_equivalent_with_datetime_tz(self, tz_naive_fixture):
16
+ # GH21358
17
+ tz = timezones.maybe_get_tz(tz_naive_fixture)
18
+
19
+ stamp = Timestamp("2018-06-04 10:20:30", tz=tz)
20
+ _datetime = datetime(2018, 6, 4, hour=10, minute=20, second=30, tzinfo=tz)
21
+
22
+ result = stamp.timetz()
23
+ expected = _datetime.timetz()
24
+
25
+ assert result == expected
.venv/lib/python3.13/site-packages/pandas/tests/series/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_cat_accessor.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas.errors import Pandas4Warning
5
+
6
+ from pandas import (
7
+ Categorical,
8
+ DataFrame,
9
+ Index,
10
+ Series,
11
+ Timestamp,
12
+ date_range,
13
+ period_range,
14
+ timedelta_range,
15
+ )
16
+ import pandas._testing as tm
17
+ from pandas.core.arrays.categorical import CategoricalAccessor
18
+ from pandas.core.indexes.accessors import Properties
19
+
20
+
21
+ class TestCatAccessor:
22
+ @pytest.mark.parametrize(
23
+ "method",
24
+ [
25
+ lambda x: x.cat.set_categories([1, 2, 3]),
26
+ lambda x: x.cat.reorder_categories([2, 3, 1], ordered=True),
27
+ lambda x: x.cat.rename_categories([1, 2, 3]),
28
+ lambda x: x.cat.remove_unused_categories(),
29
+ lambda x: x.cat.remove_categories([2]),
30
+ lambda x: x.cat.add_categories([4]),
31
+ lambda x: x.cat.as_ordered(),
32
+ lambda x: x.cat.as_unordered(),
33
+ ],
34
+ )
35
+ def test_getname_categorical_accessor(self, method):
36
+ # GH#17509
37
+ ser = Series([1, 2, 3], name="A").astype("category")
38
+ expected = "A"
39
+ result = method(ser).name
40
+ assert result == expected
41
+
42
+ def test_cat_accessor(self):
43
+ ser = Series(Categorical(["a", "b", np.nan, "a"]))
44
+ tm.assert_index_equal(ser.cat.categories, Index(["a", "b"]))
45
+ assert not ser.cat.ordered
46
+
47
+ exp = Categorical(["a", "b", np.nan, "a"], categories=["b", "a"])
48
+
49
+ res = ser.cat.set_categories(["b", "a"])
50
+ tm.assert_categorical_equal(res.values, exp)
51
+
52
+ ser[:] = "a"
53
+ ser = ser.cat.remove_unused_categories()
54
+ tm.assert_index_equal(ser.cat.categories, Index(["a"]))
55
+
56
+ def test_cat_accessor_api(self):
57
+ # GH#9322
58
+
59
+ assert Series.cat is CategoricalAccessor
60
+ ser = Series(list("aabbcde")).astype("category")
61
+ assert isinstance(ser.cat, CategoricalAccessor)
62
+
63
+ invalid = Series([1])
64
+ with pytest.raises(AttributeError, match="only use .cat accessor"):
65
+ invalid.cat
66
+ assert not hasattr(invalid, "cat")
67
+
68
+ def test_cat_accessor_no_new_attributes(self):
69
+ # https://github.com/pandas-dev/pandas/issues/10673
70
+ cat = Series(list("aabbcde")).astype("category")
71
+ with pytest.raises(AttributeError, match="You cannot add any new attribute"):
72
+ cat.cat.xlabel = "a"
73
+
74
+ def test_categorical_delegations(self):
75
+ # invalid accessor
76
+ msg = r"Can only use \.cat accessor with a 'category' dtype"
77
+ with pytest.raises(AttributeError, match=msg):
78
+ Series([1, 2, 3]).cat
79
+ with pytest.raises(AttributeError, match=msg):
80
+ Series([1, 2, 3]).cat()
81
+ with pytest.raises(AttributeError, match=msg):
82
+ Series(["a", "b", "c"]).cat
83
+ with pytest.raises(AttributeError, match=msg):
84
+ Series(np.arange(5.0)).cat
85
+ with pytest.raises(AttributeError, match=msg):
86
+ Series([Timestamp("20130101")]).cat
87
+
88
+ # Series should delegate calls to '.categories', '.codes', '.ordered'
89
+ # and the methods '.set_categories()' 'drop_unused_categories()' to the
90
+ # categorical
91
+ ser = Series(Categorical(["a", "b", "c", "a"], ordered=True))
92
+ exp_categories = Index(["a", "b", "c"])
93
+ tm.assert_index_equal(ser.cat.categories, exp_categories)
94
+ ser = ser.cat.rename_categories([1, 2, 3])
95
+ exp_categories = Index([1, 2, 3])
96
+ tm.assert_index_equal(ser.cat.categories, exp_categories)
97
+
98
+ exp_codes = Series([0, 1, 2, 0], dtype="int8")
99
+ tm.assert_series_equal(ser.cat.codes, exp_codes)
100
+
101
+ assert ser.cat.ordered
102
+ ser = ser.cat.as_unordered()
103
+ assert not ser.cat.ordered
104
+
105
+ ser = ser.cat.as_ordered()
106
+ assert ser.cat.ordered
107
+
108
+ # reorder
109
+ ser = Series(Categorical(["a", "b", "c", "a"], ordered=True))
110
+ exp_categories = Index(["c", "b", "a"])
111
+ exp_values = np.array(["a", "b", "c", "a"], dtype=np.object_)
112
+ ser = ser.cat.set_categories(["c", "b", "a"])
113
+ tm.assert_index_equal(ser.cat.categories, exp_categories)
114
+ tm.assert_numpy_array_equal(ser.values.__array__(), exp_values)
115
+ tm.assert_numpy_array_equal(ser.__array__(), exp_values)
116
+
117
+ # remove unused categories
118
+ ser = Series(Categorical(["a", "b", "b", "a"], categories=["a", "b", "c"]))
119
+ exp_categories = Index(["a", "b"])
120
+ exp_values = np.array(["a", "b", "b", "a"], dtype=np.object_)
121
+ ser = ser.cat.remove_unused_categories()
122
+ tm.assert_index_equal(ser.cat.categories, exp_categories)
123
+ tm.assert_numpy_array_equal(ser.values.__array__(), exp_values)
124
+ tm.assert_numpy_array_equal(ser.__array__(), exp_values)
125
+
126
+ # This method is likely to be confused, so test that it raises an error
127
+ # on wrong inputs:
128
+ msg = "'Series' object has no attribute 'set_categories'"
129
+ with pytest.raises(AttributeError, match=msg):
130
+ ser.set_categories([4, 3, 2, 1])
131
+
132
+ # right: ser.cat.set_categories([4,3,2,1])
133
+
134
+ # GH#18862 (let Series.cat.rename_categories take callables)
135
+ ser = Series(Categorical(["a", "b", "c", "a"], ordered=True))
136
+ result = ser.cat.rename_categories(lambda x: x.upper())
137
+ expected = Series(
138
+ Categorical(["A", "B", "C", "A"], categories=["A", "B", "C"], ordered=True)
139
+ )
140
+ tm.assert_series_equal(result, expected)
141
+
142
+ @pytest.mark.parametrize(
143
+ "idx",
144
+ [
145
+ date_range("1/1/2015", periods=5, unit="ns"),
146
+ date_range("1/1/2015", periods=5, tz="MET", unit="ns"),
147
+ period_range("1/1/2015", freq="D", periods=5),
148
+ timedelta_range("1 days", "10 days"),
149
+ ],
150
+ )
151
+ def test_dt_accessor_api_for_categorical(self, idx):
152
+ # https://github.com/pandas-dev/pandas/issues/10661
153
+
154
+ ser = Series(idx)
155
+ cat = ser.astype("category")
156
+
157
+ # only testing field (like .day)
158
+ # and bool (is_month_start)
159
+ attr_names = type(ser._values)._datetimelike_ops
160
+
161
+ assert isinstance(cat.dt, Properties)
162
+
163
+ special_func_defs = [
164
+ ("strftime", ("%Y-%m-%d",), {}),
165
+ ("round", ("D",), {}),
166
+ ("floor", ("D",), {}),
167
+ ("ceil", ("D",), {}),
168
+ ("asfreq", ("D",), {}),
169
+ ("as_unit", ("s"), {}),
170
+ ]
171
+ if idx.dtype == "M8[ns]":
172
+ # exclude dt64tz since that is already localized and would raise
173
+ tup = ("tz_localize", ("UTC",), {})
174
+ special_func_defs.append(tup)
175
+ elif idx.dtype.kind == "M":
176
+ # exclude dt64 since that is not localized so would raise
177
+ tup = ("tz_convert", ("EST",), {})
178
+ special_func_defs.append(tup)
179
+
180
+ _special_func_names = [f[0] for f in special_func_defs]
181
+
182
+ _ignore_names = ["components", "tz_localize", "tz_convert"]
183
+
184
+ func_names = [
185
+ fname
186
+ for fname in dir(ser.dt)
187
+ if not (
188
+ fname.startswith("_")
189
+ or fname in attr_names
190
+ or fname in _special_func_names
191
+ or fname in _ignore_names
192
+ )
193
+ ]
194
+
195
+ func_defs = [(fname, (), {}) for fname in func_names]
196
+ func_defs.extend(
197
+ f_def for f_def in special_func_defs if f_def[0] in dir(ser.dt)
198
+ )
199
+
200
+ for func, args, kwargs in func_defs:
201
+ warn_cls = []
202
+ if func == "to_period" and getattr(idx, "tz", None) is not None:
203
+ # dropping TZ
204
+ warn_cls.append(UserWarning)
205
+ elif func == "to_pytimedelta":
206
+ # GH 57463
207
+ warn_cls.append(Pandas4Warning)
208
+ if warn_cls:
209
+ warn_cls = tuple(warn_cls)
210
+ else:
211
+ warn_cls = None
212
+ with tm.assert_produces_warning(warn_cls):
213
+ res = getattr(cat.dt, func)(*args, **kwargs)
214
+ exp = getattr(ser.dt, func)(*args, **kwargs)
215
+
216
+ tm.assert_equal(res, exp)
217
+
218
+ for attr in attr_names:
219
+ res = getattr(cat.dt, attr)
220
+ exp = getattr(ser.dt, attr)
221
+
222
+ tm.assert_equal(res, exp)
223
+
224
+ def test_dt_accessor_api_for_categorical_invalid(self):
225
+ invalid = Series([1, 2, 3]).astype("category")
226
+ msg = "Can only use .dt accessor with datetimelike"
227
+
228
+ with pytest.raises(AttributeError, match=msg):
229
+ invalid.dt
230
+ assert not hasattr(invalid, "str")
231
+
232
+ def test_set_categories_setitem(self):
233
+ # GH#43334
234
+
235
+ df = DataFrame({"Survived": [1, 0, 1], "Sex": [0, 1, 1]}, dtype="category")
236
+
237
+ df["Survived"] = df["Survived"].cat.rename_categories(["No", "Yes"])
238
+ df["Sex"] = df["Sex"].cat.rename_categories(["female", "male"])
239
+
240
+ # values should not be coerced to NaN
241
+ assert list(df["Sex"]) == ["female", "male", "male"]
242
+ assert list(df["Survived"]) == ["Yes", "No", "Yes"]
243
+
244
+ df["Sex"] = Categorical(df["Sex"], categories=["female", "male"], ordered=False)
245
+ df["Survived"] = Categorical(
246
+ df["Survived"], categories=["No", "Yes"], ordered=False
247
+ )
248
+
249
+ # values should not be coerced to NaN
250
+ assert list(df["Sex"]) == ["female", "male", "male"]
251
+ assert list(df["Survived"]) == ["Yes", "No", "Yes"]
252
+
253
+ def test_categorical_of_booleans_is_boolean(self):
254
+ # https://github.com/pandas-dev/pandas/issues/46313
255
+ df = DataFrame(
256
+ {"int_cat": [1, 2, 3], "bool_cat": [True, False, False]}, dtype="category"
257
+ )
258
+ value = df["bool_cat"].cat.categories.dtype
259
+ expected = np.dtype(np.bool_)
260
+ assert value is expected
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_dt_accessor.py ADDED
@@ -0,0 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import calendar
2
+ from datetime import (
3
+ date,
4
+ datetime,
5
+ time,
6
+ )
7
+ import locale
8
+ import unicodedata
9
+
10
+ import numpy as np
11
+ import pytest
12
+
13
+ from pandas._libs.tslibs.timezones import maybe_get_tz
14
+ from pandas.errors import Pandas4Warning
15
+
16
+ from pandas.core.dtypes.common import (
17
+ is_integer_dtype,
18
+ is_list_like,
19
+ )
20
+
21
+ import pandas as pd
22
+ from pandas import (
23
+ DataFrame,
24
+ DatetimeIndex,
25
+ Index,
26
+ Period,
27
+ PeriodIndex,
28
+ Series,
29
+ StringDtype,
30
+ TimedeltaIndex,
31
+ date_range,
32
+ period_range,
33
+ timedelta_range,
34
+ )
35
+ import pandas._testing as tm
36
+ from pandas.core.arrays import (
37
+ DatetimeArray,
38
+ PeriodArray,
39
+ TimedeltaArray,
40
+ )
41
+
42
+ ok_for_period = PeriodArray._datetimelike_ops
43
+ ok_for_period_methods = ["strftime", "to_timestamp", "asfreq"]
44
+ ok_for_dt = DatetimeArray._datetimelike_ops
45
+ ok_for_dt_methods = [
46
+ "to_period",
47
+ "to_pydatetime",
48
+ "tz_localize",
49
+ "tz_convert",
50
+ "normalize",
51
+ "strftime",
52
+ "round",
53
+ "floor",
54
+ "ceil",
55
+ "day_name",
56
+ "month_name",
57
+ "isocalendar",
58
+ "as_unit",
59
+ ]
60
+ ok_for_td = TimedeltaArray._datetimelike_ops
61
+ ok_for_td_methods = [
62
+ "components",
63
+ "to_pytimedelta",
64
+ "total_seconds",
65
+ "round",
66
+ "floor",
67
+ "ceil",
68
+ "as_unit",
69
+ ]
70
+
71
+
72
+ def get_dir(ser):
73
+ # check limited display api
74
+ results = [r for r in ser.dt.__dir__() if not r.startswith("_")]
75
+ return sorted(set(results))
76
+
77
+
78
+ class TestSeriesDatetimeValues:
79
+ def _compare(self, ser, name):
80
+ # GH 7207, 11128
81
+ # test .dt namespace accessor
82
+
83
+ def get_expected(ser, prop):
84
+ result = getattr(Index(ser._values), prop)
85
+ if isinstance(result, np.ndarray):
86
+ if is_integer_dtype(result):
87
+ result = result.astype("int64")
88
+ elif not is_list_like(result) or isinstance(result, DataFrame):
89
+ return result
90
+ return Series(result, index=ser.index, name=ser.name)
91
+
92
+ left = getattr(ser.dt, name)
93
+ right = get_expected(ser, name)
94
+ if not (is_list_like(left) and is_list_like(right)):
95
+ assert left == right
96
+ elif isinstance(left, DataFrame):
97
+ tm.assert_frame_equal(left, right)
98
+ else:
99
+ tm.assert_series_equal(left, right)
100
+
101
+ @pytest.mark.parametrize("freq", ["D", "s", "ms"])
102
+ def test_dt_namespace_accessor_datetime64(self, freq):
103
+ # GH#7207, GH#11128
104
+ # test .dt namespace accessor
105
+
106
+ # datetimeindex
107
+ dti = date_range("20130101", periods=5, freq=freq)
108
+ ser = Series(dti, name="xxx")
109
+
110
+ for prop in ok_for_dt:
111
+ # we test freq below
112
+ if prop != "freq":
113
+ self._compare(ser, prop)
114
+
115
+ for prop in ok_for_dt_methods:
116
+ getattr(ser.dt, prop)
117
+
118
+ result = ser.dt.to_pydatetime()
119
+ assert isinstance(result, Series)
120
+ assert result.dtype == object
121
+
122
+ result = ser.dt.tz_localize("US/Eastern")
123
+ exp_values = DatetimeIndex(ser.values).tz_localize("US/Eastern")
124
+ expected = Series(exp_values, index=ser.index, name="xxx")
125
+ tm.assert_series_equal(result, expected)
126
+
127
+ tz_result = result.dt.tz
128
+ assert str(tz_result) == "US/Eastern"
129
+ freq_result = ser.dt.freq
130
+ assert freq_result == DatetimeIndex(ser.values, freq="infer").freq
131
+
132
+ # let's localize, then convert
133
+ result = ser.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
134
+ exp_values = (
135
+ DatetimeIndex(ser.values).tz_localize("UTC").tz_convert("US/Eastern")
136
+ )
137
+ expected = Series(exp_values, index=ser.index, name="xxx")
138
+ tm.assert_series_equal(result, expected)
139
+
140
+ def test_dt_namespace_accessor_datetime64tz(self):
141
+ # GH#7207, GH#11128
142
+ # test .dt namespace accessor
143
+
144
+ # datetimeindex with tz
145
+ dti = date_range("20130101", periods=5, tz="US/Eastern")
146
+ ser = Series(dti, name="xxx")
147
+ for prop in ok_for_dt:
148
+ # we test freq below
149
+ if prop != "freq":
150
+ self._compare(ser, prop)
151
+
152
+ for prop in ok_for_dt_methods:
153
+ getattr(ser.dt, prop)
154
+
155
+ result = ser.dt.to_pydatetime()
156
+ assert isinstance(result, Series)
157
+ assert result.dtype == object
158
+
159
+ result = ser.dt.tz_convert("CET")
160
+ expected = Series(ser._values.tz_convert("CET"), index=ser.index, name="xxx")
161
+ tm.assert_series_equal(result, expected)
162
+
163
+ tz_result = result.dt.tz
164
+ assert str(tz_result) == "CET"
165
+ freq_result = ser.dt.freq
166
+ assert freq_result == DatetimeIndex(ser.values, freq="infer").freq
167
+
168
+ def test_dt_namespace_accessor_timedelta(self):
169
+ # GH#7207, GH#11128
170
+ # test .dt namespace accessor
171
+
172
+ # timedelta index
173
+ cases = [
174
+ Series(
175
+ timedelta_range("1 day", periods=5), index=list("abcde"), name="xxx"
176
+ ),
177
+ Series(timedelta_range("1 day 01:23:45", periods=5, freq="s"), name="xxx"),
178
+ Series(
179
+ timedelta_range("2 days 01:23:45.012345", periods=5, freq="ms"),
180
+ name="xxx",
181
+ ),
182
+ ]
183
+ for ser in cases:
184
+ for prop in ok_for_td:
185
+ # we test freq below
186
+ if prop != "freq":
187
+ self._compare(ser, prop)
188
+
189
+ for prop in ok_for_td_methods:
190
+ getattr(ser.dt, prop)
191
+
192
+ result = ser.dt.components
193
+ assert isinstance(result, DataFrame)
194
+ tm.assert_index_equal(result.index, ser.index)
195
+
196
+ msg = "The behavior of TimedeltaProperties.to_pytimedelta is deprecated"
197
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
198
+ result = ser.dt.to_pytimedelta()
199
+ assert isinstance(result, np.ndarray)
200
+ assert result.dtype == object
201
+
202
+ result = ser.dt.total_seconds()
203
+ assert isinstance(result, Series)
204
+ assert result.dtype == "float64"
205
+
206
+ freq_result = ser.dt.freq
207
+ assert freq_result == TimedeltaIndex(ser.values, freq="infer").freq
208
+
209
+ def test_dt_namespace_accessor_period(self):
210
+ # GH#7207, GH#11128
211
+ # test .dt namespace accessor
212
+
213
+ # periodindex
214
+ pi = period_range("20130101", periods=5, freq="D")
215
+ ser = Series(pi, name="xxx")
216
+
217
+ for prop in ok_for_period:
218
+ # we test freq below
219
+ if prop != "freq":
220
+ self._compare(ser, prop)
221
+
222
+ for prop in ok_for_period_methods:
223
+ getattr(ser.dt, prop)
224
+
225
+ freq_result = ser.dt.freq
226
+ assert freq_result == PeriodIndex(ser.values).freq
227
+
228
+ def test_dt_namespace_accessor_index_and_values(self):
229
+ # both
230
+ index = date_range("20130101", periods=3, freq="D")
231
+ dti = date_range("20140204", periods=3, freq="s")
232
+ ser = Series(dti, index=index, name="xxx")
233
+ exp = Series(
234
+ np.array([2014, 2014, 2014], dtype="int32"), index=index, name="xxx"
235
+ )
236
+ tm.assert_series_equal(ser.dt.year, exp)
237
+
238
+ exp = Series(np.array([2, 2, 2], dtype="int32"), index=index, name="xxx")
239
+ tm.assert_series_equal(ser.dt.month, exp)
240
+
241
+ exp = Series(np.array([0, 1, 2], dtype="int32"), index=index, name="xxx")
242
+ tm.assert_series_equal(ser.dt.second, exp)
243
+
244
+ exp = Series([ser.iloc[0]] * 3, index=index, name="xxx")
245
+ tm.assert_series_equal(ser.dt.normalize(), exp)
246
+
247
+ def test_dt_accessor_limited_display_api(self):
248
+ # tznaive
249
+ ser = Series(date_range("20130101", periods=5, freq="D"), name="xxx")
250
+ results = get_dir(ser)
251
+ tm.assert_almost_equal(results, sorted(set(ok_for_dt + ok_for_dt_methods)))
252
+
253
+ # tzaware
254
+ ser = Series(date_range("2015-01-01", "2016-01-01", freq="min"), name="xxx")
255
+ ser = ser.dt.tz_localize("UTC").dt.tz_convert("America/Chicago")
256
+ results = get_dir(ser)
257
+ tm.assert_almost_equal(results, sorted(set(ok_for_dt + ok_for_dt_methods)))
258
+
259
+ # Period
260
+ idx = period_range("20130101", periods=5, freq="D", name="xxx")
261
+ ser = Series(idx)
262
+ results = get_dir(ser)
263
+ tm.assert_almost_equal(
264
+ results, sorted(set(ok_for_period + ok_for_period_methods))
265
+ )
266
+
267
+ def test_dt_accessor_ambiguous_freq_conversions(self):
268
+ # GH#11295
269
+ # ambiguous time error on the conversions
270
+ ser = Series(date_range("2015-01-01", "2016-01-01", freq="min"), name="xxx")
271
+ ser = ser.dt.tz_localize("UTC").dt.tz_convert("America/Chicago")
272
+
273
+ exp_values = date_range(
274
+ "2015-01-01", "2016-01-01", freq="min", tz="UTC"
275
+ ).tz_convert("America/Chicago")
276
+ # freq not preserved by tz_localize above
277
+ exp_values = exp_values._with_freq(None)
278
+ expected = Series(exp_values, name="xxx")
279
+ tm.assert_series_equal(ser, expected)
280
+
281
+ def test_dt_accessor_not_writeable(self):
282
+ # no setting allowed
283
+ ser = Series(date_range("20130101", periods=5, freq="D"), name="xxx")
284
+ with pytest.raises(ValueError, match="modifications"):
285
+ ser.dt.hour = 5
286
+
287
+ # trying to set a copy
288
+ with tm.raises_chained_assignment_error():
289
+ ser.dt.hour[0] = 5
290
+
291
+ @pytest.mark.parametrize(
292
+ "method, dates",
293
+ [
294
+ ["round", ["2012-01-02", "2012-01-02", "2012-01-01"]],
295
+ ["floor", ["2012-01-01", "2012-01-01", "2012-01-01"]],
296
+ ["ceil", ["2012-01-02", "2012-01-02", "2012-01-02"]],
297
+ ],
298
+ )
299
+ def test_dt_round(self, method, dates):
300
+ # round
301
+ ser = Series(
302
+ pd.to_datetime(
303
+ ["2012-01-01 13:00:00", "2012-01-01 12:01:00", "2012-01-01 08:00:00"]
304
+ ),
305
+ name="xxx",
306
+ )
307
+ result = getattr(ser.dt, method)("D")
308
+ expected = Series(pd.to_datetime(dates), name="xxx")
309
+ tm.assert_series_equal(result, expected)
310
+
311
+ def test_dt_round_tz(self):
312
+ ser = Series(
313
+ pd.to_datetime(
314
+ ["2012-01-01 13:00:00", "2012-01-01 12:01:00", "2012-01-01 08:00:00"]
315
+ ),
316
+ name="xxx",
317
+ )
318
+ result = ser.dt.tz_localize("UTC").dt.tz_convert("US/Eastern").dt.round("D")
319
+
320
+ exp_values = pd.to_datetime(
321
+ ["2012-01-01", "2012-01-01", "2012-01-01"]
322
+ ).tz_localize("US/Eastern")
323
+ expected = Series(exp_values, name="xxx")
324
+ tm.assert_series_equal(result, expected)
325
+
326
+ @pytest.mark.parametrize("method", ["ceil", "round", "floor"])
327
+ def test_dt_round_tz_ambiguous(self, method):
328
+ # GH 18946 round near "fall back" DST
329
+ df1 = DataFrame(
330
+ [
331
+ pd.to_datetime("2017-10-29 02:00:00+02:00", utc=True),
332
+ pd.to_datetime("2017-10-29 02:00:00+01:00", utc=True),
333
+ pd.to_datetime("2017-10-29 03:00:00+01:00", utc=True),
334
+ ],
335
+ columns=["date"],
336
+ )
337
+ df1["date"] = df1["date"].dt.tz_convert("Europe/Madrid")
338
+ # infer
339
+ result = getattr(df1.date.dt, method)("h", ambiguous="infer")
340
+ expected = df1["date"]
341
+ tm.assert_series_equal(result, expected)
342
+
343
+ # bool-array
344
+ result = getattr(df1.date.dt, method)("h", ambiguous=[True, False, False])
345
+ tm.assert_series_equal(result, expected)
346
+
347
+ # NaT
348
+ result = getattr(df1.date.dt, method)("h", ambiguous="NaT")
349
+ expected = df1["date"].copy()
350
+ expected.iloc[0:2] = pd.NaT
351
+ tm.assert_series_equal(result, expected)
352
+
353
+ # raise
354
+ with tm.external_error_raised(ValueError):
355
+ getattr(df1.date.dt, method)("h", ambiguous="raise")
356
+
357
+ @pytest.mark.parametrize(
358
+ "method, ts_str, freq",
359
+ [
360
+ ["ceil", "2018-03-11 01:59:00-0600", "5min"],
361
+ ["round", "2018-03-11 01:59:00-0600", "5min"],
362
+ ["floor", "2018-03-11 03:01:00-0500", "2h"],
363
+ ],
364
+ )
365
+ def test_dt_round_tz_nonexistent(self, method, ts_str, freq):
366
+ # GH 23324 round near "spring forward" DST
367
+ ser = Series([pd.Timestamp(ts_str, tz="America/Chicago").as_unit("s")])
368
+ result = getattr(ser.dt, method)(freq, nonexistent="shift_forward")
369
+ expected = Series(
370
+ [pd.Timestamp("2018-03-11 03:00:00", tz="America/Chicago").as_unit("s")]
371
+ )
372
+ tm.assert_series_equal(result, expected)
373
+
374
+ result = getattr(ser.dt, method)(freq, nonexistent="NaT")
375
+ expected = Series([pd.NaT]).dt.tz_localize(result.dt.tz)
376
+ tm.assert_series_equal(result, expected)
377
+
378
+ with pytest.raises(ValueError, match="2018-03-11 02:00:00"):
379
+ getattr(ser.dt, method)(freq, nonexistent="raise")
380
+
381
+ @pytest.mark.parametrize("freq", ["ns", "us", "1000us"])
382
+ def test_dt_round_nonnano_higher_resolution_no_op(self, freq):
383
+ # GH 52761
384
+ ser = Series(
385
+ ["2020-05-31 08:00:00", "2000-12-31 04:00:05", "1800-03-14 07:30:20"],
386
+ dtype="datetime64[ms]",
387
+ )
388
+ expected = ser.copy()
389
+ result = ser.dt.round(freq)
390
+ tm.assert_series_equal(result, expected)
391
+
392
+ assert not np.shares_memory(ser.array._ndarray, result.array._ndarray)
393
+
394
+ def test_dt_namespace_accessor_categorical(self):
395
+ # GH 19468
396
+ dti = DatetimeIndex(["20171111", "20181212"]).repeat(2)
397
+ ser = Series(pd.Categorical(dti), name="foo")
398
+ result = ser.dt.year
399
+ expected = Series([2017, 2017, 2018, 2018], dtype="int32", name="foo")
400
+ tm.assert_series_equal(result, expected)
401
+
402
+ def test_dt_tz_localize_categorical(self, tz_aware_fixture):
403
+ # GH 27952
404
+ tz = tz_aware_fixture
405
+ datetimes = Series(
406
+ ["2019-01-01", "2019-01-01", "2019-01-02"], dtype="datetime64[ns]"
407
+ )
408
+ categorical = datetimes.astype("category")
409
+ result = categorical.dt.tz_localize(tz)
410
+ expected = datetimes.dt.tz_localize(tz)
411
+ tm.assert_series_equal(result, expected)
412
+
413
+ def test_dt_tz_convert_categorical(self, tz_aware_fixture):
414
+ # GH 27952
415
+ tz = tz_aware_fixture
416
+ datetimes = Series(
417
+ ["2019-01-01", "2019-01-01", "2019-01-02"], dtype="datetime64[ns, MET]"
418
+ )
419
+ categorical = datetimes.astype("category")
420
+ result = categorical.dt.tz_convert(tz)
421
+ expected = datetimes.dt.tz_convert(tz)
422
+ tm.assert_series_equal(result, expected)
423
+
424
+ @pytest.mark.parametrize("accessor", ["year", "month", "day"])
425
+ def test_dt_other_accessors_categorical(self, accessor):
426
+ # GH 27952
427
+ datetimes = Series(
428
+ ["2018-01-01", "2018-01-01", "2019-01-02"], dtype="datetime64[ns]"
429
+ )
430
+ categorical = datetimes.astype("category")
431
+ result = getattr(categorical.dt, accessor)
432
+ expected = getattr(datetimes.dt, accessor)
433
+ tm.assert_series_equal(result, expected)
434
+
435
+ def test_dt_accessor_no_new_attributes(self):
436
+ # https://github.com/pandas-dev/pandas/issues/10673
437
+ ser = Series(date_range("20130101", periods=5, freq="D"))
438
+ with pytest.raises(AttributeError, match="You cannot add any new attribute"):
439
+ ser.dt.xlabel = "a"
440
+
441
+ @pytest.mark.parametrize(
442
+ "time_locale",
443
+ [None, *tm.get_locales()],
444
+ )
445
+ def test_dt_accessor_datetime_name_accessors(self, time_locale):
446
+ # Test Monday -> Sunday and January -> December, in that sequence
447
+ if time_locale is None:
448
+ # If the time_locale is None, day-name and month_name should
449
+ # return the english attributes
450
+ expected_days = [
451
+ "Monday",
452
+ "Tuesday",
453
+ "Wednesday",
454
+ "Thursday",
455
+ "Friday",
456
+ "Saturday",
457
+ "Sunday",
458
+ ]
459
+ expected_months = [
460
+ "January",
461
+ "February",
462
+ "March",
463
+ "April",
464
+ "May",
465
+ "June",
466
+ "July",
467
+ "August",
468
+ "September",
469
+ "October",
470
+ "November",
471
+ "December",
472
+ ]
473
+ else:
474
+ with tm.set_locale(time_locale, locale.LC_TIME):
475
+ expected_days = calendar.day_name[:]
476
+ expected_months = calendar.month_name[1:]
477
+
478
+ ser = Series(date_range(freq="D", start=datetime(1998, 1, 1), periods=365))
479
+ english_days = [
480
+ "Monday",
481
+ "Tuesday",
482
+ "Wednesday",
483
+ "Thursday",
484
+ "Friday",
485
+ "Saturday",
486
+ "Sunday",
487
+ ]
488
+ for day, name, eng_name in zip(range(4, 11), expected_days, english_days):
489
+ name = name.capitalize()
490
+ assert ser.dt.day_name(locale=time_locale)[day] == name
491
+ assert ser.dt.day_name(locale=None)[day] == eng_name
492
+ ser = pd.concat([ser, Series([pd.NaT])])
493
+ assert np.isnan(ser.dt.day_name(locale=time_locale).iloc[-1])
494
+
495
+ ser = Series(date_range(freq="ME", start="2012", end="2013"))
496
+ result = ser.dt.month_name(locale=time_locale)
497
+ expected = Series([month.capitalize() for month in expected_months])
498
+
499
+ # work around https://github.com/pandas-dev/pandas/issues/22342
500
+ result = result.str.normalize("NFD")
501
+ expected = expected.str.normalize("NFD")
502
+
503
+ tm.assert_series_equal(result, expected)
504
+
505
+ for s_date, expected in zip(ser, expected_months):
506
+ result = s_date.month_name(locale=time_locale)
507
+ expected = expected.capitalize()
508
+
509
+ result = unicodedata.normalize("NFD", result)
510
+ expected = unicodedata.normalize("NFD", expected)
511
+
512
+ assert result == expected
513
+
514
+ ser = pd.concat([ser, Series([pd.NaT])])
515
+ assert np.isnan(ser.dt.month_name(locale=time_locale).iloc[-1])
516
+
517
+ def test_strftime(self):
518
+ # GH 10086
519
+ ser = Series(date_range("20130101", periods=5))
520
+ result = ser.dt.strftime("%Y/%m/%d")
521
+ expected = Series(
522
+ ["2013/01/01", "2013/01/02", "2013/01/03", "2013/01/04", "2013/01/05"]
523
+ )
524
+ tm.assert_series_equal(result, expected)
525
+
526
+ ser = Series(date_range("2015-02-03 11:22:33.4567", periods=5))
527
+ result = ser.dt.strftime("%Y/%m/%d %H-%M-%S")
528
+ expected = Series(
529
+ [
530
+ "2015/02/03 11-22-33",
531
+ "2015/02/04 11-22-33",
532
+ "2015/02/05 11-22-33",
533
+ "2015/02/06 11-22-33",
534
+ "2015/02/07 11-22-33",
535
+ ]
536
+ )
537
+ tm.assert_series_equal(result, expected)
538
+
539
+ ser = Series(period_range("20130101", periods=5))
540
+ result = ser.dt.strftime("%Y/%m/%d")
541
+ expected = Series(
542
+ ["2013/01/01", "2013/01/02", "2013/01/03", "2013/01/04", "2013/01/05"]
543
+ )
544
+ tm.assert_series_equal(result, expected)
545
+
546
+ ser = Series(period_range("2015-02-03 11:22:33.4567", periods=5, freq="s"))
547
+ result = ser.dt.strftime("%Y/%m/%d %H-%M-%S")
548
+ expected = Series(
549
+ [
550
+ "2015/02/03 11-22-33",
551
+ "2015/02/03 11-22-34",
552
+ "2015/02/03 11-22-35",
553
+ "2015/02/03 11-22-36",
554
+ "2015/02/03 11-22-37",
555
+ ]
556
+ )
557
+ tm.assert_series_equal(result, expected)
558
+
559
+ def test_strftime_dt64_days(self):
560
+ ser = Series(date_range("20130101", periods=5))
561
+ ser.iloc[0] = pd.NaT
562
+ result = ser.dt.strftime("%Y/%m/%d")
563
+ expected = Series(
564
+ [np.nan, "2013/01/02", "2013/01/03", "2013/01/04", "2013/01/05"]
565
+ )
566
+ tm.assert_series_equal(result, expected)
567
+
568
+ datetime_index = date_range("20150301", periods=5)
569
+ result = datetime_index.strftime("%Y/%m/%d")
570
+
571
+ expected = Index(
572
+ ["2015/03/01", "2015/03/02", "2015/03/03", "2015/03/04", "2015/03/05"],
573
+ )
574
+ # dtype may be S10 or U10 depending on python version
575
+ tm.assert_index_equal(result, expected)
576
+
577
+ def test_strftime_period_days(self, using_infer_string):
578
+ period_index = period_range("20150301", periods=5)
579
+ result = period_index.strftime("%Y/%m/%d")
580
+ expected = Index(
581
+ ["2015/03/01", "2015/03/02", "2015/03/03", "2015/03/04", "2015/03/05"],
582
+ dtype="=U10",
583
+ )
584
+ if using_infer_string:
585
+ expected = expected.astype(StringDtype(na_value=np.nan))
586
+ tm.assert_index_equal(result, expected)
587
+
588
+ def test_strftime_dt64_microsecond_resolution(self):
589
+ ser = Series([datetime(2013, 1, 1, 2, 32, 59), datetime(2013, 1, 2, 14, 32, 1)])
590
+ result = ser.dt.strftime("%Y-%m-%d %H:%M:%S")
591
+ expected = Series(["2013-01-01 02:32:59", "2013-01-02 14:32:01"])
592
+ tm.assert_series_equal(result, expected)
593
+
594
+ def test_strftime_period_hours(self):
595
+ ser = Series(period_range("20130101", periods=4, freq="h"))
596
+ result = ser.dt.strftime("%Y/%m/%d %H:%M:%S")
597
+ expected = Series(
598
+ [
599
+ "2013/01/01 00:00:00",
600
+ "2013/01/01 01:00:00",
601
+ "2013/01/01 02:00:00",
602
+ "2013/01/01 03:00:00",
603
+ ]
604
+ )
605
+ tm.assert_series_equal(result, expected)
606
+
607
+ def test_strftime_period_minutes(self):
608
+ ser = Series(period_range("20130101", periods=4, freq="ms"))
609
+ result = ser.dt.strftime("%Y/%m/%d %H:%M:%S.%l")
610
+ expected = Series(
611
+ [
612
+ "2013/01/01 00:00:00.000",
613
+ "2013/01/01 00:00:00.001",
614
+ "2013/01/01 00:00:00.002",
615
+ "2013/01/01 00:00:00.003",
616
+ ]
617
+ )
618
+ tm.assert_series_equal(result, expected)
619
+
620
+ @pytest.mark.parametrize(
621
+ "data",
622
+ [
623
+ DatetimeIndex(["2019-01-01", pd.NaT]),
624
+ PeriodIndex(["2019-01-01", pd.NaT], dtype="period[D]"),
625
+ ],
626
+ )
627
+ def test_strftime_nat(self, data):
628
+ # GH 29578
629
+ ser = Series(data)
630
+ result = ser.dt.strftime("%Y-%m-%d")
631
+ expected = Series(["2019-01-01", np.nan])
632
+ tm.assert_series_equal(result, expected)
633
+
634
+ @pytest.mark.parametrize(
635
+ "data", [DatetimeIndex([pd.NaT]), PeriodIndex([pd.NaT], dtype="period[D]")]
636
+ )
637
+ def test_strftime_all_nat(self, data):
638
+ # https://github.com/pandas-dev/pandas/issues/45858
639
+ ser = Series(data)
640
+ with tm.assert_produces_warning(None):
641
+ result = ser.dt.strftime("%Y-%m-%d")
642
+ expected = Series([np.nan], dtype="str")
643
+ tm.assert_series_equal(result, expected)
644
+
645
+ def test_valid_dt_with_missing_values(self):
646
+ # GH 8689
647
+ ser = Series(date_range("20130101", periods=5, freq="D"))
648
+ ser.iloc[2] = pd.NaT
649
+
650
+ for attr in ["microsecond", "nanosecond", "second", "minute", "hour", "day"]:
651
+ expected = getattr(ser.dt, attr).copy()
652
+ expected.iloc[2] = np.nan
653
+ result = getattr(ser.dt, attr)
654
+ tm.assert_series_equal(result, expected)
655
+
656
+ result = ser.dt.date
657
+ expected = Series(
658
+ [
659
+ date(2013, 1, 1),
660
+ date(2013, 1, 2),
661
+ pd.NaT,
662
+ date(2013, 1, 4),
663
+ date(2013, 1, 5),
664
+ ],
665
+ dtype="object",
666
+ )
667
+ tm.assert_series_equal(result, expected)
668
+
669
+ result = ser.dt.time
670
+ expected = Series([time(0), time(0), pd.NaT, time(0), time(0)], dtype="object")
671
+ tm.assert_series_equal(result, expected)
672
+
673
+ def test_dt_accessor_api(self):
674
+ # GH 9322
675
+ from pandas.core.indexes.accessors import (
676
+ CombinedDatetimelikeProperties,
677
+ DatetimeProperties,
678
+ )
679
+
680
+ assert Series.dt is CombinedDatetimelikeProperties
681
+
682
+ ser = Series(date_range("2000-01-01", periods=3))
683
+ assert isinstance(ser.dt, DatetimeProperties)
684
+
685
+ @pytest.mark.parametrize(
686
+ "data",
687
+ [
688
+ np.arange(5),
689
+ list("abcde"),
690
+ np.random.default_rng(2).standard_normal(5),
691
+ ],
692
+ )
693
+ def test_dt_accessor_invalid(self, data):
694
+ # GH#9322 check that series with incorrect dtypes don't have attr
695
+ ser = Series(data)
696
+ with pytest.raises(AttributeError, match="only use .dt accessor"):
697
+ ser.dt
698
+ assert not hasattr(ser, "dt")
699
+
700
+ def test_dt_accessor_updates_on_inplace(self):
701
+ ser = Series(date_range("2018-01-01", periods=10))
702
+ ser[2] = None
703
+ result = ser.fillna(pd.Timestamp("2018-01-01"), inplace=True)
704
+ assert result is ser
705
+ result = ser.dt.date
706
+ assert result[0] == result[2]
707
+
708
+ def test_date_tz(self):
709
+ # GH11757
710
+ rng = DatetimeIndex(
711
+ ["2014-04-04 23:56", "2014-07-18 21:24", "2015-11-22 22:14"],
712
+ tz="US/Eastern",
713
+ )
714
+ ser = Series(rng)
715
+ expected = Series([date(2014, 4, 4), date(2014, 7, 18), date(2015, 11, 22)])
716
+ tm.assert_series_equal(ser.dt.date, expected)
717
+ tm.assert_series_equal(ser.apply(lambda x: x.date()), expected)
718
+
719
+ def test_dt_timetz_accessor(self, tz_naive_fixture):
720
+ # GH21358
721
+ tz = maybe_get_tz(tz_naive_fixture)
722
+
723
+ dtindex = DatetimeIndex(
724
+ ["2014-04-04 23:56", "2014-07-18 21:24", "2015-11-22 22:14"], tz=tz
725
+ )
726
+ ser = Series(dtindex)
727
+ expected = Series(
728
+ [time(23, 56, tzinfo=tz), time(21, 24, tzinfo=tz), time(22, 14, tzinfo=tz)]
729
+ )
730
+ result = ser.dt.timetz
731
+ tm.assert_series_equal(result, expected)
732
+
733
+ @pytest.mark.parametrize(
734
+ "input_series, expected_output",
735
+ [
736
+ [["2020-01-01"], [[2020, 1, 3]]],
737
+ [[pd.NaT], [[None, None, None]]],
738
+ [["2019-12-31", "2019-12-29"], [[2020, 1, 2], [2019, 52, 7]]],
739
+ [["2010-01-01", pd.NaT], [[2009, 53, 5], [None, None, None]]],
740
+ # see GH#36032
741
+ [["2016-01-08", "2016-01-04"], [[2016, 1, 5], [2016, 1, 1]]],
742
+ [["2016-01-07", "2016-01-01"], [[2016, 1, 4], [2015, 53, 5]]],
743
+ ],
744
+ )
745
+ def test_isocalendar(self, input_series, expected_output):
746
+ result = pd.to_datetime(Series(input_series)).dt.isocalendar()
747
+ expected_frame = DataFrame(
748
+ expected_output, columns=["year", "week", "day"], dtype="UInt32"
749
+ )
750
+ tm.assert_frame_equal(result, expected_frame)
751
+
752
+ def test_hour_index(self):
753
+ dt_series = Series(
754
+ date_range(start="2021-01-01", periods=5, freq="h"),
755
+ index=[2, 6, 7, 8, 11],
756
+ dtype="category",
757
+ )
758
+ result = dt_series.dt.hour
759
+ expected = Series(
760
+ [0, 1, 2, 3, 4],
761
+ dtype="int32",
762
+ index=[2, 6, 7, 8, 11],
763
+ )
764
+ tm.assert_series_equal(result, expected)
765
+
766
+
767
+ class TestSeriesPeriodValuesDtAccessor:
768
+ @pytest.mark.parametrize(
769
+ "input_vals",
770
+ [
771
+ [Period("2016-01", freq="M"), Period("2016-02", freq="M")],
772
+ [Period("2016-01-01", freq="D"), Period("2016-01-02", freq="D")],
773
+ [
774
+ Period("2016-01-01 00:00:00", freq="h"),
775
+ Period("2016-01-01 01:00:00", freq="h"),
776
+ ],
777
+ [
778
+ Period("2016-01-01 00:00:00", freq="M"),
779
+ Period("2016-01-01 00:01:00", freq="M"),
780
+ ],
781
+ [
782
+ Period("2016-01-01 00:00:00", freq="s"),
783
+ Period("2016-01-01 00:00:01", freq="s"),
784
+ ],
785
+ ],
786
+ )
787
+ def test_end_time_timevalues(self, input_vals):
788
+ # GH#17157
789
+ # Check that the time part of the Period is adjusted by end_time
790
+ # when using the dt accessor on a Series
791
+ dtype = pd.PeriodDtype(input_vals[0].freq)
792
+ input_vals = PeriodArray._from_sequence(np.asarray(input_vals), dtype=dtype)
793
+
794
+ ser = Series(input_vals)
795
+ result = ser.dt.end_time
796
+ expected = ser.apply(lambda x: x.end_time)
797
+ tm.assert_series_equal(result, expected)
798
+
799
+ @pytest.mark.parametrize("input_vals", [("2001"), ("NaT")])
800
+ def test_to_period(self, input_vals):
801
+ # GH#21205
802
+ expected = Series([input_vals], dtype="Period[D]")
803
+ result = Series([input_vals], dtype="datetime64[ns]").dt.to_period("D")
804
+ tm.assert_series_equal(result, expected)
805
+
806
+
807
+ def test_normalize_pre_epoch_dates():
808
+ # GH: 36294
809
+ ser = pd.to_datetime(Series(["1969-01-01 09:00:00", "2016-01-01 09:00:00"]))
810
+ result = ser.dt.normalize()
811
+ expected = pd.to_datetime(Series(["1969-01-01", "2016-01-01"]))
812
+ tm.assert_series_equal(result, expected)
813
+
814
+
815
+ def test_day_attribute_non_nano_beyond_int32():
816
+ # GH 52386
817
+ data = np.array(
818
+ [
819
+ 136457654736252,
820
+ 134736784364431,
821
+ 245345345545332,
822
+ 223432411,
823
+ 2343241,
824
+ 3634548734,
825
+ 23234,
826
+ ],
827
+ dtype="timedelta64[s]",
828
+ )
829
+ ser = Series(data)
830
+ result = ser.dt.days
831
+ expected = Series([1579371003, 1559453522, 2839645203, 2586, 27, 42066, 0])
832
+ tm.assert_series_equal(result, expected)
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_list_accessor.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import pytest
4
+
5
+ from pandas import (
6
+ ArrowDtype,
7
+ Series,
8
+ )
9
+ import pandas._testing as tm
10
+
11
+ pa = pytest.importorskip("pyarrow")
12
+
13
+
14
+ @pytest.mark.parametrize(
15
+ "list_dtype",
16
+ (
17
+ pa.list_(pa.int64()),
18
+ pa.list_(pa.int64(), list_size=3),
19
+ pa.large_list(pa.int64()),
20
+ ),
21
+ )
22
+ def test_list_getitem(list_dtype):
23
+ ser = Series(
24
+ [[1, 2, 3], [4, None, 5], None],
25
+ dtype=ArrowDtype(list_dtype),
26
+ name="a",
27
+ )
28
+ actual = ser.list[1]
29
+ expected = Series([2, None, None], dtype="int64[pyarrow]", name="a")
30
+ tm.assert_series_equal(actual, expected)
31
+
32
+
33
+ def test_list_getitem_index():
34
+ # GH 58425
35
+ ser = Series(
36
+ [[1, 2, 3], [4, None, 5], None],
37
+ dtype=ArrowDtype(pa.list_(pa.int64())),
38
+ index=[1, 3, 7],
39
+ name="a",
40
+ )
41
+ actual = ser.list[1]
42
+ expected = Series(
43
+ [2, None, None],
44
+ dtype="int64[pyarrow]",
45
+ index=[1, 3, 7],
46
+ name="a",
47
+ )
48
+ tm.assert_series_equal(actual, expected)
49
+
50
+
51
+ def test_list_getitem_slice():
52
+ ser = Series(
53
+ [[1, 2, 3], [4, None, 5], None],
54
+ dtype=ArrowDtype(pa.list_(pa.int64())),
55
+ index=[1, 3, 7],
56
+ name="a",
57
+ )
58
+ actual = ser.list[1:None:None]
59
+ expected = Series(
60
+ [[2, 3], [None, 5], None],
61
+ dtype=ArrowDtype(pa.list_(pa.int64())),
62
+ index=[1, 3, 7],
63
+ name="a",
64
+ )
65
+ tm.assert_series_equal(actual, expected)
66
+
67
+
68
+ def test_list_len():
69
+ ser = Series(
70
+ [[1, 2, 3], [4, None], None],
71
+ dtype=ArrowDtype(pa.list_(pa.int64())),
72
+ name="a",
73
+ )
74
+ actual = ser.list.len()
75
+ expected = Series([3, 2, None], dtype=ArrowDtype(pa.int32()), name="a")
76
+ tm.assert_series_equal(actual, expected)
77
+
78
+
79
+ def test_list_flatten():
80
+ ser = Series(
81
+ [[1, 2, 3], None, [4, None], [], [7, 8]],
82
+ dtype=ArrowDtype(pa.list_(pa.int64())),
83
+ name="a",
84
+ )
85
+ actual = ser.list.flatten()
86
+ expected = Series(
87
+ [1, 2, 3, 4, None, 7, 8],
88
+ dtype=ArrowDtype(pa.int64()),
89
+ index=[0, 0, 0, 2, 2, 4, 4],
90
+ name="a",
91
+ )
92
+ tm.assert_series_equal(actual, expected)
93
+
94
+
95
+ def test_list_getitem_slice_invalid():
96
+ ser = Series(
97
+ [[1, 2, 3], [4, None, 5], None],
98
+ dtype=ArrowDtype(pa.list_(pa.int64())),
99
+ )
100
+ with tm.external_error_raised(pa.ArrowInvalid):
101
+ ser.list[1:None:0]
102
+
103
+
104
+ def test_list_accessor_non_list_dtype():
105
+ ser = Series(
106
+ [1, 2, 4],
107
+ dtype=ArrowDtype(pa.int64()),
108
+ )
109
+ with pytest.raises(
110
+ AttributeError,
111
+ match=re.escape(
112
+ "Can only use the '.list' accessor with 'list[pyarrow]' dtype, "
113
+ "not int64[pyarrow]."
114
+ ),
115
+ ):
116
+ ser.list[1:None:0]
117
+
118
+
119
+ @pytest.mark.parametrize(
120
+ "list_dtype",
121
+ (
122
+ pa.list_(pa.int64()),
123
+ pa.list_(pa.int64(), list_size=3),
124
+ pa.large_list(pa.int64()),
125
+ ),
126
+ )
127
+ def test_list_getitem_invalid_index(list_dtype):
128
+ ser = Series(
129
+ [[1, 2, 3], [4, None, 5], None],
130
+ dtype=ArrowDtype(list_dtype),
131
+ )
132
+ with tm.external_error_raised(pa.ArrowInvalid):
133
+ ser.list[-1]
134
+ with tm.external_error_raised(pa.ArrowInvalid):
135
+ ser.list[5]
136
+ with pytest.raises(ValueError, match="key must be an int or slice, got str"):
137
+ ser.list["abc"]
138
+
139
+
140
+ def test_list_accessor_not_iterable():
141
+ ser = Series(
142
+ [[1, 2, 3], [4, None], None],
143
+ dtype=ArrowDtype(pa.list_(pa.int64())),
144
+ )
145
+ with pytest.raises(TypeError, match="'ListAccessor' object is not iterable"):
146
+ iter(ser.list)
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_sparse_accessor.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from pandas import Series
2
+
3
+
4
+ class TestSparseAccessor:
5
+ def test_sparse_accessor_updates_on_inplace(self):
6
+ ser = Series([1, 1, 2, 3], dtype="Sparse[int]")
7
+ return_value = ser.drop([0, 1], inplace=True)
8
+ assert return_value is None
9
+ assert ser.sparse.density == 1.0
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_str_accessor.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas import Series
4
+ import pandas._testing as tm
5
+
6
+
7
+ class TestStrAccessor:
8
+ def test_str_attribute(self):
9
+ # GH#9068
10
+ methods = ["strip", "rstrip", "lstrip"]
11
+ ser = Series([" jack", "jill ", " jesse ", "frank"])
12
+ for method in methods:
13
+ expected = Series([getattr(str, method)(x) for x in ser.values])
14
+ tm.assert_series_equal(getattr(Series.str, method)(ser.str), expected)
15
+
16
+ # str accessor only valid with string values
17
+ ser = Series(range(5))
18
+ msg = "Can only use .str accessor with string values, not integer"
19
+ with pytest.raises(AttributeError, match=msg):
20
+ ser.str.repeat(2)
21
+
22
+ def test_str_accessor_updates_on_inplace(self):
23
+ ser = Series(list("abc"))
24
+ return_value = ser.drop([0], inplace=True)
25
+ assert return_value is None
26
+ assert len(ser.str.lower()) == 2
.venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_struct_accessor.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import pytest
4
+
5
+ from pandas import (
6
+ ArrowDtype,
7
+ DataFrame,
8
+ Index,
9
+ Series,
10
+ )
11
+ import pandas._testing as tm
12
+
13
+ pa = pytest.importorskip("pyarrow")
14
+ pc = pytest.importorskip("pyarrow.compute")
15
+
16
+
17
+ def test_struct_accessor_dtypes():
18
+ ser = Series(
19
+ [],
20
+ dtype=ArrowDtype(
21
+ pa.struct(
22
+ [
23
+ ("int_col", pa.int64()),
24
+ ("string_col", pa.string()),
25
+ (
26
+ "struct_col",
27
+ pa.struct(
28
+ [
29
+ ("int_col", pa.int64()),
30
+ ("float_col", pa.float64()),
31
+ ]
32
+ ),
33
+ ),
34
+ ]
35
+ )
36
+ ),
37
+ )
38
+ actual = ser.struct.dtypes
39
+ expected = Series(
40
+ [
41
+ ArrowDtype(pa.int64()),
42
+ ArrowDtype(pa.string()),
43
+ ArrowDtype(
44
+ pa.struct(
45
+ [
46
+ ("int_col", pa.int64()),
47
+ ("float_col", pa.float64()),
48
+ ]
49
+ )
50
+ ),
51
+ ],
52
+ index=Index(["int_col", "string_col", "struct_col"]),
53
+ )
54
+ tm.assert_series_equal(actual, expected)
55
+
56
+
57
+ def test_struct_accessor_field():
58
+ index = Index([-100, 42, 123])
59
+ ser = Series(
60
+ [
61
+ {"rice": 1.0, "maize": -1, "wheat": "a"},
62
+ {"rice": 2.0, "maize": 0, "wheat": "b"},
63
+ {"rice": 3.0, "maize": 1, "wheat": "c"},
64
+ ],
65
+ dtype=ArrowDtype(
66
+ pa.struct(
67
+ [
68
+ ("rice", pa.float64()),
69
+ ("maize", pa.int64()),
70
+ ("wheat", pa.string()),
71
+ ]
72
+ )
73
+ ),
74
+ index=index,
75
+ )
76
+ by_name = ser.struct.field("maize")
77
+ by_name_expected = Series(
78
+ [-1, 0, 1],
79
+ dtype=ArrowDtype(pa.int64()),
80
+ index=index,
81
+ name="maize",
82
+ )
83
+ tm.assert_series_equal(by_name, by_name_expected)
84
+
85
+ by_index = ser.struct.field(2)
86
+ by_index_expected = Series(
87
+ ["a", "b", "c"],
88
+ dtype=ArrowDtype(pa.string()),
89
+ index=index,
90
+ name="wheat",
91
+ )
92
+ tm.assert_series_equal(by_index, by_index_expected)
93
+
94
+
95
+ def test_struct_accessor_field_with_invalid_name_or_index():
96
+ ser = Series([], dtype=ArrowDtype(pa.struct([("field", pa.int64())])))
97
+
98
+ with pytest.raises(ValueError, match="name_or_index must be an int, str,"):
99
+ ser.struct.field(1.1)
100
+
101
+
102
+ def test_struct_accessor_explode():
103
+ index = Index([-100, 42, 123])
104
+ ser = Series(
105
+ [
106
+ {"painted": 1, "snapping": {"sea": "green"}},
107
+ {"painted": 2, "snapping": {"sea": "leatherback"}},
108
+ {"painted": 3, "snapping": {"sea": "hawksbill"}},
109
+ ],
110
+ dtype=ArrowDtype(
111
+ pa.struct(
112
+ [
113
+ ("painted", pa.int64()),
114
+ ("snapping", pa.struct([("sea", pa.string())])),
115
+ ]
116
+ )
117
+ ),
118
+ index=index,
119
+ )
120
+ actual = ser.struct.explode()
121
+ expected = DataFrame(
122
+ {
123
+ "painted": Series([1, 2, 3], index=index, dtype=ArrowDtype(pa.int64())),
124
+ "snapping": Series(
125
+ [{"sea": "green"}, {"sea": "leatherback"}, {"sea": "hawksbill"}],
126
+ index=index,
127
+ dtype=ArrowDtype(pa.struct([("sea", pa.string())])),
128
+ ),
129
+ },
130
+ )
131
+ tm.assert_frame_equal(actual, expected)
132
+
133
+
134
+ @pytest.mark.parametrize(
135
+ "invalid",
136
+ [
137
+ pytest.param(Series([1, 2, 3], dtype="int64"), id="int64"),
138
+ pytest.param(
139
+ Series(["a", "b", "c"], dtype="string[pyarrow]"), id="string-pyarrow"
140
+ ),
141
+ ],
142
+ )
143
+ def test_struct_accessor_api_for_invalid(invalid):
144
+ with pytest.raises(
145
+ AttributeError,
146
+ match=re.escape(
147
+ "Can only use the '.struct' accessor with 'struct[pyarrow]' dtype, "
148
+ f"not {invalid.dtype}."
149
+ ),
150
+ ):
151
+ invalid.struct
152
+
153
+
154
+ @pytest.mark.parametrize(
155
+ ["indices", "name"],
156
+ [
157
+ (0, "int_col"),
158
+ ([1, 2], "str_col"),
159
+ (pc.field("int_col"), "int_col"),
160
+ ("int_col", "int_col"),
161
+ (b"string_col", b"string_col"),
162
+ ([b"string_col"], "string_col"),
163
+ ],
164
+ )
165
+ def test_struct_accessor_field_expanded(indices, name):
166
+ arrow_type = pa.struct(
167
+ [
168
+ ("int_col", pa.int64()),
169
+ (
170
+ "struct_col",
171
+ pa.struct(
172
+ [
173
+ ("int_col", pa.int64()),
174
+ ("float_col", pa.float64()),
175
+ ("str_col", pa.string()),
176
+ ]
177
+ ),
178
+ ),
179
+ (b"string_col", pa.string()),
180
+ ]
181
+ )
182
+
183
+ data = pa.array([], type=arrow_type)
184
+ ser = Series(data, dtype=ArrowDtype(arrow_type))
185
+ expected = pc.struct_field(data, indices)
186
+ result = ser.struct.field(indices)
187
+ tm.assert_equal(result.array._pa_array.combine_chunks(), expected)
188
+ assert result.name == name
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_datetime.py ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Also test support for datetime64[ns] in Series / DataFrame
3
+ """
4
+
5
+ from datetime import (
6
+ datetime,
7
+ timedelta,
8
+ )
9
+ import re
10
+
11
+ from dateutil.tz import (
12
+ gettz,
13
+ tzutc,
14
+ )
15
+ import numpy as np
16
+ import pytest
17
+
18
+ from pandas._libs import index as libindex
19
+
20
+ import pandas as pd
21
+ from pandas import (
22
+ DataFrame,
23
+ Series,
24
+ Timestamp,
25
+ date_range,
26
+ period_range,
27
+ )
28
+ import pandas._testing as tm
29
+
30
+
31
+ def test_fancy_getitem():
32
+ dti = date_range(
33
+ freq="WOM-1FRI", start=datetime(2005, 1, 1), end=datetime(2010, 1, 1)
34
+ )
35
+
36
+ s = Series(np.arange(len(dti)), index=dti)
37
+
38
+ assert s["1/2/2009"] == 48
39
+ assert s["2009-1-2"] == 48
40
+ assert s[datetime(2009, 1, 2)] == 48
41
+ assert s[Timestamp(datetime(2009, 1, 2))] == 48
42
+ with pytest.raises(KeyError, match=r"^'2009-1-3'$"):
43
+ s["2009-1-3"]
44
+ tm.assert_series_equal(
45
+ s["3/6/2009":"2009-06-05"], s[datetime(2009, 3, 6) : datetime(2009, 6, 5)]
46
+ )
47
+
48
+
49
+ def test_fancy_setitem():
50
+ dti = date_range(
51
+ freq="WOM-1FRI", start=datetime(2005, 1, 1), end=datetime(2010, 1, 1)
52
+ )
53
+
54
+ s = Series(np.arange(len(dti)), index=dti)
55
+
56
+ s["1/2/2009"] = -2
57
+ assert s.iloc[48] == -2
58
+ s["1/2/2009":"2009-06-05"] = -3
59
+ assert (s[48:54] == -3).all()
60
+
61
+
62
+ @pytest.mark.parametrize("tz_source", ["pytz", "dateutil"])
63
+ def test_getitem_setitem_datetime_tz(tz_source):
64
+ if tz_source == "pytz":
65
+ pytz = pytest.importorskip(tz_source)
66
+ tzget = pytz.timezone
67
+ else:
68
+ # handle special case for utc in dateutil
69
+ tzget = lambda x: tzutc() if x == "UTC" else gettz(x)
70
+
71
+ N = 50
72
+ # testing with timezone, GH #2785
73
+ rng = date_range("1/1/1990", periods=N, freq="h", tz=tzget("US/Eastern"))
74
+ ts = Series(np.random.default_rng(2).standard_normal(N), index=rng)
75
+
76
+ # also test Timestamp tz handling, GH #2789
77
+ result = ts.copy()
78
+ result["1990-01-01 09:00:00+00:00"] = 0
79
+ result["1990-01-01 09:00:00+00:00"] = ts.iloc[4]
80
+ tm.assert_series_equal(result, ts)
81
+
82
+ result = ts.copy()
83
+ result["1990-01-01 03:00:00-06:00"] = 0
84
+ result["1990-01-01 03:00:00-06:00"] = ts.iloc[4]
85
+ tm.assert_series_equal(result, ts)
86
+
87
+ # repeat with datetimes
88
+ result = ts.copy()
89
+ result[datetime(1990, 1, 1, 9, tzinfo=tzget("UTC"))] = 0
90
+ result[datetime(1990, 1, 1, 9, tzinfo=tzget("UTC"))] = ts.iloc[4]
91
+ tm.assert_series_equal(result, ts)
92
+
93
+ result = ts.copy()
94
+ dt = Timestamp(1990, 1, 1, 3).tz_localize(tzget("US/Central"))
95
+ dt = dt.to_pydatetime()
96
+ result[dt] = 0
97
+ result[dt] = ts.iloc[4]
98
+ tm.assert_series_equal(result, ts)
99
+
100
+
101
+ def test_getitem_setitem_datetimeindex():
102
+ N = 50
103
+ # testing with timezone, GH #2785
104
+ rng = date_range("1/1/1990", periods=N, freq="h", tz="US/Eastern", unit="ns")
105
+ ts = Series(np.random.default_rng(2).standard_normal(N), index=rng)
106
+
107
+ result = ts["1990-01-01 04:00:00"]
108
+ expected = ts.iloc[4]
109
+ assert result == expected
110
+
111
+ result = ts.copy()
112
+ result["1990-01-01 04:00:00"] = 0
113
+ result["1990-01-01 04:00:00"] = ts.iloc[4]
114
+ tm.assert_series_equal(result, ts)
115
+
116
+ result = ts["1990-01-01 04:00:00":"1990-01-01 07:00:00"]
117
+ expected = ts[4:8]
118
+ tm.assert_series_equal(result, expected)
119
+
120
+ result = ts.copy()
121
+ result["1990-01-01 04:00:00":"1990-01-01 07:00:00"] = 0
122
+ result["1990-01-01 04:00:00":"1990-01-01 07:00:00"] = ts[4:8]
123
+ tm.assert_series_equal(result, ts)
124
+
125
+ lb = "1990-01-01 04:00:00"
126
+ rb = "1990-01-01 07:00:00"
127
+ # GH#18435 strings get a pass from tzawareness compat
128
+ result = ts[(ts.index >= lb) & (ts.index <= rb)]
129
+ expected = ts[4:8]
130
+ tm.assert_series_equal(result, expected)
131
+
132
+ lb = "1990-01-01 04:00:00-0500"
133
+ rb = "1990-01-01 07:00:00-0500"
134
+ result = ts[(ts.index >= lb) & (ts.index <= rb)]
135
+ expected = ts[4:8]
136
+ tm.assert_series_equal(result, expected)
137
+
138
+ # But we do not give datetimes a pass on tzawareness compat
139
+ msg = "Cannot compare tz-naive and tz-aware datetime-like objects"
140
+ naive = datetime(1990, 1, 1, 4)
141
+ for key in [naive, Timestamp(naive), np.datetime64(naive, "ns")]:
142
+ with pytest.raises(KeyError, match=re.escape(repr(key))):
143
+ # GH#36148 as of 2.0 we require tzawareness-compat
144
+ ts[key]
145
+
146
+ result = ts.copy()
147
+ # GH#36148 as of 2.0 we do not ignore tzawareness mismatch in indexing,
148
+ # so setting it as a new key casts to object rather than matching
149
+ # rng[4]
150
+ result[naive] = ts.iloc[4]
151
+ assert result.index.dtype == object
152
+ tm.assert_index_equal(result.index[:-1], rng.astype(object))
153
+ assert result.index[-1] == naive
154
+
155
+ msg = "Cannot compare tz-naive and tz-aware datetime-like objects"
156
+ with pytest.raises(TypeError, match=msg):
157
+ # GH#36148 require tzawareness compat as of 2.0
158
+ ts[naive : datetime(1990, 1, 1, 7)]
159
+
160
+ result = ts.copy()
161
+ with pytest.raises(TypeError, match=msg):
162
+ # GH#36148 require tzawareness compat as of 2.0
163
+ result[naive : datetime(1990, 1, 1, 7)] = 0
164
+ with pytest.raises(TypeError, match=msg):
165
+ # GH#36148 require tzawareness compat as of 2.0
166
+ result[naive : datetime(1990, 1, 1, 7)] = 99
167
+ # the __setitems__ here failed, so result should still match ts
168
+ tm.assert_series_equal(result, ts)
169
+
170
+ lb = naive
171
+ rb = datetime(1990, 1, 1, 7)
172
+ msg = r"Invalid comparison between dtype=datetime64\[ns, US/Eastern\] and datetime"
173
+ with pytest.raises(TypeError, match=msg):
174
+ # tznaive vs tzaware comparison is invalid
175
+ # see GH#18376, GH#18162
176
+ ts[(ts.index >= lb) & (ts.index <= rb)]
177
+
178
+ lb = Timestamp(naive).tz_localize(rng.tzinfo)
179
+ rb = Timestamp(datetime(1990, 1, 1, 7)).tz_localize(rng.tzinfo)
180
+ result = ts[(ts.index >= lb) & (ts.index <= rb)]
181
+ expected = ts[4:8]
182
+ tm.assert_series_equal(result, expected)
183
+
184
+ result = ts[ts.index[4]]
185
+ expected = ts.iloc[4]
186
+ assert result == expected
187
+
188
+ result = ts[ts.index[4:8]]
189
+ expected = ts[4:8]
190
+ tm.assert_series_equal(result, expected)
191
+
192
+ result = ts.copy()
193
+ result[ts.index[4:8]] = 0
194
+ result.iloc[4:8] = ts.iloc[4:8]
195
+ tm.assert_series_equal(result, ts)
196
+
197
+ # also test partial date slicing
198
+ result = ts["1990-01-02"]
199
+ expected = ts[24:48]
200
+ tm.assert_series_equal(result, expected)
201
+
202
+ result = ts.copy()
203
+ result["1990-01-02"] = 0
204
+ result["1990-01-02"] = ts[24:48]
205
+ tm.assert_series_equal(result, ts)
206
+
207
+
208
+ def test_getitem_setitem_periodindex():
209
+ N = 50
210
+ rng = period_range("1/1/1990", periods=N, freq="h")
211
+ ts = Series(np.random.default_rng(2).standard_normal(N), index=rng)
212
+
213
+ result = ts["1990-01-01 04"]
214
+ expected = ts.iloc[4]
215
+ assert result == expected
216
+
217
+ result = ts.copy()
218
+ result["1990-01-01 04"] = 0
219
+ result["1990-01-01 04"] = ts.iloc[4]
220
+ tm.assert_series_equal(result, ts)
221
+
222
+ result = ts["1990-01-01 04":"1990-01-01 07"]
223
+ expected = ts[4:8]
224
+ tm.assert_series_equal(result, expected)
225
+
226
+ result = ts.copy()
227
+ result["1990-01-01 04":"1990-01-01 07"] = 0
228
+ result["1990-01-01 04":"1990-01-01 07"] = ts[4:8]
229
+ tm.assert_series_equal(result, ts)
230
+
231
+ lb = "1990-01-01 04"
232
+ rb = "1990-01-01 07"
233
+ result = ts[(ts.index >= lb) & (ts.index <= rb)]
234
+ expected = ts[4:8]
235
+ tm.assert_series_equal(result, expected)
236
+
237
+ # GH 2782
238
+ result = ts[ts.index[4]]
239
+ expected = ts.iloc[4]
240
+ assert result == expected
241
+
242
+ result = ts[ts.index[4:8]]
243
+ expected = ts[4:8]
244
+ tm.assert_series_equal(result, expected)
245
+
246
+ result = ts.copy()
247
+ result[ts.index[4:8]] = 0
248
+ result.iloc[4:8] = ts.iloc[4:8]
249
+ tm.assert_series_equal(result, ts)
250
+
251
+
252
+ def test_datetime_indexing():
253
+ index = date_range("1/1/2000", "1/7/2000")
254
+ index = index.repeat(3)
255
+
256
+ s = Series(len(index), index=index)
257
+ stamp = Timestamp("1/8/2000")
258
+
259
+ with pytest.raises(KeyError, match=re.escape(repr(stamp))):
260
+ s[stamp]
261
+ s[stamp] = 0
262
+ assert s[stamp] == 0
263
+
264
+ # not monotonic
265
+ s = Series(len(index), index=index)
266
+ s = s[::-1]
267
+
268
+ with pytest.raises(KeyError, match=re.escape(repr(stamp))):
269
+ s[stamp]
270
+ s[stamp] = 0
271
+ assert s[stamp] == 0
272
+
273
+
274
+ # test duplicates in time series
275
+
276
+
277
+ def test_indexing_with_duplicate_datetimeindex(
278
+ rand_series_with_duplicate_datetimeindex,
279
+ ):
280
+ ts = rand_series_with_duplicate_datetimeindex
281
+
282
+ uniques = ts.index.unique()
283
+ for date in uniques:
284
+ result = ts[date]
285
+
286
+ mask = ts.index == date
287
+ total = (ts.index == date).sum()
288
+ expected = ts[mask]
289
+ if total > 1:
290
+ tm.assert_series_equal(result, expected)
291
+ else:
292
+ tm.assert_almost_equal(result, expected.iloc[0])
293
+
294
+ cp = ts.copy()
295
+ cp[date] = 0
296
+ expected = Series(np.where(mask, 0, ts), index=ts.index)
297
+ tm.assert_series_equal(cp, expected)
298
+
299
+ key = datetime(2000, 1, 6)
300
+ with pytest.raises(KeyError, match=re.escape(repr(key))):
301
+ ts[key]
302
+
303
+ # new index
304
+ ts[datetime(2000, 1, 6)] = 0
305
+ assert ts[datetime(2000, 1, 6)] == 0
306
+
307
+
308
+ def test_loc_getitem_over_size_cutoff(monkeypatch):
309
+ # #1821
310
+
311
+ monkeypatch.setattr(libindex, "_SIZE_CUTOFF", 1000)
312
+
313
+ # create large list of non periodic datetime
314
+ dates = []
315
+ sec = timedelta(seconds=1)
316
+ half_sec = timedelta(microseconds=500000)
317
+ d = datetime(2011, 12, 5, 20, 30)
318
+ n = 1100
319
+ for i in range(n):
320
+ dates.append(d)
321
+ dates.append(d + sec)
322
+ dates.append(d + sec + half_sec)
323
+ dates.append(d + sec + sec + half_sec)
324
+ d += 3 * sec
325
+
326
+ # duplicate some values in the list
327
+ duplicate_positions = np.random.default_rng(2).integers(0, len(dates) - 1, 20)
328
+ for p in duplicate_positions:
329
+ dates[p + 1] = dates[p]
330
+
331
+ df = DataFrame(
332
+ np.random.default_rng(2).standard_normal((len(dates), 4)),
333
+ index=dates,
334
+ columns=list("ABCD"),
335
+ )
336
+
337
+ pos = n * 3
338
+ timestamp = df.index[pos]
339
+ assert timestamp in df.index
340
+
341
+ # it works!
342
+ df.loc[timestamp]
343
+ assert len(df.loc[[timestamp]]) > 0
344
+
345
+
346
+ def test_indexing_over_size_cutoff_period_index(monkeypatch):
347
+ # GH 27136
348
+
349
+ monkeypatch.setattr(libindex, "_SIZE_CUTOFF", 1000)
350
+
351
+ n = 1100
352
+ idx = period_range("1/1/2000", freq="min", periods=n)
353
+ assert idx._engine.over_size_threshold
354
+
355
+ s = Series(np.random.default_rng(2).standard_normal(len(idx)), index=idx)
356
+
357
+ pos = n - 1
358
+ timestamp = idx[pos]
359
+ assert timestamp in s.index
360
+
361
+ # it works!
362
+ s[timestamp]
363
+ assert len(s.loc[[timestamp]]) > 0
364
+
365
+
366
+ def test_indexing_unordered():
367
+ # GH 2437
368
+ rng = date_range(start="2011-01-01", end="2011-01-15")
369
+ ts = Series(np.random.default_rng(2).random(len(rng)), index=rng)
370
+ ts2 = pd.concat([ts[0:4], ts[-4:], ts[4:-4]])
371
+
372
+ for t in ts.index:
373
+ expected = ts[t]
374
+ result = ts2[t]
375
+ assert expected == result
376
+
377
+ # GH 3448 (ranges)
378
+ def compare(slobj):
379
+ result = ts2[slobj].copy()
380
+ result = result.sort_index()
381
+ expected = ts[slobj]
382
+ expected.index = expected.index._with_freq(None)
383
+ tm.assert_series_equal(result, expected)
384
+
385
+ for key in [
386
+ slice("2011-01-01", "2011-01-15"),
387
+ slice("2010-12-30", "2011-01-15"),
388
+ slice("2011-01-01", "2011-01-16"),
389
+ # partial ranges
390
+ slice("2011-01-01", "2011-01-6"),
391
+ slice("2011-01-06", "2011-01-8"),
392
+ slice("2011-01-06", "2011-01-12"),
393
+ ]:
394
+ with pytest.raises(
395
+ KeyError, match="Value based partial slicing on non-monotonic"
396
+ ):
397
+ compare(key)
398
+
399
+ # single values
400
+ result = ts2["2011"].sort_index()
401
+ expected = ts["2011"]
402
+ expected.index = expected.index._with_freq(None)
403
+ tm.assert_series_equal(result, expected)
404
+
405
+
406
+ def test_indexing_unordered2():
407
+ # diff freq
408
+ rng = date_range(datetime(2005, 1, 1), periods=20, freq="ME")
409
+ ts = Series(np.arange(len(rng)), index=rng)
410
+ ts = ts.take(np.random.default_rng(2).permutation(20))
411
+
412
+ result = ts["2005"]
413
+ for t in result.index:
414
+ assert t.year == 2005
415
+
416
+
417
+ def test_indexing():
418
+ idx = date_range("2001-1-1", periods=20, freq="ME")
419
+ ts = Series(np.random.default_rng(2).random(len(idx)), index=idx)
420
+
421
+ # getting
422
+
423
+ # GH 3070, make sure semantics work on Series/Frame
424
+ result = ts["2001"]
425
+ tm.assert_series_equal(result, ts.iloc[:12])
426
+
427
+ df = DataFrame({"A": ts})
428
+
429
+ # GH#36179 pre-2.0 df["2001"] operated as slicing on rows. in 2.0 it behaves
430
+ # like any other key, so raises
431
+ with pytest.raises(KeyError, match="2001"):
432
+ df["2001"]
433
+
434
+ # setting
435
+ ts = Series(np.random.default_rng(2).random(len(idx)), index=idx)
436
+ expected = ts.copy()
437
+ expected.iloc[:12] = 1
438
+ ts["2001"] = 1
439
+ tm.assert_series_equal(ts, expected)
440
+
441
+ expected = df.copy()
442
+ expected.iloc[:12, 0] = 1
443
+ df.loc["2001", "A"] = 1
444
+ tm.assert_frame_equal(df, expected)
445
+
446
+
447
+ def test_getitem_str_month_with_datetimeindex():
448
+ # GH3546 (not including times on the last day)
449
+ idx = date_range(start="2013-05-31 00:00", end="2013-05-31 23:00", freq="h")
450
+ ts = Series(range(len(idx)), index=idx)
451
+ expected = ts["2013-05"]
452
+ tm.assert_series_equal(expected, ts)
453
+
454
+ idx = date_range(start="2013-05-31 00:00", end="2013-05-31 23:59", freq="s")
455
+ ts = Series(range(len(idx)), index=idx)
456
+ expected = ts["2013-05"]
457
+ tm.assert_series_equal(expected, ts)
458
+
459
+
460
+ def test_getitem_str_year_with_datetimeindex():
461
+ idx = [
462
+ Timestamp("2013-05-31 00:00"),
463
+ Timestamp(datetime(2013, 5, 31, 23, 59, 59, 999999)),
464
+ ]
465
+ ts = Series(range(len(idx)), index=idx)
466
+ expected = ts["2013"]
467
+ tm.assert_series_equal(expected, ts)
468
+
469
+
470
+ def test_getitem_str_second_with_datetimeindex():
471
+ # GH14826, indexing with a seconds resolution string / datetime object
472
+ df = DataFrame(
473
+ np.random.default_rng(2).random((5, 5)),
474
+ columns=["open", "high", "low", "close", "volume"],
475
+ index=date_range("2012-01-02 18:01:00", periods=5, tz="US/Central", freq="s"),
476
+ )
477
+
478
+ # this is a single date, so will raise
479
+ with pytest.raises(KeyError, match=r"^'2012-01-02 18:01:02'$"):
480
+ df["2012-01-02 18:01:02"]
481
+
482
+ msg = r"Timestamp\('2012-01-02 18:01:02-0600', tz='US/Central'\)"
483
+ with pytest.raises(KeyError, match=msg):
484
+ df[df.index[2]]
485
+
486
+
487
+ def test_compare_datetime_with_all_none():
488
+ # GH#54870
489
+ ser = Series(["2020-01-01", "2020-01-02"], dtype="datetime64[ns]")
490
+ ser2 = Series([None, None])
491
+ result = ser > ser2
492
+ expected = Series([False, False])
493
+ tm.assert_series_equal(result, expected)
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_delitem.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ from pandas import (
4
+ Index,
5
+ Series,
6
+ date_range,
7
+ )
8
+ import pandas._testing as tm
9
+
10
+
11
+ class TestSeriesDelItem:
12
+ def test_delitem(self):
13
+ # GH#5542
14
+ # should delete the item inplace
15
+ s = Series(range(5))
16
+ del s[0]
17
+
18
+ expected = Series(range(1, 5), index=range(1, 5))
19
+ tm.assert_series_equal(s, expected)
20
+
21
+ del s[1]
22
+ expected = Series(range(2, 5), index=range(2, 5))
23
+ tm.assert_series_equal(s, expected)
24
+
25
+ # only 1 left, del, add, del
26
+ s = Series(1)
27
+ del s[0]
28
+ tm.assert_series_equal(s, Series(dtype="int64", index=Index([], dtype="int64")))
29
+ s[0] = 1
30
+ tm.assert_series_equal(s, Series(1))
31
+ del s[0]
32
+ tm.assert_series_equal(s, Series(dtype="int64", index=Index([], dtype="int64")))
33
+
34
+ def test_delitem_object_index(self):
35
+ # Index(dtype=object)
36
+ s = Series(1, index=Index(["a"], dtype="str"))
37
+ del s["a"]
38
+ tm.assert_series_equal(s, Series(dtype="int64", index=Index([], dtype="str")))
39
+ s["a"] = 1
40
+ tm.assert_series_equal(s, Series(1, index=Index(["a"], dtype="str")))
41
+ del s["a"]
42
+ tm.assert_series_equal(s, Series(dtype="int64", index=Index([], dtype="str")))
43
+
44
+ def test_delitem_missing_key(self):
45
+ # empty
46
+ s = Series(dtype=object)
47
+
48
+ with pytest.raises(KeyError, match=r"^0$"):
49
+ del s[0]
50
+
51
+ def test_delitem_extension_dtype(self):
52
+ # GH#40386
53
+ # DatetimeTZDtype
54
+ dti = date_range("2016-01-01", periods=3, tz="US/Pacific")
55
+ ser = Series(dti)
56
+
57
+ expected = ser[[0, 2]]
58
+ del ser[1]
59
+ assert ser.dtype == dti.dtype
60
+ tm.assert_series_equal(ser, expected)
61
+
62
+ # PeriodDtype
63
+ pi = dti.tz_localize(None).to_period("D")
64
+ ser = Series(pi)
65
+
66
+ expected = ser[:2]
67
+ del ser[2]
68
+ assert ser.dtype == pi.dtype
69
+ tm.assert_series_equal(ser, expected)
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_get.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ import pandas as pd
5
+ from pandas import (
6
+ DatetimeIndex,
7
+ Index,
8
+ Series,
9
+ date_range,
10
+ )
11
+ import pandas._testing as tm
12
+
13
+
14
+ def test_get():
15
+ # GH 6383
16
+ s = Series(
17
+ np.array(
18
+ [
19
+ 43,
20
+ 48,
21
+ 60,
22
+ 48,
23
+ 50,
24
+ 51,
25
+ 50,
26
+ 45,
27
+ 57,
28
+ 48,
29
+ 56,
30
+ 45,
31
+ 51,
32
+ 39,
33
+ 55,
34
+ 43,
35
+ 54,
36
+ 52,
37
+ 51,
38
+ 54,
39
+ ]
40
+ )
41
+ )
42
+
43
+ result = s.get(25, 0)
44
+ expected = 0
45
+ assert result == expected
46
+
47
+ s = Series(
48
+ np.array(
49
+ [
50
+ 43,
51
+ 48,
52
+ 60,
53
+ 48,
54
+ 50,
55
+ 51,
56
+ 50,
57
+ 45,
58
+ 57,
59
+ 48,
60
+ 56,
61
+ 45,
62
+ 51,
63
+ 39,
64
+ 55,
65
+ 43,
66
+ 54,
67
+ 52,
68
+ 51,
69
+ 54,
70
+ ]
71
+ ),
72
+ index=Index(
73
+ [
74
+ 25.0,
75
+ 36.0,
76
+ 49.0,
77
+ 64.0,
78
+ 81.0,
79
+ 100.0,
80
+ 121.0,
81
+ 144.0,
82
+ 169.0,
83
+ 196.0,
84
+ 1225.0,
85
+ 1296.0,
86
+ 1369.0,
87
+ 1444.0,
88
+ 1521.0,
89
+ 1600.0,
90
+ 1681.0,
91
+ 1764.0,
92
+ 1849.0,
93
+ 1936.0,
94
+ ],
95
+ dtype=np.float64,
96
+ ),
97
+ )
98
+
99
+ result = s.get(25, 0)
100
+ expected = 43
101
+ assert result == expected
102
+
103
+ # GH 7407
104
+ # with a boolean accessor
105
+ df = pd.DataFrame({"i": [0] * 3, "b": [False] * 3})
106
+ vc = df.i.value_counts()
107
+ result = vc.get(99, default="Missing")
108
+ assert result == "Missing"
109
+
110
+ vc = df.b.value_counts()
111
+ result = vc.get(False, default="Missing")
112
+ assert result == 3
113
+
114
+ result = vc.get(True, default="Missing")
115
+ assert result == "Missing"
116
+
117
+
118
+ def test_get_nan(float_numpy_dtype):
119
+ # GH 8569
120
+ s = Index(range(10), dtype=float_numpy_dtype).to_series()
121
+ assert s.get(np.nan) is None
122
+ assert s.get(np.nan, default="Missing") == "Missing"
123
+
124
+
125
+ def test_get_nan_multiple(float_numpy_dtype):
126
+ # GH 8569
127
+ # ensure that fixing "test_get_nan" above hasn't broken get
128
+ # with multiple elements
129
+ s = Index(range(10), dtype=float_numpy_dtype).to_series()
130
+
131
+ idx = [2, 30]
132
+ assert s.get(idx) is None
133
+
134
+ idx = [2, np.nan]
135
+ assert s.get(idx) is None
136
+
137
+ # GH 17295 - all missing keys
138
+ idx = [20, 30]
139
+ assert s.get(idx) is None
140
+
141
+ idx = [np.nan, np.nan]
142
+ assert s.get(idx) is None
143
+
144
+
145
+ def test_get_with_default():
146
+ # GH#7725
147
+ d0 = ["a", "b", "c", "d"]
148
+ d1 = np.arange(4, dtype="int64")
149
+
150
+ for data, index in ((d0, d1), (d1, d0)):
151
+ s = Series(data, index=index)
152
+ for i, d in zip(index, data):
153
+ assert s.get(i) == d
154
+ assert s.get(i, d) == d
155
+ assert s.get(i, "z") == d
156
+
157
+ assert s.get("e", "z") == "z"
158
+ assert s.get("e", "e") == "e"
159
+
160
+ assert s.get(10, "z") == "z"
161
+ assert s.get(10, 10) == 10
162
+
163
+
164
+ @pytest.mark.parametrize(
165
+ "arr",
166
+ [
167
+ np.random.default_rng(2).standard_normal(10),
168
+ DatetimeIndex(date_range("2020-01-01", periods=10), name="a").tz_localize(
169
+ tz="US/Eastern"
170
+ ),
171
+ ],
172
+ )
173
+ def test_get_with_ea(arr):
174
+ # GH#21260
175
+ ser = Series(arr, index=[2 * i for i in range(len(arr))])
176
+ assert ser.get(4) == ser.iloc[2]
177
+
178
+ result = ser.get([4, 6])
179
+ expected = ser.iloc[[2, 3]]
180
+ tm.assert_series_equal(result, expected)
181
+
182
+ result = ser.get(slice(2))
183
+ expected = ser.iloc[[0, 1]]
184
+ tm.assert_series_equal(result, expected)
185
+
186
+ assert ser.get(-1) is None
187
+ assert ser.get(ser.index.max() + 1) is None
188
+
189
+ ser = Series(arr[:6], index=list("abcdef"))
190
+ assert ser.get("c") == ser.iloc[2]
191
+
192
+ result = ser.get(slice("b", "d"))
193
+ expected = ser.iloc[[1, 2, 3]]
194
+ tm.assert_series_equal(result, expected)
195
+
196
+ result = ser.get("Z")
197
+ assert result is None
198
+
199
+ # As of 3.0, ints are treated as labels
200
+ assert ser.get(4) is None
201
+ assert ser.get(-1) is None
202
+ assert ser.get(len(ser)) is None
203
+
204
+ # GH#21257
205
+ ser = Series(arr)
206
+ ser2 = ser[::2]
207
+ assert ser2.get(1) is None
208
+
209
+
210
+ def test_getitem_get(string_series, object_series):
211
+ for obj in [string_series, object_series]:
212
+ idx = obj.index[5]
213
+
214
+ assert obj[idx] == obj.get(idx)
215
+ assert obj[idx] == obj.iloc[5]
216
+
217
+ # As of 3.0, ints are treated as labels
218
+ assert string_series.get(-1) is None
219
+ assert string_series.iloc[5] == string_series.get(string_series.index[5])
220
+
221
+
222
+ def test_get_none():
223
+ # GH#5652
224
+ s1 = Series(dtype=object)
225
+ s2 = Series(dtype=object, index=list("abc"))
226
+ for s in [s1, s2]:
227
+ result = s.get(None)
228
+ assert result is None
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_getitem.py ADDED
@@ -0,0 +1,721 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Series.__getitem__ test classes are organized by the type of key passed.
3
+ """
4
+
5
+ from datetime import (
6
+ date,
7
+ datetime,
8
+ time,
9
+ )
10
+
11
+ import numpy as np
12
+ import pytest
13
+
14
+ from pandas._libs.tslibs import (
15
+ conversion,
16
+ timezones,
17
+ )
18
+ from pandas.compat.numpy import np_version_gt2
19
+ from pandas.errors import Pandas4Warning
20
+
21
+ from pandas.core.dtypes.common import is_scalar
22
+
23
+ import pandas as pd
24
+ from pandas import (
25
+ Categorical,
26
+ DataFrame,
27
+ DatetimeIndex,
28
+ Index,
29
+ Series,
30
+ Timestamp,
31
+ date_range,
32
+ period_range,
33
+ timedelta_range,
34
+ )
35
+ import pandas._testing as tm
36
+ from pandas.core.indexing import IndexingError
37
+
38
+ from pandas.tseries.offsets import BDay
39
+
40
+
41
+ class TestSeriesGetitemScalars:
42
+ def test_getitem_object_index_float_string(self):
43
+ # GH#17286
44
+ ser = Series([1] * 4, index=Index(["a", "b", "c", 1.0]))
45
+ assert ser["a"] == 1
46
+ assert ser[1.0] == 1
47
+
48
+ def test_getitem_float_keys_tuple_values(self):
49
+ # see GH#13509
50
+
51
+ # unique Index
52
+ ser = Series([(1, 1), (2, 2), (3, 3)], index=[0.0, 0.1, 0.2], name="foo")
53
+ result = ser[0.0]
54
+ assert result == (1, 1)
55
+
56
+ # non-unique Index
57
+ expected = Series([(1, 1), (2, 2)], index=[0.0, 0.0], name="foo")
58
+ ser = Series([(1, 1), (2, 2), (3, 3)], index=[0.0, 0.0, 0.2], name="foo")
59
+
60
+ result = ser[0.0]
61
+ tm.assert_series_equal(result, expected)
62
+
63
+ def test_getitem_unrecognized_scalar(self):
64
+ # GH#32684 a scalar key that is not recognized by lib.is_scalar
65
+
66
+ # a series that might be produced via `frame.dtypes`
67
+ ser = Series([1, 2], index=[np.dtype("O"), np.dtype("i8")])
68
+
69
+ key = ser.index[1]
70
+
71
+ result = ser[key]
72
+ assert result == 2
73
+
74
+ def test_getitem_negative_out_of_bounds(self):
75
+ ser = Series(["a"] * 10, index=["a"] * 10)
76
+
77
+ with pytest.raises(KeyError, match="^-11$"):
78
+ ser[-11]
79
+
80
+ def test_getitem_out_of_bounds_indexerror(self, datetime_series):
81
+ # don't segfault, GH#495
82
+ N = len(datetime_series)
83
+ with pytest.raises(KeyError, match=str(N)):
84
+ datetime_series[N]
85
+
86
+ def test_getitem_out_of_bounds_empty_rangeindex_keyerror(self):
87
+ # GH#917
88
+ # With a RangeIndex, an int key gives a KeyError
89
+ ser = Series([], dtype=object)
90
+ with pytest.raises(KeyError, match="-1"):
91
+ ser[-1]
92
+
93
+ def test_getitem_keyerror_with_integer_index(self, any_int_numpy_dtype):
94
+ dtype = any_int_numpy_dtype
95
+ ser = Series(
96
+ np.random.default_rng(2).standard_normal(6),
97
+ index=Index([0, 0, 1, 1, 2, 2], dtype=dtype),
98
+ )
99
+
100
+ with pytest.raises(KeyError, match=r"^5$"):
101
+ ser[5]
102
+
103
+ with pytest.raises(KeyError, match=r"^'c'$"):
104
+ ser["c"]
105
+
106
+ # not monotonic
107
+ ser = Series(
108
+ np.random.default_rng(2).standard_normal(6), index=[2, 2, 0, 0, 1, 1]
109
+ )
110
+
111
+ with pytest.raises(KeyError, match=r"^5$"):
112
+ ser[5]
113
+
114
+ with pytest.raises(KeyError, match=r"^'c'$"):
115
+ ser["c"]
116
+
117
+ def test_getitem_int64(self, datetime_series):
118
+ if np_version_gt2:
119
+ msg = r"^np.int64\(5\)$"
120
+ else:
121
+ msg = "^5$"
122
+ idx = np.int64(5)
123
+ with pytest.raises(KeyError, match=msg):
124
+ datetime_series[idx]
125
+
126
+ def test_getitem_full_range(self):
127
+ # github.com/pandas-dev/pandas/commit/4f433773141d2eb384325714a2776bcc5b2e20f7
128
+ ser = Series(range(5), index=list(range(5)))
129
+ result = ser[list(range(5))]
130
+ tm.assert_series_equal(result, ser)
131
+
132
+ # ------------------------------------------------------------------
133
+ # Series with DatetimeIndex
134
+
135
+ @pytest.mark.parametrize("tzstr", ["Europe/Berlin", "dateutil/Europe/Berlin"])
136
+ def test_getitem_pydatetime_tz(self, tzstr):
137
+ tz = timezones.maybe_get_tz(tzstr)
138
+
139
+ index = date_range(
140
+ start="2012-12-24 16:00", end="2012-12-24 18:00", freq="h", tz=tzstr
141
+ )
142
+ ts = Series(index=index, data=index.hour)
143
+ time_pandas = Timestamp("2012-12-24 17:00", tz=tzstr)
144
+
145
+ dt = datetime(2012, 12, 24, 17, 0)
146
+ time_datetime = conversion.localize_pydatetime(dt, tz)
147
+ assert ts[time_pandas] == ts[time_datetime]
148
+
149
+ @pytest.mark.parametrize("tz", ["US/Eastern", "dateutil/US/Eastern"])
150
+ def test_string_index_alias_tz_aware(self, tz):
151
+ rng = date_range("1/1/2000", periods=10, tz=tz)
152
+ ser = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
153
+
154
+ result = ser["1/3/2000"]
155
+ tm.assert_almost_equal(result, ser.iloc[2])
156
+
157
+ def test_getitem_time_object(self):
158
+ rng = date_range("1/1/2000", "1/5/2000", freq="5min")
159
+ ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
160
+
161
+ mask = (rng.hour == 9) & (rng.minute == 30)
162
+ result = ts[time(9, 30)]
163
+ expected = ts[mask]
164
+ result.index = result.index._with_freq(None)
165
+ tm.assert_series_equal(result, expected)
166
+
167
+ # ------------------------------------------------------------------
168
+ # Series with CategoricalIndex
169
+
170
+ def test_getitem_scalar_categorical_index(self):
171
+ cats = Categorical([Timestamp("12-31-1999"), Timestamp("12-31-2000")])
172
+
173
+ ser = Series([1, 2], index=cats)
174
+
175
+ expected = ser.iloc[0]
176
+ result = ser[cats[0]]
177
+ assert result == expected
178
+
179
+ def test_getitem_numeric_categorical_listlike_matches_scalar(self):
180
+ # GH#15470
181
+ ser = Series(["a", "b", "c"], index=pd.CategoricalIndex([2, 1, 0]))
182
+
183
+ # 0 is treated as a label
184
+ assert ser[0] == "c"
185
+
186
+ # the listlike analogue should also be treated as labels
187
+ res = ser[[0]]
188
+ expected = ser.iloc[-1:]
189
+ tm.assert_series_equal(res, expected)
190
+
191
+ res2 = ser[[0, 1, 2]]
192
+ tm.assert_series_equal(res2, ser.iloc[::-1])
193
+
194
+ def test_getitem_integer_categorical_not_positional(self):
195
+ # GH#14865
196
+ ser = Series(["a", "b", "c"], index=Index([1, 2, 3], dtype="category"))
197
+ assert ser.get(3) == "c"
198
+ assert ser[3] == "c"
199
+
200
+ def test_getitem_str_with_timedeltaindex(self):
201
+ rng = timedelta_range("1 day 10:11:12", freq="h", periods=500)
202
+ ser = Series(np.arange(len(rng)), index=rng)
203
+
204
+ key = "6 days, 23:11:12"
205
+ indexer = rng.get_loc(key)
206
+ assert indexer == 133
207
+
208
+ result = ser[key]
209
+ assert result == ser.iloc[133]
210
+
211
+ msg = r"^Timedelta\('50 days 00:00:00'\)$"
212
+ with pytest.raises(KeyError, match=msg):
213
+ rng.get_loc("50 days")
214
+ with pytest.raises(KeyError, match=msg):
215
+ ser["50 days"]
216
+
217
+ def test_getitem_bool_index_positional(self):
218
+ # GH#48653
219
+ ser = Series({True: 1, False: 0})
220
+ with pytest.raises(KeyError, match="^0$"):
221
+ ser[0]
222
+
223
+
224
+ class TestSeriesGetitemSlices:
225
+ def test_getitem_partial_str_slice_with_datetimeindex(self):
226
+ # GH#34860
227
+ arr = date_range("1/1/2008", "1/1/2009")
228
+ ser = arr.to_series()
229
+ result = ser["2008"]
230
+
231
+ rng = date_range(start="2008-01-01", end="2008-12-31")
232
+ expected = Series(rng, index=rng)
233
+
234
+ tm.assert_series_equal(result, expected)
235
+
236
+ def test_getitem_slice_strings_with_datetimeindex(self):
237
+ idx = DatetimeIndex(
238
+ ["1/1/2000", "1/2/2000", "1/2/2000", "1/3/2000", "1/4/2000"]
239
+ )
240
+
241
+ ts = Series(np.random.default_rng(2).standard_normal(len(idx)), index=idx)
242
+
243
+ result = ts["1/2/2000":]
244
+ expected = ts[1:]
245
+ tm.assert_series_equal(result, expected)
246
+
247
+ result = ts["1/2/2000":"1/3/2000"]
248
+ expected = ts[1:4]
249
+ tm.assert_series_equal(result, expected)
250
+
251
+ def test_getitem_partial_str_slice_with_timedeltaindex(self):
252
+ rng = timedelta_range("1 day 10:11:12", freq="h", periods=500)
253
+ ser = Series(np.arange(len(rng)), index=rng)
254
+
255
+ result = ser["5 day":"6 day"]
256
+ expected = ser.iloc[86:134]
257
+ tm.assert_series_equal(result, expected)
258
+
259
+ result = ser["5 day":]
260
+ expected = ser.iloc[86:]
261
+ tm.assert_series_equal(result, expected)
262
+
263
+ result = ser[:"6 day"]
264
+ expected = ser.iloc[:134]
265
+ tm.assert_series_equal(result, expected)
266
+
267
+ def test_getitem_partial_str_slice_high_reso_with_timedeltaindex(self):
268
+ # higher reso
269
+ rng = timedelta_range("1 day 10:11:12", freq="us", periods=2000)
270
+ ser = Series(np.arange(len(rng)), index=rng)
271
+
272
+ result = ser["1 day 10:11:12":]
273
+ expected = ser.iloc[0:]
274
+ tm.assert_series_equal(result, expected)
275
+
276
+ result = ser["1 day 10:11:12.001":]
277
+ expected = ser.iloc[1000:]
278
+ tm.assert_series_equal(result, expected)
279
+
280
+ result = ser["1 days, 10:11:12.001001"]
281
+ assert result == ser.iloc[1001]
282
+
283
+ def test_getitem_slice_2d(self, datetime_series):
284
+ # GH#30588 multi-dimensional indexing deprecated
285
+ with pytest.raises(ValueError, match="Multi-dimensional indexing"):
286
+ datetime_series[:, np.newaxis]
287
+
288
+ def test_getitem_median_slice_bug(self):
289
+ index = date_range("20090415", "20090519", freq="2B")
290
+ ser = Series(np.random.default_rng(2).standard_normal(13), index=index)
291
+
292
+ indexer = [slice(6, 7, None)]
293
+ msg = "Indexing with a single-item list"
294
+ with pytest.raises(ValueError, match=msg):
295
+ # GH#31299
296
+ ser[indexer]
297
+ # but we're OK with a single-element tuple
298
+ result = ser[(indexer[0],)]
299
+ expected = ser[indexer[0]]
300
+ tm.assert_series_equal(result, expected)
301
+
302
+ @pytest.mark.parametrize(
303
+ "slc, positions",
304
+ [
305
+ [slice(date(2018, 1, 1), None), [0, 1, 2]],
306
+ [slice(date(2019, 1, 2), None), [2]],
307
+ [slice(date(2020, 1, 1), None), []],
308
+ [slice(None, date(2020, 1, 1)), [0, 1, 2]],
309
+ [slice(None, date(2019, 1, 1)), [0]],
310
+ ],
311
+ )
312
+ def test_getitem_slice_date(self, slc, positions):
313
+ # https://github.com/pandas-dev/pandas/issues/31501
314
+ ser = Series(
315
+ [0, 1, 2],
316
+ DatetimeIndex(["2019-01-01", "2019-01-01T06:00:00", "2019-01-02"]),
317
+ )
318
+ msg = "Slicing with a datetime.date object is deprecated"
319
+ with tm.assert_produces_warning(Pandas4Warning, match=msg):
320
+ result = ser[slc]
321
+ expected = ser.take(positions)
322
+ tm.assert_series_equal(result, expected)
323
+
324
+ def test_getitem_slice_float_raises(self, datetime_series):
325
+ msg = (
326
+ "cannot do slice indexing on DatetimeIndex with these indexers "
327
+ r"\[{key}\] of type float"
328
+ )
329
+ with pytest.raises(TypeError, match=msg.format(key=r"4\.0")):
330
+ datetime_series[4.0:10.0]
331
+
332
+ with pytest.raises(TypeError, match=msg.format(key=r"4\.5")):
333
+ datetime_series[4.5:10.0]
334
+
335
+ def test_getitem_slice_bug(self):
336
+ ser = Series(range(10), index=list(range(10)))
337
+ result = ser[-12:]
338
+ tm.assert_series_equal(result, ser)
339
+
340
+ result = ser[-7:]
341
+ tm.assert_series_equal(result, ser[3:])
342
+
343
+ result = ser[:-12]
344
+ tm.assert_series_equal(result, ser[:0])
345
+
346
+ def test_getitem_slice_integers(self):
347
+ ser = Series(
348
+ np.random.default_rng(2).standard_normal(8),
349
+ index=[2, 4, 6, 8, 10, 12, 14, 16],
350
+ )
351
+
352
+ result = ser[:4]
353
+ expected = Series(ser.values[:4], index=[2, 4, 6, 8])
354
+ tm.assert_series_equal(result, expected)
355
+
356
+
357
+ class TestSeriesGetitemListLike:
358
+ @pytest.mark.parametrize("box", [list, np.array, Index, Series])
359
+ def test_getitem_no_matches(self, box):
360
+ # GH#33462 we expect the same behavior for list/ndarray/Index/Series
361
+ ser = Series(["A", "B"])
362
+
363
+ key = Series(["C"], dtype=object)
364
+ key = box(key)
365
+
366
+ msg = r"None of \[Index\(\['C'\], dtype='object|str'\)\] are in the \[index\]"
367
+ with pytest.raises(KeyError, match=msg):
368
+ ser[key]
369
+
370
+ def test_getitem_intlist_intindex_periodvalues(self):
371
+ ser = Series(period_range("2000-01-01", periods=10, freq="D"))
372
+
373
+ result = ser[[2, 4]]
374
+ exp = Series(
375
+ [pd.Period("2000-01-03", freq="D"), pd.Period("2000-01-05", freq="D")],
376
+ index=[2, 4],
377
+ dtype="Period[D]",
378
+ )
379
+ tm.assert_series_equal(result, exp)
380
+ assert result.dtype == "Period[D]"
381
+
382
+ @pytest.mark.parametrize("box", [list, np.array, Index])
383
+ def test_getitem_intlist_intervalindex_non_int(self, box):
384
+ # GH#33404 fall back to positional since ints are unambiguous;
385
+ # changed in 3.0 to never fallback
386
+ dti = date_range("2000-01-03", periods=3)._with_freq(None)
387
+ ii = pd.IntervalIndex.from_breaks(dti)
388
+ ser = Series(range(len(ii)), index=ii)
389
+
390
+ key = box([0])
391
+ msg = r"None of \[Index\(\[0\], dtype='int(32|64)'\)\] are in the \[index\]"
392
+ with pytest.raises(KeyError, match=msg):
393
+ ser[key]
394
+
395
+ @pytest.mark.parametrize("box", [list, np.array, Index])
396
+ @pytest.mark.parametrize("dtype", [np.int64, np.float64, np.uint64])
397
+ def test_getitem_intlist_multiindex_numeric_level(self, dtype, box):
398
+ # GH#33404 do _not_ fall back to positional since ints are ambiguous
399
+ idx = Index(range(4)).astype(dtype)
400
+ dti = date_range("2000-01-03", periods=3)
401
+ mi = pd.MultiIndex.from_product([idx, dti])
402
+ ser = Series(range(len(mi))[::-1], index=mi)
403
+
404
+ key = box([5])
405
+ with pytest.raises(KeyError, match="5"):
406
+ ser[key]
407
+
408
+ def test_getitem_uint_array_key(self, any_unsigned_int_numpy_dtype):
409
+ # GH #37218
410
+ ser = Series([1, 2, 3])
411
+ key = np.array([4], dtype=any_unsigned_int_numpy_dtype)
412
+
413
+ with pytest.raises(KeyError, match="4"):
414
+ ser[key]
415
+ with pytest.raises(KeyError, match="4"):
416
+ ser.loc[key]
417
+
418
+
419
+ class TestGetitemBooleanMask:
420
+ def test_getitem_boolean(self, string_series):
421
+ ser = string_series
422
+ mask = ser > ser.median()
423
+
424
+ # passing list is OK
425
+ result = ser[list(mask)]
426
+ expected = ser[mask]
427
+ tm.assert_series_equal(result, expected)
428
+ tm.assert_index_equal(result.index, ser.index[mask])
429
+
430
+ def test_getitem_boolean_empty(self):
431
+ ser = Series([], dtype=np.int64)
432
+ ser.index.name = "index_name"
433
+ ser = ser[ser.isna()]
434
+ assert ser.index.name == "index_name"
435
+ assert ser.dtype == np.int64
436
+
437
+ # GH#5877
438
+ # indexing with empty series
439
+ ser = Series(["A", "B"], dtype=object)
440
+ expected = Series(dtype=object, index=Index([], dtype="int64"))
441
+ result = ser[Series([], dtype=object)]
442
+ tm.assert_series_equal(result, expected)
443
+
444
+ # invalid because of the boolean indexer
445
+ # that's empty or not-aligned
446
+ msg = (
447
+ r"Unalignable boolean Series provided as indexer \(index of "
448
+ r"the boolean Series and of the indexed object do not match"
449
+ )
450
+ with pytest.raises(IndexingError, match=msg):
451
+ ser[Series([], dtype=bool)]
452
+
453
+ with pytest.raises(IndexingError, match=msg):
454
+ ser[Series([True], dtype=bool)]
455
+
456
+ def test_getitem_boolean_object(self, string_series):
457
+ # using column from DataFrame
458
+
459
+ ser = string_series
460
+ mask = ser > ser.median()
461
+ omask = mask.astype(object)
462
+
463
+ # getitem
464
+ result = ser[omask]
465
+ expected = ser[mask]
466
+ tm.assert_series_equal(result, expected)
467
+
468
+ # setitem
469
+ s2 = ser.copy()
470
+ cop = ser.copy()
471
+ cop[omask] = 5
472
+ s2[mask] = 5
473
+ tm.assert_series_equal(cop, s2)
474
+
475
+ # nans raise exception
476
+ omask[5:10] = np.nan
477
+ msg = "Cannot mask with non-boolean array containing NA / NaN values"
478
+ with pytest.raises(ValueError, match=msg):
479
+ ser[omask]
480
+ with pytest.raises(ValueError, match=msg):
481
+ ser[omask] = 5
482
+
483
+ def test_getitem_boolean_dt64_copies(self):
484
+ # GH#36210
485
+ dti = date_range("2016-01-01", periods=4, tz="US/Pacific")
486
+ key = np.array([True, True, False, False])
487
+
488
+ ser = Series(dti._data)
489
+
490
+ res = ser[key]
491
+ assert res._values._ndarray.base is None
492
+
493
+ # compare with numeric case for reference
494
+ ser2 = Series(range(4))
495
+ res2 = ser2[key]
496
+ assert res2._values.base is None
497
+
498
+ def test_getitem_boolean_corner(self, datetime_series):
499
+ ts = datetime_series
500
+ mask_shifted = ts.shift(1, freq=BDay()) > ts.median()
501
+
502
+ msg = (
503
+ r"Unalignable boolean Series provided as indexer \(index of "
504
+ r"the boolean Series and of the indexed object do not match"
505
+ )
506
+ with pytest.raises(IndexingError, match=msg):
507
+ ts[mask_shifted]
508
+
509
+ with pytest.raises(IndexingError, match=msg):
510
+ ts.loc[mask_shifted]
511
+
512
+ def test_getitem_boolean_different_order(self, string_series):
513
+ ordered = string_series.sort_values()
514
+
515
+ sel = string_series[ordered > 0]
516
+ exp = string_series[string_series > 0]
517
+ tm.assert_series_equal(sel, exp)
518
+
519
+ def test_getitem_boolean_contiguous_preserve_freq(self):
520
+ rng = date_range("1/1/2000", "3/1/2000", freq="B")
521
+
522
+ mask = np.zeros(len(rng), dtype=bool)
523
+ mask[10:20] = True
524
+
525
+ masked = rng[mask]
526
+ expected = rng[10:20]
527
+ assert expected.freq == rng.freq
528
+ tm.assert_index_equal(masked, expected)
529
+
530
+ mask[22] = True
531
+ masked = rng[mask]
532
+ assert masked.freq is None
533
+
534
+
535
+ class TestGetitemCallable:
536
+ def test_getitem_callable(self):
537
+ # GH#12533
538
+ ser = Series(4, index=list("ABCD"))
539
+ result = ser[lambda x: "A"]
540
+ assert result == ser.loc["A"]
541
+
542
+ result = ser[lambda x: ["A", "B"]]
543
+ expected = ser.loc[["A", "B"]]
544
+ tm.assert_series_equal(result, expected)
545
+
546
+ result = ser[lambda x: [True, False, True, True]]
547
+ expected = ser.iloc[[0, 2, 3]]
548
+ tm.assert_series_equal(result, expected)
549
+
550
+
551
+ def test_getitem_generator(string_series):
552
+ gen = (x > 0 for x in string_series)
553
+ result = string_series[gen]
554
+ result2 = string_series[iter(string_series > 0)]
555
+ expected = string_series[string_series > 0]
556
+ tm.assert_series_equal(result, expected)
557
+ tm.assert_series_equal(result2, expected)
558
+
559
+
560
+ @pytest.mark.parametrize(
561
+ "data",
562
+ [
563
+ [0, 1],
564
+ date_range("2012-01-01", periods=2),
565
+ date_range("2012-01-01", periods=2, tz="CET"),
566
+ ],
567
+ )
568
+ def test_getitem_ndim_deprecated(data):
569
+ series = Series(data)
570
+ with pytest.raises(ValueError, match="Multi-dimensional indexing"):
571
+ series[:, None]
572
+
573
+
574
+ def test_getitem_multilevel_scalar_slice_not_implemented(
575
+ multiindex_year_month_day_dataframe_random_data,
576
+ ):
577
+ # not implementing this for now
578
+ df = multiindex_year_month_day_dataframe_random_data
579
+ ser = df["A"]
580
+
581
+ msg = r"\(2000, slice\(3, 4, None\)\)"
582
+ with pytest.raises(TypeError, match=msg):
583
+ ser[2000, 3:4]
584
+
585
+
586
+ def test_getitem_dataframe_raises():
587
+ rng = list(range(10))
588
+ ser = Series(10, index=rng)
589
+ df = DataFrame(rng, index=rng)
590
+ msg = (
591
+ "Indexing a Series with DataFrame is not supported, "
592
+ "use the appropriate DataFrame column"
593
+ )
594
+ with pytest.raises(TypeError, match=msg):
595
+ ser[df > 5]
596
+
597
+
598
+ def test_getitem_assignment_series_alignment():
599
+ # https://github.com/pandas-dev/pandas/issues/37427
600
+ # with getitem, when assigning with a Series, it is not first aligned
601
+ ser = Series(range(10))
602
+ idx = np.array([2, 4, 9])
603
+ ser[idx] = Series([10, 11, 12])
604
+ expected = Series([0, 1, 10, 3, 11, 5, 6, 7, 8, 12])
605
+ tm.assert_series_equal(ser, expected)
606
+
607
+
608
+ def test_getitem_duplicate_index_mistyped_key_raises_keyerror():
609
+ # GH#29189 float_index.get_loc(None) should raise KeyError, not TypeError
610
+ ser = Series([2, 5, 6, 8], index=[2.0, 4.0, 4.0, 5.0])
611
+ with pytest.raises(KeyError, match="None"):
612
+ ser[None]
613
+
614
+ with pytest.raises(KeyError, match="None"):
615
+ ser.index.get_loc(None)
616
+
617
+ with pytest.raises(KeyError, match="None"):
618
+ ser.index._engine.get_loc(None)
619
+
620
+
621
+ def test_getitem_1tuple_slice_without_multiindex():
622
+ ser = Series(range(5))
623
+ key = (slice(3),)
624
+
625
+ result = ser[key]
626
+ expected = ser[key[0]]
627
+ tm.assert_series_equal(result, expected)
628
+
629
+
630
+ def test_getitem_preserve_name(datetime_series):
631
+ result = datetime_series[datetime_series > 0]
632
+ assert result.name == datetime_series.name
633
+
634
+ result = datetime_series[5:10]
635
+ assert result.name == datetime_series.name
636
+
637
+
638
+ def test_getitem_with_integer_labels():
639
+ # integer indexes, be careful
640
+ ser = Series(
641
+ np.random.default_rng(2).standard_normal(10), index=list(range(0, 20, 2))
642
+ )
643
+ inds = [0, 2, 5, 7, 8]
644
+ arr_inds = np.array([0, 2, 5, 7, 8])
645
+ with pytest.raises(KeyError, match="not in index"):
646
+ ser[inds]
647
+
648
+ with pytest.raises(KeyError, match="not in index"):
649
+ ser[arr_inds]
650
+
651
+
652
+ def test_getitem_missing(datetime_series):
653
+ # missing
654
+ d = datetime_series.index[0] - BDay()
655
+ msg = r"Timestamp\('1999-12-31 00:00:00'\)"
656
+ with pytest.raises(KeyError, match=msg):
657
+ datetime_series[d]
658
+
659
+
660
+ def test_getitem_fancy(string_series, object_series):
661
+ msg = r"None of \[Index\(\[1, 2, 3\], dtype='int(32|64)'\)\] are in the \[index\]"
662
+ with pytest.raises(KeyError, match=msg):
663
+ string_series[[1, 2, 3]]
664
+ with pytest.raises(KeyError, match=msg):
665
+ object_series[[1, 2, 3]]
666
+
667
+
668
+ def test_getitem_box_float64(datetime_series):
669
+ with pytest.raises(KeyError, match="^5$"):
670
+ datetime_series[5]
671
+
672
+
673
+ def test_getitem_unordered_dup():
674
+ obj = Series(range(5), index=["c", "a", "a", "b", "b"])
675
+ assert is_scalar(obj["c"])
676
+ assert obj["c"] == 0
677
+
678
+
679
+ def test_getitem_dups():
680
+ ser = Series(range(5), index=["A", "A", "B", "C", "C"], dtype=np.int64)
681
+ expected = Series([3, 4], index=["C", "C"], dtype=np.int64)
682
+ result = ser["C"]
683
+ tm.assert_series_equal(result, expected)
684
+
685
+
686
+ def test_getitem_categorical_str():
687
+ # GH#31765
688
+ ser = Series(range(5), index=Categorical(["a", "b", "c", "a", "b"]))
689
+ result = ser["a"]
690
+ expected = ser.iloc[[0, 3]]
691
+ tm.assert_series_equal(result, expected)
692
+
693
+
694
+ def test_slice_can_reorder_not_uniquely_indexed():
695
+ ser = Series(1, index=["a", "a", "b", "b", "c"])
696
+ ser[::-1] # it works!
697
+
698
+
699
+ @pytest.mark.parametrize("index_vals", ["aabcd", "aadcb"])
700
+ def test_duplicated_index_getitem_positional_indexer(index_vals):
701
+ # GH 11747; changed in 3.0 integers are treated as always-labels
702
+ s = Series(range(5), index=list(index_vals))
703
+
704
+ with pytest.raises(KeyError, match="^3$"):
705
+ s[3]
706
+
707
+
708
+ class TestGetitemDeprecatedIndexers:
709
+ @pytest.mark.parametrize("key", [{1}, {1: 1}])
710
+ def test_getitem_dict_and_set_deprecated(self, key):
711
+ # GH#42825 enforced in 2.0
712
+ ser = Series([1, 2, 3])
713
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
714
+ ser[key]
715
+
716
+ @pytest.mark.parametrize("key", [{1}, {1: 1}])
717
+ def test_setitem_dict_and_set_disallowed(self, key):
718
+ # GH#42825 enforced in 2.0
719
+ ser = Series([1, 2, 3])
720
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
721
+ ser[key] = 1
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_indexing.py ADDED
@@ -0,0 +1,510 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """test get/set & misc"""
2
+
3
+ from datetime import timedelta
4
+ import re
5
+
6
+ import numpy as np
7
+ import pytest
8
+
9
+ from pandas.errors import IndexingError
10
+
11
+ from pandas import (
12
+ NA,
13
+ DataFrame,
14
+ Index,
15
+ IndexSlice,
16
+ MultiIndex,
17
+ NaT,
18
+ Series,
19
+ Timedelta,
20
+ Timestamp,
21
+ concat,
22
+ date_range,
23
+ isna,
24
+ period_range,
25
+ timedelta_range,
26
+ )
27
+ import pandas._testing as tm
28
+
29
+
30
+ def test_basic_indexing():
31
+ s = Series(
32
+ np.random.default_rng(2).standard_normal(5), index=["a", "b", "a", "a", "b"]
33
+ )
34
+
35
+ with pytest.raises(KeyError, match="^5$"):
36
+ s[5]
37
+
38
+ with pytest.raises(KeyError, match=r"^'c'$"):
39
+ s["c"]
40
+
41
+ s = s.sort_index()
42
+
43
+ with pytest.raises(KeyError, match="^5$"):
44
+ s[5]
45
+
46
+
47
+ def test_getitem_numeric_should_not_fallback_to_positional(any_numeric_dtype):
48
+ # GH51053
49
+ dtype = any_numeric_dtype
50
+ idx = Index([1, 0, 1], dtype=dtype)
51
+ ser = Series(range(3), index=idx)
52
+ result = ser[1]
53
+ expected = Series([0, 2], index=Index([1, 1], dtype=dtype))
54
+ tm.assert_series_equal(result, expected, check_exact=True)
55
+
56
+
57
+ def test_setitem_numeric_should_not_fallback_to_positional(any_numeric_dtype):
58
+ # GH51053
59
+ dtype = any_numeric_dtype
60
+ idx = Index([1, 0, 1], dtype=dtype)
61
+ ser = Series(range(3), index=idx)
62
+ ser[1] = 10
63
+ expected = Series([10, 1, 10], index=idx)
64
+ tm.assert_series_equal(ser, expected, check_exact=True)
65
+
66
+
67
+ def test_basic_getitem_with_labels(datetime_series):
68
+ indices = datetime_series.index[[5, 10, 15]]
69
+
70
+ result = datetime_series[indices]
71
+ expected = datetime_series.reindex(indices)
72
+ tm.assert_series_equal(result, expected)
73
+
74
+ result = datetime_series[indices[0] : indices[2]]
75
+ expected = datetime_series.loc[indices[0] : indices[2]]
76
+ tm.assert_series_equal(result, expected)
77
+
78
+
79
+ def test_basic_getitem_dt64tz_values():
80
+ # GH12089
81
+ # with tz for values
82
+ ser = Series(
83
+ date_range("2011-01-01", periods=3, tz="US/Eastern"), index=["a", "b", "c"]
84
+ )
85
+ expected = Timestamp("2011-01-01", tz="US/Eastern")
86
+ result = ser.loc["a"]
87
+ assert result == expected
88
+ result = ser.iloc[0]
89
+ assert result == expected
90
+ result = ser["a"]
91
+ assert result == expected
92
+
93
+
94
+ def test_getitem_setitem_ellipsis():
95
+ s = Series(np.random.default_rng(2).standard_normal(10))
96
+
97
+ result = s[...]
98
+ tm.assert_series_equal(result, s)
99
+
100
+
101
+ @pytest.mark.parametrize(
102
+ "result_1, duplicate_item, expected_1",
103
+ [
104
+ [
105
+ {1: 12, 2: [1, 2, 2, 3]},
106
+ {1: 313},
107
+ Series({1: 12}, dtype=object),
108
+ ],
109
+ [
110
+ {1: [1, 2, 3], 2: [1, 2, 2, 3]},
111
+ {1: [1, 2, 3]},
112
+ Series({1: [1, 2, 3]}),
113
+ ],
114
+ ],
115
+ )
116
+ def test_getitem_with_duplicates_indices(result_1, duplicate_item, expected_1):
117
+ # GH 17610
118
+ result_1 = Series(result_1)
119
+ duplicate_item = Series(duplicate_item)
120
+ result = result_1._append_internal(duplicate_item)
121
+ expected = expected_1._append_internal(duplicate_item)
122
+ tm.assert_series_equal(result[1], expected)
123
+ assert result[2] == result_1[2]
124
+
125
+
126
+ def test_getitem_setitem_integers():
127
+ # caused bug without test
128
+ s = Series([1, 2, 3], ["a", "b", "c"])
129
+
130
+ assert s.iloc[0] == s["a"]
131
+ s.iloc[0] = 5
132
+ tm.assert_almost_equal(s["a"], 5)
133
+
134
+
135
+ def test_series_box_timestamp():
136
+ rng = date_range("20090415", "20090519", freq="B")
137
+ ser = Series(rng)
138
+ assert isinstance(ser[0], Timestamp)
139
+ assert isinstance(ser.at[1], Timestamp)
140
+ assert isinstance(ser.iat[2], Timestamp)
141
+ assert isinstance(ser.loc[3], Timestamp)
142
+ assert isinstance(ser.iloc[4], Timestamp)
143
+
144
+ ser = Series(rng, index=rng)
145
+ assert isinstance(ser[rng[0]], Timestamp)
146
+ assert isinstance(ser.at[rng[1]], Timestamp)
147
+ assert isinstance(ser.iat[2], Timestamp)
148
+ assert isinstance(ser.loc[rng[3]], Timestamp)
149
+ assert isinstance(ser.iloc[4], Timestamp)
150
+
151
+
152
+ def test_series_box_timedelta():
153
+ rng = timedelta_range("1 day 1 s", periods=5, freq="h")
154
+ ser = Series(rng)
155
+ assert isinstance(ser[0], Timedelta)
156
+ assert isinstance(ser.at[1], Timedelta)
157
+ assert isinstance(ser.iat[2], Timedelta)
158
+ assert isinstance(ser.loc[3], Timedelta)
159
+ assert isinstance(ser.iloc[4], Timedelta)
160
+
161
+
162
+ def test_getitem_ambiguous_keyerror(indexer_sl):
163
+ ser = Series(range(10), index=list(range(0, 20, 2)))
164
+ with pytest.raises(KeyError, match=r"^1$"):
165
+ indexer_sl(ser)[1]
166
+
167
+
168
+ def test_getitem_dups_with_missing(indexer_sl):
169
+ # breaks reindex, so need to use .loc internally
170
+ # GH 4246
171
+ ser = Series([1, 2, 3, 4], ["foo", "bar", "foo", "bah"])
172
+ with pytest.raises(KeyError, match=re.escape("['bam'] not in index")):
173
+ indexer_sl(ser)[["foo", "bar", "bah", "bam"]]
174
+
175
+
176
+ def test_setitem_ambiguous_keyerror(indexer_sl):
177
+ s = Series(range(10), index=list(range(0, 20, 2)))
178
+
179
+ # equivalent of an append
180
+ s2 = s.copy()
181
+ indexer_sl(s2)[1] = 5
182
+ expected = concat([s, Series([5], index=[1])])
183
+ tm.assert_series_equal(s2, expected)
184
+
185
+
186
+ def test_setitem(datetime_series):
187
+ datetime_series[datetime_series.index[5]] = np.nan
188
+ datetime_series.iloc[[1, 2, 17]] = np.nan
189
+ datetime_series.iloc[6] = np.nan
190
+ assert np.isnan(datetime_series.iloc[6])
191
+ assert np.isnan(datetime_series.iloc[2])
192
+ datetime_series[np.isnan(datetime_series)] = 5
193
+ assert not np.isnan(datetime_series.iloc[2])
194
+
195
+
196
+ def test_setslice(datetime_series):
197
+ sl = datetime_series[5:20]
198
+ assert len(sl) == len(sl.index)
199
+ assert sl.index.is_unique is True
200
+
201
+
202
+ def test_basic_getitem_setitem_corner(datetime_series):
203
+ # invalid tuples, e.g. td.ts[:, None] vs. td.ts[:, 2]
204
+ msg = "key of type tuple not found and not a MultiIndex"
205
+ with pytest.raises(KeyError, match=msg):
206
+ datetime_series[:, 2]
207
+ with pytest.raises(KeyError, match=msg):
208
+ datetime_series[:, 2] = 2
209
+
210
+ # weird lists. [slice(0, 5)] raises but not two slices
211
+ msg = "Indexing with a single-item list"
212
+ with pytest.raises(ValueError, match=msg):
213
+ # GH#31299
214
+ datetime_series[[slice(None, 5)]]
215
+
216
+ # but we're OK with a single-element tuple
217
+ result = datetime_series[(slice(None, 5),)]
218
+ expected = datetime_series[:5]
219
+ tm.assert_series_equal(result, expected)
220
+
221
+ # OK
222
+ msg = r"unhashable type(: 'slice')?"
223
+ with pytest.raises(TypeError, match=msg):
224
+ datetime_series[[5, [None, None]]]
225
+ with pytest.raises(TypeError, match=msg):
226
+ datetime_series[[5, [None, None]]] = 2
227
+
228
+
229
+ def test_slice(string_series, object_series):
230
+ original = string_series.copy()
231
+ numSlice = string_series[10:20]
232
+ numSliceEnd = string_series[-10:]
233
+ objSlice = object_series[10:20]
234
+
235
+ assert string_series.index[9] not in numSlice.index
236
+ assert object_series.index[9] not in objSlice.index
237
+
238
+ assert len(numSlice) == len(numSlice.index)
239
+ assert string_series[numSlice.index[0]] == numSlice[numSlice.index[0]]
240
+
241
+ assert numSlice.index[1] == string_series.index[11]
242
+ tm.assert_numpy_array_equal(np.array(numSliceEnd), np.array(string_series)[-10:])
243
+
244
+ # Test return view.
245
+ sl = string_series[10:20]
246
+ sl[:] = 0
247
+
248
+ # Doesn't modify parent (CoW)
249
+ tm.assert_series_equal(string_series, original)
250
+
251
+
252
+ def test_timedelta_assignment():
253
+ # GH 8209
254
+ s = Series([], dtype=object)
255
+ s.loc["B"] = timedelta(1)
256
+ expected = Series(Timedelta("1 days"), dtype="timedelta64[us]", index=["B"])
257
+ tm.assert_series_equal(s, expected)
258
+
259
+ s = s.reindex(s.index.insert(0, "A"))
260
+ expected = Series(
261
+ [np.nan, Timedelta("1 days")], dtype="timedelta64[us]", index=["A", "B"]
262
+ )
263
+ tm.assert_series_equal(s, expected)
264
+
265
+ s.loc["A"] = timedelta(1)
266
+ expected = Series(Timedelta("1 days"), dtype="timedelta64[us]", index=["A", "B"])
267
+ tm.assert_series_equal(s, expected)
268
+
269
+
270
+ def test_underlying_data_conversion():
271
+ # GH 4080
272
+ df = DataFrame({c: [1, 2, 3] for c in ["a", "b", "c"]})
273
+ return_value = df.set_index(["a", "b", "c"], inplace=True)
274
+ assert return_value is None
275
+ s = Series([1], index=[(2, 2, 2)])
276
+ df["val"] = 0
277
+ df_original = df.copy()
278
+ df
279
+
280
+ with tm.raises_chained_assignment_error():
281
+ df["val"].update(s)
282
+ expected = df_original
283
+ tm.assert_frame_equal(df, expected)
284
+
285
+
286
+ def test_preserve_refs(datetime_series):
287
+ seq = datetime_series.iloc[[5, 10, 15]]
288
+ seq.iloc[1] = np.nan
289
+ assert not np.isnan(datetime_series.iloc[10])
290
+
291
+
292
+ def test_multilevel_preserve_name(lexsorted_two_level_string_multiindex, indexer_sl):
293
+ index = lexsorted_two_level_string_multiindex
294
+ ser = Series(
295
+ np.random.default_rng(2).standard_normal(len(index)), index=index, name="sth"
296
+ )
297
+
298
+ result = indexer_sl(ser)["foo"]
299
+ assert result.name == ser.name
300
+
301
+
302
+ # miscellaneous methods
303
+
304
+
305
+ @pytest.mark.parametrize(
306
+ "index",
307
+ [
308
+ date_range("2014-01-01", periods=20, freq="MS"),
309
+ period_range("2014-01", periods=20, freq="M"),
310
+ timedelta_range("0", periods=20, freq="h"),
311
+ ],
312
+ )
313
+ def test_slice_with_negative_step(index):
314
+ keystr1 = str(index[9])
315
+ keystr2 = str(index[13])
316
+
317
+ ser = Series(np.arange(20), index)
318
+ SLC = IndexSlice
319
+
320
+ for key in [keystr1, index[9]]:
321
+ tm.assert_indexing_slices_equivalent(ser, SLC[key::-1], SLC[9::-1])
322
+ tm.assert_indexing_slices_equivalent(ser, SLC[:key:-1], SLC[:8:-1])
323
+
324
+ for key2 in [keystr2, index[13]]:
325
+ tm.assert_indexing_slices_equivalent(ser, SLC[key2:key:-1], SLC[13:8:-1])
326
+ tm.assert_indexing_slices_equivalent(ser, SLC[key:key2:-1], SLC[0:0:-1])
327
+
328
+
329
+ def test_tuple_index():
330
+ # GH 35534 - Selecting values when a Series has an Index of tuples
331
+ s = Series([1, 2], index=[("a",), ("b",)])
332
+ assert s[("a",)] == 1
333
+ assert s[("b",)] == 2
334
+ s[("b",)] = 3
335
+ assert s[("b",)] == 3
336
+
337
+
338
+ def test_frozenset_index():
339
+ # GH35747 - Selecting values when a Series has an Index of frozenset
340
+ idx0, idx1 = frozenset("a"), frozenset("b")
341
+ s = Series([1, 2], index=[idx0, idx1])
342
+ assert s[idx0] == 1
343
+ assert s[idx1] == 2
344
+ s[idx1] = 3
345
+ assert s[idx1] == 3
346
+
347
+
348
+ def test_loc_setitem_all_false_indexer():
349
+ # GH#45778
350
+ ser = Series([1, 2], index=["a", "b"])
351
+ expected = ser.copy()
352
+ rhs = Series([6, 7], index=["a", "b"])
353
+ ser.loc[ser > 100] = rhs
354
+ tm.assert_series_equal(ser, expected)
355
+
356
+
357
+ def test_loc_boolean_indexer_non_matching_index():
358
+ # GH#46551
359
+ ser = Series([1])
360
+ result = ser.loc[Series([NA, False], dtype="boolean")]
361
+ expected = Series([], dtype="int64")
362
+ tm.assert_series_equal(result, expected)
363
+
364
+
365
+ def test_loc_boolean_indexer_miss_matching_index():
366
+ # GH#46551
367
+ ser = Series([1])
368
+ indexer = Series([NA, False], dtype="boolean", index=[1, 2])
369
+ with pytest.raises(IndexingError, match="Unalignable"):
370
+ ser.loc[indexer]
371
+
372
+
373
+ def test_loc_setitem_nested_data_enlargement():
374
+ # GH#48614
375
+ df = DataFrame({"a": [1]})
376
+ ser = Series({"label": df})
377
+ ser.loc["new_label"] = df
378
+ expected = Series({"label": df, "new_label": df})
379
+ tm.assert_series_equal(ser, expected)
380
+
381
+
382
+ def test_loc_ea_numeric_index_oob_slice_end():
383
+ # GH#50161
384
+ ser = Series(1, index=Index([0, 1, 2], dtype="Int64"))
385
+ result = ser.loc[2:3]
386
+ expected = Series(1, index=Index([2], dtype="Int64"))
387
+ tm.assert_series_equal(result, expected)
388
+
389
+
390
+ def test_getitem_bool_int_key():
391
+ # GH#48653
392
+ ser = Series({True: 1, False: 0})
393
+ with pytest.raises(KeyError, match="0"):
394
+ ser.loc[0]
395
+
396
+
397
+ @pytest.mark.parametrize("val", [{}, {"b": "x"}])
398
+ @pytest.mark.parametrize("indexer", [[], [False, False], slice(0, -1), np.array([])])
399
+ def test_setitem_empty_indexer(indexer, val):
400
+ # GH#45981
401
+ df = DataFrame({"a": [1, 2], **val})
402
+ expected = df.copy()
403
+ df.loc[indexer] = 1.5
404
+ tm.assert_frame_equal(df, expected)
405
+
406
+
407
+ def test_loc_non_monotonic_index_with_a_missing_label():
408
+ msg = "Cannot get left slice bound for non-monotonic index with a missing label 4"
409
+ ser = Series([3, 6, 7, 6], index=[3, 8, 7, 6])
410
+ with pytest.raises(KeyError, match=msg):
411
+ ser.loc[4:7]
412
+
413
+
414
+ class TestDeprecatedIndexers:
415
+ @pytest.mark.parametrize("key", [{1}, {1: 1}])
416
+ def test_getitem_dict_and_set_deprecated(self, key):
417
+ # GH#42825 enforced in 2.0
418
+ ser = Series([1, 2])
419
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
420
+ ser.loc[key]
421
+
422
+ @pytest.mark.parametrize("key", [{1}, {1: 1}, ({1}, 2), ({1: 1}, 2)])
423
+ def test_getitem_dict_and_set_deprecated_multiindex(self, key):
424
+ # GH#42825 enforced in 2.0
425
+ ser = Series([1, 2], index=MultiIndex.from_tuples([(1, 2), (3, 4)]))
426
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
427
+ ser.loc[key]
428
+
429
+ @pytest.mark.parametrize("key", [{1}, {1: 1}])
430
+ def test_setitem_dict_and_set_disallowed(self, key):
431
+ # GH#42825 enforced in 2.0
432
+ ser = Series([1, 2])
433
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
434
+ ser.loc[key] = 1
435
+
436
+ @pytest.mark.parametrize("key", [{1}, {1: 1}, ({1}, 2), ({1: 1}, 2)])
437
+ def test_setitem_dict_and_set_disallowed_multiindex(self, key):
438
+ # GH#42825 enforced in 2.0
439
+ ser = Series([1, 2], index=MultiIndex.from_tuples([(1, 2), (3, 4)]))
440
+ with pytest.raises(TypeError, match="as an indexer is not supported"):
441
+ ser.loc[key] = 1
442
+
443
+
444
+ class TestSetitemValidation:
445
+ # This is adapted from pandas/tests/arrays/masked/test_indexing.py
446
+ def _check_setitem_invalid(self, ser, invalid, indexer):
447
+ orig_ser = ser.copy()
448
+
449
+ with pytest.raises(TypeError, match="Invalid value"):
450
+ ser[indexer] = invalid
451
+ ser = orig_ser.copy()
452
+
453
+ with pytest.raises(TypeError, match="Invalid value"):
454
+ ser.iloc[indexer] = invalid
455
+ ser = orig_ser.copy()
456
+
457
+ with pytest.raises(TypeError, match="Invalid value"):
458
+ ser.loc[indexer] = invalid
459
+ ser = orig_ser.copy()
460
+
461
+ with pytest.raises(TypeError, match="Invalid value"):
462
+ ser[:] = invalid
463
+
464
+ def _check_setitem_valid(self, ser, value, indexer):
465
+ orig_ser = ser.copy()
466
+
467
+ ser[indexer] = value
468
+ ser = orig_ser.copy()
469
+
470
+ ser.iloc[indexer] = value
471
+ ser = orig_ser.copy()
472
+
473
+ ser.loc[indexer] = value
474
+ ser = orig_ser.copy()
475
+
476
+ ser[:] = value
477
+
478
+ _invalid_scalars = [
479
+ 1 + 2j,
480
+ "True",
481
+ "1",
482
+ "1.0",
483
+ NaT,
484
+ np.datetime64("NaT", "ns"),
485
+ np.timedelta64("NaT", "ns"),
486
+ ]
487
+ _indexers = [0, [0], slice(0, 1), [True, False, False], slice(None, None, None)]
488
+
489
+ @pytest.mark.parametrize(
490
+ "invalid", [*_invalid_scalars, 1, 1.0, np.int64(1), np.float64(1)]
491
+ )
492
+ @pytest.mark.parametrize("indexer", _indexers)
493
+ def test_setitem_validation_scalar_bool(self, invalid, indexer):
494
+ ser = Series([True, False, False], dtype="bool")
495
+ self._check_setitem_invalid(ser, invalid, indexer)
496
+
497
+ @pytest.mark.parametrize("invalid", [*_invalid_scalars, True, 1.5, np.float64(1.5)])
498
+ @pytest.mark.parametrize("indexer", _indexers)
499
+ def test_setitem_validation_scalar_int(self, invalid, any_int_numpy_dtype, indexer):
500
+ ser = Series([1, 2, 3], dtype=any_int_numpy_dtype)
501
+ if isna(invalid) and invalid is not NaT and not np.isnat(invalid):
502
+ self._check_setitem_valid(ser, invalid, indexer)
503
+ else:
504
+ self._check_setitem_invalid(ser, invalid, indexer)
505
+
506
+ @pytest.mark.parametrize("invalid", [*_invalid_scalars, True])
507
+ @pytest.mark.parametrize("indexer", _indexers)
508
+ def test_setitem_validation_scalar_float(self, invalid, float_numpy_dtype, indexer):
509
+ ser = Series([1, 2, None], dtype=float_numpy_dtype)
510
+ self._check_setitem_invalid(ser, invalid, indexer)
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_mask.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import Series
5
+ import pandas._testing as tm
6
+
7
+
8
+ def test_mask():
9
+ # compare with tested results in test_where
10
+ s = Series(np.random.default_rng(2).standard_normal(5))
11
+ cond = s > 0
12
+
13
+ rs = s.where(~cond, np.nan)
14
+ tm.assert_series_equal(rs, s.mask(cond))
15
+
16
+ rs = s.where(~cond)
17
+ rs2 = s.mask(cond)
18
+ tm.assert_series_equal(rs, rs2)
19
+
20
+ rs = s.where(~cond, -s)
21
+ rs2 = s.mask(cond, -s)
22
+ tm.assert_series_equal(rs, rs2)
23
+
24
+ cond = Series([True, False, False, True, False], index=s.index)
25
+ s2 = -(s.abs())
26
+ rs = s2.where(~cond[:3])
27
+ rs2 = s2.mask(cond[:3])
28
+ tm.assert_series_equal(rs, rs2)
29
+
30
+ rs = s2.where(~cond[:3], -s2)
31
+ rs2 = s2.mask(cond[:3], -s2)
32
+ tm.assert_series_equal(rs, rs2)
33
+
34
+ msg = "Array conditional must be same shape as self"
35
+ with pytest.raises(ValueError, match=msg):
36
+ s.mask(1)
37
+ with pytest.raises(ValueError, match=msg):
38
+ s.mask(cond[:3].values, -s)
39
+
40
+
41
+ def test_mask_casts():
42
+ # dtype changes
43
+ ser = Series([1, 2, 3, 4])
44
+ result = ser.mask(ser > 2, np.nan)
45
+ expected = Series([1, 2, np.nan, np.nan])
46
+ tm.assert_series_equal(result, expected)
47
+
48
+
49
+ def test_mask_casts2():
50
+ # see gh-21891
51
+ ser = Series([1, 2])
52
+ res = ser.mask([True, False])
53
+
54
+ exp = Series([np.nan, 2])
55
+ tm.assert_series_equal(res, exp)
56
+
57
+
58
+ def test_mask_inplace():
59
+ s = Series(np.random.default_rng(2).standard_normal(5))
60
+ cond = s > 0
61
+
62
+ rs = s.copy()
63
+ rs.mask(cond, inplace=True)
64
+ tm.assert_series_equal(rs.dropna(), s[~cond])
65
+ tm.assert_series_equal(rs, s.mask(cond))
66
+
67
+ rs = s.copy()
68
+ rs.mask(cond, -s, inplace=True)
69
+ tm.assert_series_equal(rs, s.mask(cond, -s))
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_set_value.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+
3
+ import numpy as np
4
+
5
+ from pandas import (
6
+ DatetimeIndex,
7
+ Index,
8
+ Series,
9
+ )
10
+ import pandas._testing as tm
11
+
12
+
13
+ def test_series_set_value(using_infer_string):
14
+ # GH#1561, GH#51363 as of 3.0 we do not do inference in Index.insert
15
+
16
+ dates = [datetime(2001, 1, 1), datetime(2001, 1, 2)]
17
+ if using_infer_string:
18
+ index = DatetimeIndex(dates)
19
+ else:
20
+ index = Index(dates, dtype=object)
21
+
22
+ s = Series(dtype=object)
23
+ s._set_value(dates[0], 1.0)
24
+ s._set_value(dates[1], np.nan)
25
+
26
+ expected = Series([1.0, np.nan], index=index)
27
+
28
+ tm.assert_series_equal(s, expected)
29
+
30
+
31
+ def test_set_value_dt64(datetime_series):
32
+ idx = datetime_series.index[10]
33
+ res = datetime_series._set_value(idx, 0)
34
+ assert res is None
35
+ assert datetime_series[idx] == 0
36
+
37
+
38
+ def test_set_value_str_index(string_series):
39
+ # equiv
40
+ ser = string_series.copy()
41
+ res = ser._set_value("foobar", 0)
42
+ assert res is None
43
+ assert ser.index[-1] == "foobar"
44
+ assert ser["foobar"] == 0
45
+
46
+ ser2 = string_series.copy()
47
+ ser2.loc["foobar"] = 0
48
+ assert ser2.index[-1] == "foobar"
49
+ assert ser2["foobar"] == 0
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_setitem.py ADDED
@@ -0,0 +1,1843 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ from datetime import (
3
+ date,
4
+ datetime,
5
+ )
6
+ from decimal import Decimal
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from pandas.compat.numpy import np_version_gt2
12
+ from pandas.errors import IndexingError
13
+
14
+ from pandas.core.dtypes.common import is_list_like
15
+
16
+ from pandas import (
17
+ NA,
18
+ Categorical,
19
+ DataFrame,
20
+ DatetimeIndex,
21
+ Index,
22
+ Interval,
23
+ IntervalIndex,
24
+ MultiIndex,
25
+ NaT,
26
+ Period,
27
+ Series,
28
+ StringDtype,
29
+ Timedelta,
30
+ Timestamp,
31
+ array,
32
+ concat,
33
+ date_range,
34
+ interval_range,
35
+ period_range,
36
+ timedelta_range,
37
+ )
38
+ import pandas._testing as tm
39
+
40
+ from pandas.tseries.offsets import BDay
41
+
42
+
43
+ class TestSetitemDT64Values:
44
+ def test_setitem_none_nan(self):
45
+ series = Series(date_range("1/1/2000", periods=10, unit="ns"))
46
+ series[3] = None
47
+ assert series[3] is NaT
48
+
49
+ series[3:5] = None
50
+ assert series[4] is NaT
51
+
52
+ series[5] = np.nan
53
+ assert series[5] is NaT
54
+
55
+ series[5:7] = np.nan
56
+ assert series[6] is NaT
57
+
58
+ def test_setitem_multiindex_empty_slice(self):
59
+ # https://github.com/pandas-dev/pandas/issues/35878
60
+ idx = MultiIndex.from_tuples([("a", 1), ("b", 2)])
61
+ result = Series([1, 2], index=idx)
62
+ expected = result.copy()
63
+ result.loc[[]] = 0
64
+ tm.assert_series_equal(result, expected)
65
+
66
+ def test_setitem_with_string_index(self):
67
+ # GH#23451
68
+ # Set object dtype to avoid upcast when setting date.today()
69
+ ser = Series([1, 2, 3], index=["Date", "b", "other"], dtype=object)
70
+ ser["Date"] = date.today()
71
+ assert ser.Date == date.today()
72
+ assert ser["Date"] == date.today()
73
+
74
+ def test_setitem_tuple_with_datetimetz_values(self):
75
+ # GH#20441
76
+ arr = date_range("2017", periods=4, tz="US/Eastern", unit="ns")
77
+ index = [(0, 1), (0, 2), (0, 3), (0, 4)]
78
+ result = Series(arr, index=index)
79
+ expected = result.copy()
80
+ result[(0, 1)] = np.nan
81
+ expected.iloc[0] = np.nan
82
+ tm.assert_series_equal(result, expected)
83
+
84
+ @pytest.mark.parametrize("tz", ["US/Eastern", "UTC", "Asia/Tokyo"])
85
+ def test_setitem_with_tz(self, tz, indexer_sli):
86
+ orig = Series(date_range("2016-01-01", freq="h", periods=3, tz=tz, unit="ns"))
87
+ assert orig.dtype == f"datetime64[ns, {tz}]"
88
+
89
+ exp = Series(
90
+ [
91
+ Timestamp("2016-01-01 00:00", tz=tz),
92
+ Timestamp("2011-01-01 00:00", tz=tz),
93
+ Timestamp("2016-01-01 02:00", tz=tz),
94
+ ],
95
+ dtype=orig.dtype,
96
+ )
97
+
98
+ # scalar
99
+ ser = orig.copy()
100
+ indexer_sli(ser)[1] = Timestamp("2011-01-01", tz=tz)
101
+ tm.assert_series_equal(ser, exp)
102
+
103
+ # vector
104
+ vals = Series(
105
+ [Timestamp("2011-01-01", tz=tz), Timestamp("2012-01-01", tz=tz)],
106
+ index=[1, 2],
107
+ dtype=orig.dtype,
108
+ )
109
+ assert vals.dtype == f"datetime64[ns, {tz}]"
110
+
111
+ exp = Series(
112
+ [
113
+ Timestamp("2016-01-01 00:00", tz=tz),
114
+ Timestamp("2011-01-01 00:00", tz=tz),
115
+ Timestamp("2012-01-01 00:00", tz=tz),
116
+ ],
117
+ dtype=orig.dtype,
118
+ )
119
+
120
+ ser = orig.copy()
121
+ indexer_sli(ser)[[1, 2]] = vals
122
+ tm.assert_series_equal(ser, exp)
123
+
124
+ def test_setitem_with_tz_dst(self, indexer_sli):
125
+ # GH#14146 trouble setting values near DST boundary
126
+ tz = "US/Eastern"
127
+ orig = Series(date_range("2016-11-06", freq="h", periods=3, tz=tz, unit="ns"))
128
+ assert orig.dtype == f"datetime64[ns, {tz}]"
129
+
130
+ exp = Series(
131
+ [
132
+ Timestamp("2016-11-06 00:00-04:00", tz=tz),
133
+ Timestamp("2011-01-01 00:00-05:00", tz=tz),
134
+ Timestamp("2016-11-06 01:00-05:00", tz=tz),
135
+ ],
136
+ dtype=orig.dtype,
137
+ )
138
+
139
+ # scalar
140
+ ser = orig.copy()
141
+ indexer_sli(ser)[1] = Timestamp("2011-01-01", tz=tz)
142
+ tm.assert_series_equal(ser, exp)
143
+
144
+ # vector
145
+ vals = Series(
146
+ [Timestamp("2011-01-01", tz=tz), Timestamp("2012-01-01", tz=tz)],
147
+ index=[1, 2],
148
+ dtype=orig.dtype,
149
+ )
150
+ assert vals.dtype == f"datetime64[ns, {tz}]"
151
+
152
+ exp = Series(
153
+ [
154
+ Timestamp("2016-11-06 00:00", tz=tz),
155
+ Timestamp("2011-01-01 00:00", tz=tz),
156
+ Timestamp("2012-01-01 00:00", tz=tz),
157
+ ],
158
+ dtype=orig.dtype,
159
+ )
160
+
161
+ ser = orig.copy()
162
+ indexer_sli(ser)[[1, 2]] = vals
163
+ tm.assert_series_equal(ser, exp)
164
+
165
+ def test_object_series_setitem_dt64array_exact_match(self):
166
+ # make sure the dt64 isn't cast by numpy to integers
167
+ # https://github.com/numpy/numpy/issues/12550
168
+
169
+ ser = Series({"X": np.nan}, dtype=object)
170
+
171
+ indexer = [True]
172
+
173
+ # "exact_match" -> size of array being set matches size of ser
174
+ value = np.array([4], dtype="M8[ns]")
175
+
176
+ ser.iloc[indexer] = value
177
+
178
+ expected = Series([value[0]], index=["X"], dtype=object)
179
+ assert all(isinstance(x, np.datetime64) for x in expected.values)
180
+
181
+ tm.assert_series_equal(ser, expected)
182
+
183
+
184
+ class TestSetitemScalarIndexer:
185
+ def test_setitem_negative_out_of_bounds(self):
186
+ # As of 3.0, int keys are treated as labels, so this becomes
187
+ # setitem-with-expansion
188
+ ser = Series(["a"] * 10, index=["a"] * 10)
189
+ ser[-11] = "foo"
190
+ exp = Series(["a"] * 10 + ["foo"], index=["a"] * 10 + [-11])
191
+ tm.assert_series_equal(ser, exp)
192
+
193
+ @pytest.mark.parametrize("indexer", [tm.loc, tm.at])
194
+ @pytest.mark.parametrize("ser_index", [0, 1])
195
+ def test_setitem_series_object_dtype(self, indexer, ser_index):
196
+ # GH#38303
197
+ ser = Series([0, 0], dtype="object")
198
+ idxr = indexer(ser)
199
+ idxr[0] = Series([42], index=[ser_index])
200
+ expected = Series([Series([42], index=[ser_index]), 0], dtype="object")
201
+ tm.assert_series_equal(ser, expected)
202
+
203
+ @pytest.mark.parametrize("index, exp_value", [(0, 42), (1, np.nan)])
204
+ def test_setitem_series(self, index, exp_value):
205
+ # GH#38303
206
+ ser = Series([0, 0])
207
+ ser.loc[0] = Series([42], index=[index])
208
+ expected = Series([exp_value, 0])
209
+ tm.assert_series_equal(ser, expected)
210
+
211
+
212
+ class TestSetitemSlices:
213
+ def test_setitem_slice_float_raises(self, datetime_series):
214
+ msg = (
215
+ "cannot do slice indexing on DatetimeIndex with these indexers "
216
+ r"\[{key}\] of type float"
217
+ )
218
+ with pytest.raises(TypeError, match=msg.format(key=r"4\.0")):
219
+ datetime_series[4.0:10.0] = 0
220
+
221
+ with pytest.raises(TypeError, match=msg.format(key=r"4\.5")):
222
+ datetime_series[4.5:10.0] = 0
223
+
224
+ def test_setitem_slice(self):
225
+ ser = Series(range(10), index=list(range(10)))
226
+ ser[-12:] = 0
227
+ assert (ser == 0).all()
228
+
229
+ ser[:-12] = 5
230
+ assert (ser == 0).all()
231
+
232
+ def test_setitem_slice_integers(self):
233
+ ser = Series(
234
+ np.random.default_rng(2).standard_normal(8),
235
+ index=[2, 4, 6, 8, 10, 12, 14, 16],
236
+ )
237
+
238
+ ser[:4] = 0
239
+ assert (ser[:4] == 0).all()
240
+ assert not (ser[4:] == 0).any()
241
+
242
+ def test_setitem_slicestep(self):
243
+ # caught this bug when writing tests
244
+ series = Series(
245
+ np.arange(20, dtype=np.float64), index=np.arange(20, dtype=np.int64)
246
+ )
247
+
248
+ series[::2] = 0
249
+ assert (series[::2] == 0).all()
250
+
251
+ def test_setitem_multiindex_slice(self, indexer_sli):
252
+ # GH 8856
253
+ mi = MultiIndex.from_product(([0, 1], list("abcde")))
254
+ result = Series(np.arange(10, dtype=np.int64), mi)
255
+ indexer_sli(result)[::4] = 100
256
+ expected = Series([100, 1, 2, 3, 100, 5, 6, 7, 100, 9], mi)
257
+ tm.assert_series_equal(result, expected)
258
+
259
+
260
+ class TestSetitemBooleanMask:
261
+ def test_setitem_mask_cast(self):
262
+ # GH#2746
263
+ # need to upcast
264
+ ser = Series([1, 2], index=[1, 2], dtype="int64")
265
+ ser[[True, False]] = Series([0], index=[1], dtype="int64")
266
+ expected = Series([0, 2], index=[1, 2], dtype="int64")
267
+
268
+ tm.assert_series_equal(ser, expected)
269
+
270
+ def test_setitem_mask_align_and_promote(self):
271
+ # GH#8387: test that changing types does not break alignment
272
+ ts = Series(
273
+ np.random.default_rng(2).standard_normal(100), index=np.arange(100, 0, -1)
274
+ ).round(5)
275
+ mask = ts > 0
276
+ left = ts.copy()
277
+ right = ts[mask].copy().map(str)
278
+ with pytest.raises(TypeError, match="Invalid value"):
279
+ left[mask] = right
280
+
281
+ def test_setitem_mask_promote_strs(self):
282
+ ser = Series([0, 1, 2, 0])
283
+ mask = ser > 0
284
+ ser2 = ser[mask].map(str)
285
+ with pytest.raises(TypeError, match="Invalid value"):
286
+ ser[mask] = ser2
287
+
288
+ def test_setitem_mask_promote(self):
289
+ ser = Series([0, "foo", "bar", 0])
290
+ mask = Series([False, True, True, False])
291
+ ser2 = ser[mask]
292
+ ser[mask] = ser2
293
+
294
+ expected = Series([0, "foo", "bar", 0])
295
+ tm.assert_series_equal(ser, expected)
296
+
297
+ def test_setitem_boolean(self, string_series):
298
+ mask = string_series > string_series.median()
299
+
300
+ # similar indexed series
301
+ result = string_series.copy()
302
+ result[mask] = string_series * 2
303
+ expected = string_series * 2
304
+ tm.assert_series_equal(result[mask], expected[mask])
305
+
306
+ # needs alignment
307
+ result = string_series.copy()
308
+ result[mask] = (string_series * 2)[0:5]
309
+ expected = (string_series * 2)[0:5].reindex_like(string_series)
310
+ expected[-mask] = string_series[mask]
311
+ tm.assert_series_equal(result[mask], expected[mask])
312
+
313
+ def test_setitem_boolean_corner(self, datetime_series):
314
+ ts = datetime_series
315
+ mask_shifted = ts.shift(1, freq=BDay()) > ts.median()
316
+
317
+ msg = (
318
+ r"Unalignable boolean Series provided as indexer \(index of "
319
+ r"the boolean Series and of the indexed object do not match"
320
+ )
321
+ with pytest.raises(IndexingError, match=msg):
322
+ ts[mask_shifted] = 1
323
+
324
+ with pytest.raises(IndexingError, match=msg):
325
+ ts.loc[mask_shifted] = 1
326
+
327
+ def test_setitem_boolean_different_order(self, string_series):
328
+ ordered = string_series.sort_values()
329
+
330
+ copy = string_series.copy()
331
+ copy[ordered > 0] = 0
332
+
333
+ expected = string_series.copy()
334
+ expected[expected > 0] = 0
335
+
336
+ tm.assert_series_equal(copy, expected)
337
+
338
+ @pytest.mark.parametrize("func", [list, np.array, Series])
339
+ def test_setitem_boolean_python_list(self, func):
340
+ # GH19406
341
+ ser = Series([None, "b", None])
342
+ mask = func([True, False, True])
343
+ ser[mask] = ["a", "c"]
344
+ expected = Series(["a", "b", "c"])
345
+ tm.assert_series_equal(ser, expected)
346
+
347
+ def test_setitem_boolean_nullable_int_types(self, any_numeric_ea_dtype):
348
+ # GH: 26468
349
+ ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
350
+ ser[ser > 6] = Series(range(4), dtype=any_numeric_ea_dtype)
351
+ expected = Series([5, 6, 2, 3], dtype=any_numeric_ea_dtype)
352
+ tm.assert_series_equal(ser, expected)
353
+
354
+ ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
355
+ ser.loc[ser > 6] = Series(range(4), dtype=any_numeric_ea_dtype)
356
+ tm.assert_series_equal(ser, expected)
357
+
358
+ ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
359
+ loc_ser = Series(range(4), dtype=any_numeric_ea_dtype)
360
+ ser.loc[ser > 6] = loc_ser.loc[loc_ser > 1]
361
+ tm.assert_series_equal(ser, expected)
362
+
363
+ def test_setitem_with_bool_mask_and_values_matching_n_trues_in_length(self):
364
+ # GH#30567
365
+ ser = Series([None] * 10)
366
+ mask = [False] * 3 + [True] * 5 + [False] * 2
367
+ ser[mask] = range(5)
368
+ result = ser
369
+ expected = Series([None] * 3 + list(range(5)) + [None] * 2, dtype=object)
370
+ tm.assert_series_equal(result, expected)
371
+
372
+ def test_setitem_nan_with_bool(self):
373
+ # GH 13034
374
+ result = Series([True, False, True])
375
+ with pytest.raises(TypeError, match="Invalid value"):
376
+ result[0] = np.nan
377
+
378
+ def test_setitem_mask_smallint_upcast(self):
379
+ orig = Series([1, 2, 3], dtype="int8")
380
+ alt = np.array([999, 1000, 1001], dtype=np.int64)
381
+
382
+ mask = np.array([True, False, True])
383
+
384
+ ser = orig.copy()
385
+ with pytest.raises(TypeError, match="Invalid value"):
386
+ ser[mask] = Series(alt)
387
+
388
+ with pytest.raises(TypeError, match="Invalid value"):
389
+ ser.mask(mask, alt, inplace=True)
390
+
391
+ res = ser.where(~mask, Series(alt))
392
+ expected = Series([999, 2, 1001])
393
+ tm.assert_series_equal(res, expected)
394
+
395
+ def test_setitem_mask_smallint_no_upcast(self):
396
+ # like test_setitem_mask_smallint_upcast, but while we can't hold 'alt',
397
+ # we *can* hold alt[mask] without casting
398
+ orig = Series([1, 2, 3], dtype="uint8")
399
+ alt = Series([245, 1000, 246], dtype=np.int64)
400
+
401
+ mask = np.array([True, False, True])
402
+
403
+ ser = orig.copy()
404
+ ser[mask] = alt
405
+ expected = Series([245, 2, 246], dtype="uint8")
406
+ tm.assert_series_equal(ser, expected)
407
+
408
+ ser2 = orig.copy()
409
+ ser2.mask(mask, alt, inplace=True)
410
+ tm.assert_series_equal(ser2, expected)
411
+
412
+ # TODO: ser.where(~mask, alt) unnecessarily upcasts to int64
413
+ ser3 = orig.copy()
414
+ res = ser3.where(~mask, alt)
415
+ tm.assert_series_equal(res, expected, check_dtype=False)
416
+
417
+
418
+ class TestSetitemViewCopySemantics:
419
+ def test_setitem_invalidates_datetime_index_freq(self):
420
+ # GH#24096 altering a datetime64tz Series inplace invalidates the
421
+ # `freq` attribute on the underlying DatetimeIndex
422
+
423
+ dti = date_range("20130101", periods=3, tz="US/Eastern")
424
+ ts = dti[1]
425
+ ser = Series(dti)
426
+ assert ser._values is not dti
427
+ assert ser._values._ndarray.base is dti._data._ndarray.base
428
+ assert dti.freq == "D"
429
+ ser.iloc[1] = NaT
430
+ assert ser._values.freq is None
431
+
432
+ # check that the DatetimeIndex was not altered in place
433
+ assert ser._values is not dti
434
+ assert ser._values._ndarray.base is not dti._data._ndarray.base
435
+ assert dti[1] == ts
436
+ assert dti.freq == "D"
437
+
438
+ def test_dt64tz_setitem_does_not_mutate_dti(self):
439
+ # GH#21907, GH#24096
440
+ dti = date_range("2016-01-01", periods=10, tz="US/Pacific")
441
+ ts = dti[0]
442
+ ser = Series(dti)
443
+ assert ser._values is not dti
444
+ assert ser._values._ndarray.base is dti._data._ndarray.base
445
+ assert ser._mgr.blocks[0].values._ndarray.base is dti._data._ndarray.base
446
+
447
+ assert ser._mgr.blocks[0].values is not dti
448
+
449
+ ser[::3] = NaT
450
+ assert ser[0] is NaT
451
+ assert dti[0] == ts
452
+
453
+
454
+ class TestSetitemCallable:
455
+ def test_setitem_callable_key(self):
456
+ # GH#12533
457
+ ser = Series([1, 2, 3, 4], index=list("ABCD"))
458
+ ser[lambda x: "A"] = -1
459
+
460
+ expected = Series([-1, 2, 3, 4], index=list("ABCD"))
461
+ tm.assert_series_equal(ser, expected)
462
+
463
+ def test_setitem_callable_other(self):
464
+ # GH#13299
465
+ inc = lambda x: x + 1
466
+
467
+ # set object dtype to avoid upcast when setting inc
468
+ ser = Series([1, 2, -1, 4], dtype=object)
469
+ ser[ser < 0] = inc
470
+
471
+ expected = Series([1, 2, inc, 4])
472
+ tm.assert_series_equal(ser, expected)
473
+
474
+
475
+ class TestSetitemWithExpansion:
476
+ def test_setitem_empty_series(self, using_infer_string):
477
+ # GH#10193
478
+ key = Timestamp("2012-01-01")
479
+ series = Series(dtype=object)
480
+ series[key] = 47
481
+ expected = Series(
482
+ 47, index=[key] if using_infer_string else Index([key], dtype=object)
483
+ )
484
+ tm.assert_series_equal(series, expected)
485
+
486
+ def test_setitem_empty_series_datetimeindex_preserves_freq(self):
487
+ # GH#33573 our index should retain its freq
488
+ dti = DatetimeIndex([], freq="D", dtype="M8[ns]")
489
+ series = Series([], index=dti, dtype=object)
490
+ key = Timestamp("2012-01-01")
491
+ series[key] = 47
492
+ expected = Series(47, DatetimeIndex([key], freq="D").as_unit("ns"))
493
+ tm.assert_series_equal(series, expected)
494
+ assert series.index.freq == expected.index.freq
495
+
496
+ def test_setitem_empty_series_timestamp_preserves_dtype(self):
497
+ # GH 21881
498
+ timestamp = Timestamp(1412526600000000000)
499
+ series = Series([timestamp], index=["timestamp"], dtype=object)
500
+ expected = series["timestamp"]
501
+
502
+ series = Series([], dtype=object)
503
+ series["anything"] = 300.0
504
+ series["timestamp"] = timestamp
505
+ result = series["timestamp"]
506
+ assert result == expected
507
+
508
+ @pytest.mark.parametrize(
509
+ "td",
510
+ [
511
+ Timedelta("9 days"),
512
+ Timedelta("9 days").to_timedelta64(),
513
+ Timedelta("9 days").to_pytimedelta(),
514
+ ],
515
+ )
516
+ def test_append_timedelta_does_not_cast(self, td, using_infer_string, request):
517
+ # GH#22717 inserting a Timedelta should _not_ cast to int64
518
+ if using_infer_string and not isinstance(td, Timedelta):
519
+ # TODO: GH#56010
520
+ request.applymarker(pytest.mark.xfail(reason="inferred as string"))
521
+
522
+ expected = Series(["x", td], index=[0, "td"], dtype=object)
523
+
524
+ ser = Series(["x"])
525
+ ser["td"] = td
526
+ tm.assert_series_equal(ser, expected)
527
+ assert isinstance(ser["td"], Timedelta)
528
+
529
+ ser = Series(["x"])
530
+ ser.loc["td"] = Timedelta("9 days")
531
+ tm.assert_series_equal(ser, expected)
532
+ assert isinstance(ser["td"], Timedelta)
533
+
534
+ def test_setitem_with_expansion_type_promotion(self):
535
+ # GH#12599
536
+ ser = Series(dtype=object)
537
+ ser["a"] = Timestamp("2016-01-01")
538
+ ser["b"] = 3.0
539
+ ser["c"] = "foo"
540
+ expected = Series([Timestamp("2016-01-01"), 3.0, "foo"], index=["a", "b", "c"])
541
+ tm.assert_series_equal(ser, expected)
542
+
543
+ def test_setitem_not_contained(self, string_series):
544
+ # set item that's not contained
545
+ ser = string_series.copy()
546
+ assert "foobar" not in ser.index
547
+ ser["foobar"] = 1
548
+
549
+ app = Series([1], index=["foobar"], name="series")
550
+ expected = concat([string_series, app])
551
+ tm.assert_series_equal(ser, expected)
552
+
553
+ def test_setitem_keep_precision(self, any_numeric_ea_dtype):
554
+ # GH#32346
555
+ ser = Series([1, 2], dtype=any_numeric_ea_dtype)
556
+ ser[2] = 10
557
+ expected = Series([1, 2, 10], dtype=any_numeric_ea_dtype)
558
+ tm.assert_series_equal(ser, expected)
559
+
560
+ @pytest.mark.parametrize(
561
+ "na, target_na, dtype, target_dtype, indexer, raises",
562
+ [
563
+ (NA, NA, "Int64", "Int64", 1, False),
564
+ (NA, NA, "Int64", "Int64", 2, False),
565
+ (NA, np.nan, "int64", "float64", 1, False),
566
+ (NA, np.nan, "int64", "float64", 2, False),
567
+ (NaT, NaT, "int64", "object", 1, True),
568
+ (NaT, NaT, "int64", "object", 2, False),
569
+ (np.nan, NA, "Int64", "Int64", 1, False),
570
+ (np.nan, NA, "Int64", "Int64", 2, False),
571
+ (np.nan, NA, "Float64", "Float64", 1, False),
572
+ (np.nan, NA, "Float64", "Float64", 2, False),
573
+ (np.nan, np.nan, "int64", "float64", 1, False),
574
+ (np.nan, np.nan, "int64", "float64", 2, False),
575
+ ],
576
+ )
577
+ def test_setitem_enlarge_with_na(
578
+ self, na, target_na, dtype, target_dtype, indexer, raises
579
+ ):
580
+ # GH#32346
581
+ ser = Series([1, 2], dtype=dtype)
582
+ if raises:
583
+ with pytest.raises(TypeError, match="Invalid value"):
584
+ ser[indexer] = na
585
+ else:
586
+ ser[indexer] = na
587
+ expected_values = [1, target_na] if indexer == 1 else [1, 2, target_na]
588
+ expected = Series(expected_values, dtype=target_dtype)
589
+ tm.assert_series_equal(ser, expected)
590
+
591
+ def test_setitem_enlargement_object_none(self, nulls_fixture, using_infer_string):
592
+ # GH#48665
593
+ ser = Series(["a", "b"])
594
+ ser[3] = nulls_fixture
595
+ dtype = (
596
+ "str"
597
+ if using_infer_string and not isinstance(nulls_fixture, Decimal)
598
+ else object
599
+ )
600
+ expected = Series(["a", "b", nulls_fixture], index=[0, 1, 3], dtype=dtype)
601
+ tm.assert_series_equal(ser, expected)
602
+ if using_infer_string:
603
+ ser[3] is np.nan
604
+ else:
605
+ assert ser[3] is nulls_fixture
606
+
607
+
608
+ def test_setitem_scalar_into_readonly_backing_data():
609
+ # GH#14359: test that you cannot mutate a read only buffer
610
+
611
+ array = np.zeros(5)
612
+ array.flags.writeable = False # make the array immutable
613
+ series = Series(array, copy=False)
614
+
615
+ for n in series.index:
616
+ msg = "assignment destination is read-only"
617
+ with pytest.raises(ValueError, match=msg):
618
+ series[n] = 1
619
+
620
+ assert array[n] == 0
621
+
622
+
623
+ def test_setitem_slice_into_readonly_backing_data():
624
+ # GH#14359: test that you cannot mutate a read only buffer
625
+
626
+ array = np.zeros(5)
627
+ array.flags.writeable = False # make the array immutable
628
+ series = Series(array, copy=False)
629
+
630
+ msg = "assignment destination is read-only"
631
+ with pytest.raises(ValueError, match=msg):
632
+ series[1:3] = 1
633
+
634
+ assert not array.any()
635
+
636
+
637
+ def test_setitem_categorical_assigning_ops():
638
+ orig = Series(Categorical(["b", "b"], categories=["a", "b"]))
639
+ ser = orig.copy()
640
+ ser[:] = "a"
641
+ exp = Series(Categorical(["a", "a"], categories=["a", "b"]))
642
+ tm.assert_series_equal(ser, exp)
643
+
644
+ ser = orig.copy()
645
+ ser[1] = "a"
646
+ exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
647
+ tm.assert_series_equal(ser, exp)
648
+
649
+ ser = orig.copy()
650
+ ser[ser.index > 0] = "a"
651
+ exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
652
+ tm.assert_series_equal(ser, exp)
653
+
654
+ ser = orig.copy()
655
+ ser[[False, True]] = "a"
656
+ exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
657
+ tm.assert_series_equal(ser, exp)
658
+
659
+ ser = orig.copy()
660
+ ser.index = ["x", "y"]
661
+ ser["y"] = "a"
662
+ exp = Series(Categorical(["b", "a"], categories=["a", "b"]), index=["x", "y"])
663
+ tm.assert_series_equal(ser, exp)
664
+
665
+
666
+ def test_setitem_nan_into_categorical():
667
+ # ensure that one can set something to np.nan
668
+ ser = Series(Categorical([1, 2, 3]))
669
+ exp = Series(Categorical([1, np.nan, 3], categories=[1, 2, 3]))
670
+ ser[1] = np.nan
671
+ tm.assert_series_equal(ser, exp)
672
+
673
+
674
+ class TestSetitemCasting:
675
+ @pytest.mark.parametrize("unique", [True, False])
676
+ @pytest.mark.parametrize("val", [3, 3.0, "3"], ids=type)
677
+ def test_setitem_non_bool_into_bool(self, val, indexer_sli, unique):
678
+ # dont cast these 3-like values to bool
679
+ ser = Series([True, False])
680
+ if not unique:
681
+ ser.index = [1, 1]
682
+
683
+ with pytest.raises(TypeError, match="Invalid value"):
684
+ indexer_sli(ser)[1] = val
685
+
686
+ def test_setitem_boolean_array_into_npbool(self):
687
+ # GH#45462
688
+ ser = Series([True, False, True])
689
+ values = ser._values
690
+ arr = array([True, False, None])
691
+
692
+ ser[:2] = arr[:2] # no NAs -> can set inplace
693
+ assert ser._values is values
694
+
695
+ with pytest.raises(TypeError, match="Invalid value"):
696
+ ser[1:] = arr[1:] # has an NA -> cast to boolean dtype
697
+
698
+
699
+ class SetitemCastingEquivalents:
700
+ """
701
+ Check each of several methods that _should_ be equivalent to `obj[key] = val`
702
+
703
+ We assume that
704
+ - obj.index is the default Index(range(len(obj)))
705
+ - the setitem does not expand the obj
706
+ """
707
+
708
+ @pytest.fixture
709
+ def is_inplace(self, obj, expected):
710
+ """
711
+ Whether we expect the setting to be in-place or not.
712
+ """
713
+ return expected.dtype == obj.dtype
714
+
715
+ def check_indexer(self, obj, key, expected, val, indexer, is_inplace):
716
+ orig = obj
717
+ obj = obj.copy()
718
+ arr = obj._values
719
+
720
+ indexer(obj)[key] = val
721
+ tm.assert_series_equal(obj, expected)
722
+
723
+ self._check_inplace(is_inplace, orig, arr, obj)
724
+
725
+ def _check_inplace(self, is_inplace, orig, arr, obj):
726
+ if is_inplace is None:
727
+ # We are not (yet) checking whether setting is inplace or not
728
+ pass
729
+ elif is_inplace:
730
+ if arr.dtype.kind in ["m", "M"]:
731
+ # We may not have the same DTA/TDA, but will have the same
732
+ # underlying data
733
+ assert arr._ndarray is obj._values._ndarray
734
+ else:
735
+ assert obj._values is arr
736
+ else:
737
+ # otherwise original array should be unchanged
738
+ tm.assert_equal(arr, orig._values)
739
+
740
+ def test_int_key(self, obj, key, expected, raises, val, indexer_sli, is_inplace):
741
+ if not isinstance(key, int):
742
+ pytest.skip("Not relevant for int key")
743
+ if raises:
744
+ ctx = pytest.raises(TypeError, match="Invalid value")
745
+ else:
746
+ ctx = contextlib.nullcontext()
747
+
748
+ with ctx:
749
+ self.check_indexer(obj, key, expected, val, indexer_sli, is_inplace)
750
+
751
+ if indexer_sli is tm.loc:
752
+ with ctx:
753
+ self.check_indexer(obj, key, expected, val, tm.at, is_inplace)
754
+ elif indexer_sli is tm.iloc:
755
+ with ctx:
756
+ self.check_indexer(obj, key, expected, val, tm.iat, is_inplace)
757
+
758
+ rng = range(key, key + 1)
759
+ with ctx:
760
+ self.check_indexer(obj, rng, expected, val, indexer_sli, is_inplace)
761
+
762
+ if indexer_sli is not tm.loc:
763
+ # Note: no .loc because that handles slice edges differently
764
+ slc = slice(key, key + 1)
765
+ with ctx:
766
+ self.check_indexer(obj, slc, expected, val, indexer_sli, is_inplace)
767
+
768
+ ilkey = [key]
769
+ with ctx:
770
+ self.check_indexer(obj, ilkey, expected, val, indexer_sli, is_inplace)
771
+
772
+ indkey = np.array(ilkey)
773
+ with ctx:
774
+ self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
775
+
776
+ genkey = (x for x in [key])
777
+ with ctx:
778
+ self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
779
+
780
+ def test_slice_key(self, obj, key, expected, raises, val, indexer_sli, is_inplace):
781
+ if not isinstance(key, slice):
782
+ pytest.skip("Not relevant for slice key")
783
+ if raises:
784
+ ctx = pytest.raises(TypeError, match="Invalid value")
785
+ else:
786
+ ctx = contextlib.nullcontext()
787
+
788
+ if indexer_sli is not tm.loc:
789
+ # Note: no .loc because that handles slice edges differently
790
+ with ctx:
791
+ self.check_indexer(obj, key, expected, val, indexer_sli, is_inplace)
792
+
793
+ ilkey = list(range(len(obj)))[key]
794
+ with ctx:
795
+ self.check_indexer(obj, ilkey, expected, val, indexer_sli, is_inplace)
796
+
797
+ indkey = np.array(ilkey)
798
+ with ctx:
799
+ self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
800
+
801
+ genkey = (x for x in indkey)
802
+ with ctx:
803
+ self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
804
+
805
+ def test_mask_key(self, obj, key, expected, raises, val, indexer_sli):
806
+ # setitem with boolean mask
807
+ mask = np.zeros(obj.shape, dtype=bool)
808
+ mask[key] = True
809
+
810
+ obj = obj.copy()
811
+
812
+ if is_list_like(val) and len(val) < mask.sum():
813
+ msg = "boolean index did not match indexed array along dimension"
814
+ with pytest.raises(IndexError, match=msg):
815
+ indexer_sli(obj)[mask] = val
816
+ return
817
+
818
+ if raises:
819
+ with pytest.raises(TypeError, match="Invalid value"):
820
+ indexer_sli(obj)[mask] = val
821
+ else:
822
+ indexer_sli(obj)[mask] = val
823
+
824
+ def test_series_where(self, obj, key, expected, raises, val, is_inplace):
825
+ mask = np.zeros(obj.shape, dtype=bool)
826
+ mask[key] = True
827
+
828
+ if is_list_like(val) and len(val) < len(obj):
829
+ # Series.where is not valid here
830
+ msg = "operands could not be broadcast together with shapes"
831
+ with pytest.raises(ValueError, match=msg):
832
+ obj.where(~mask, val)
833
+ return
834
+
835
+ orig = obj
836
+ obj = obj.copy()
837
+ arr = obj._values
838
+
839
+ res = obj.where(~mask, val)
840
+
841
+ if val is NA and res.dtype == object:
842
+ expected = expected.fillna(NA)
843
+ elif val is None and res.dtype == object:
844
+ assert expected.dtype == object
845
+ expected = expected.copy()
846
+ expected[expected.isna()] = None
847
+ tm.assert_series_equal(res, expected)
848
+
849
+ self._check_inplace(is_inplace, orig, arr, obj)
850
+
851
+ def test_index_where(self, obj, key, expected, raises, val):
852
+ mask = np.zeros(obj.shape, dtype=bool)
853
+ mask[key] = True
854
+
855
+ res = Index(obj).where(~mask, val)
856
+ expected_idx = Index(expected, dtype=expected.dtype)
857
+ tm.assert_index_equal(res, expected_idx)
858
+
859
+ def test_index_putmask(self, obj, key, expected, raises, val):
860
+ mask = np.zeros(obj.shape, dtype=bool)
861
+ mask[key] = True
862
+
863
+ res = Index(obj).putmask(mask, val)
864
+ tm.assert_index_equal(res, Index(expected, dtype=expected.dtype))
865
+
866
+
867
+ @pytest.mark.parametrize(
868
+ "obj,expected,key,raises",
869
+ [
870
+ pytest.param(
871
+ # GH#45568 setting a valid NA value into IntervalDtype[int] should
872
+ # cast to IntervalDtype[float]
873
+ Series(interval_range(1, 5)),
874
+ Series(
875
+ [Interval(1, 2), np.nan, Interval(3, 4), Interval(4, 5)],
876
+ dtype="interval[float64]",
877
+ ),
878
+ 1,
879
+ True,
880
+ id="interval_int_na_value",
881
+ ),
882
+ pytest.param(
883
+ # these induce dtype changes
884
+ Series([2, 3, 4, 5, 6, 7, 8, 9, 10]),
885
+ Series([np.nan, 3, np.nan, 5, np.nan, 7, np.nan, 9, np.nan]),
886
+ slice(None, None, 2),
887
+ False,
888
+ id="int_series_slice_key_step",
889
+ ),
890
+ pytest.param(
891
+ Series([True, True, False, False]),
892
+ Series([np.nan, True, np.nan, False], dtype=object),
893
+ slice(None, None, 2),
894
+ True,
895
+ id="bool_series_slice_key_step",
896
+ ),
897
+ pytest.param(
898
+ # these induce dtype changes
899
+ Series(np.arange(10)),
900
+ Series([np.nan, np.nan, np.nan, np.nan, np.nan, 5, 6, 7, 8, 9]),
901
+ slice(None, 5),
902
+ False,
903
+ id="int_series_slice_key",
904
+ ),
905
+ pytest.param(
906
+ # changes dtype GH#4463
907
+ Series([1, 2, 3]),
908
+ Series([np.nan, 2, 3]),
909
+ 0,
910
+ False,
911
+ id="int_series_int_key",
912
+ ),
913
+ pytest.param(
914
+ # changes dtype GH#4463
915
+ Series([False]),
916
+ Series([np.nan], dtype=object),
917
+ # TODO: maybe go to float64 since we are changing the _whole_ Series?
918
+ 0,
919
+ True,
920
+ id="bool_series_int_key_change_all",
921
+ ),
922
+ pytest.param(
923
+ # changes dtype GH#4463
924
+ Series([False, True]),
925
+ Series([np.nan, True], dtype=object),
926
+ 0,
927
+ True,
928
+ id="bool_series_int_key",
929
+ ),
930
+ ],
931
+ )
932
+ class TestSetitemCastingEquivalents(SetitemCastingEquivalents):
933
+ @pytest.fixture(params=[np.nan, np.float64("NaN"), None, NA])
934
+ def val(self, request):
935
+ """
936
+ NA values that should generally be valid_na for *all* dtypes.
937
+
938
+ Include both python float NaN and np.float64; only np.float64 has a
939
+ `dtype` attribute.
940
+ """
941
+ return request.param
942
+
943
+
944
+ class TestSetitemTimedelta64IntoNumeric(SetitemCastingEquivalents):
945
+ # timedelta64 should not be treated as integers when setting into
946
+ # numeric Series
947
+
948
+ @pytest.fixture
949
+ def val(self):
950
+ td = np.timedelta64(4, "ns")
951
+ return td
952
+ # TODO: could also try np.full((1,), td)
953
+
954
+ @pytest.fixture(params=[complex, int, float])
955
+ def dtype(self, request):
956
+ return request.param
957
+
958
+ @pytest.fixture
959
+ def obj(self, dtype):
960
+ arr = np.arange(5).astype(dtype)
961
+ ser = Series(arr)
962
+ return ser
963
+
964
+ @pytest.fixture
965
+ def expected(self, dtype):
966
+ arr = np.arange(5).astype(dtype)
967
+ ser = Series(arr)
968
+ ser = ser.astype(object)
969
+ ser.iloc[0] = np.timedelta64(4, "ns")
970
+ return ser
971
+
972
+ @pytest.fixture
973
+ def key(self):
974
+ return 0
975
+
976
+ @pytest.fixture
977
+ def raises(self):
978
+ return True
979
+
980
+
981
+ class TestSetitemDT64IntoInt(SetitemCastingEquivalents):
982
+ # GH#39619 dont cast dt64 to int when doing this setitem
983
+
984
+ @pytest.fixture(params=["M8[ns]", "m8[ns]"])
985
+ def dtype(self, request):
986
+ return request.param
987
+
988
+ @pytest.fixture
989
+ def scalar(self, dtype):
990
+ val = np.datetime64("2021-01-18 13:25:00", "ns")
991
+ if dtype == "m8[ns]":
992
+ val = val - val
993
+ return val
994
+
995
+ @pytest.fixture
996
+ def expected(self, scalar):
997
+ expected = Series([scalar, scalar, 3], dtype=object)
998
+ assert isinstance(expected[0], type(scalar))
999
+ return expected
1000
+
1001
+ @pytest.fixture
1002
+ def obj(self):
1003
+ return Series([1, 2, 3])
1004
+
1005
+ @pytest.fixture
1006
+ def key(self):
1007
+ return slice(None, -1)
1008
+
1009
+ @pytest.fixture(params=[None, list, np.array])
1010
+ def val(self, scalar, request):
1011
+ box = request.param
1012
+ if box is None:
1013
+ return scalar
1014
+ return box([scalar, scalar])
1015
+
1016
+ @pytest.fixture
1017
+ def raises(self):
1018
+ return True
1019
+
1020
+
1021
+ class TestSetitemNAPeriodDtype(SetitemCastingEquivalents):
1022
+ # Setting compatible NA values into Series with PeriodDtype
1023
+
1024
+ @pytest.fixture
1025
+ def expected(self, key):
1026
+ exp = Series(period_range("2000-01-01", periods=10, freq="D"))
1027
+ exp._values.view("i8")[key] = NaT._value
1028
+ assert exp[key] is NaT or all(x is NaT for x in exp[key])
1029
+ return exp
1030
+
1031
+ @pytest.fixture
1032
+ def obj(self):
1033
+ return Series(period_range("2000-01-01", periods=10, freq="D"))
1034
+
1035
+ @pytest.fixture(params=[3, slice(3, 5)])
1036
+ def key(self, request):
1037
+ return request.param
1038
+
1039
+ @pytest.fixture(params=[None, np.nan])
1040
+ def val(self, request):
1041
+ return request.param
1042
+
1043
+ @pytest.fixture
1044
+ def raises(self):
1045
+ return False
1046
+
1047
+
1048
+ class TestSetitemNADatetimeLikeDtype(SetitemCastingEquivalents):
1049
+ # some nat-like values should be cast to datetime64/timedelta64 when
1050
+ # inserting into a datetime64/timedelta64 series. Others should coerce
1051
+ # to object and retain their dtypes.
1052
+ # GH#18586 for td64 and boolean mask case
1053
+
1054
+ @pytest.fixture(
1055
+ params=["m8[ns]", "M8[ns]", "datetime64[ns, UTC]", "datetime64[ns, US/Central]"]
1056
+ )
1057
+ def dtype(self, request):
1058
+ return request.param
1059
+
1060
+ @pytest.fixture
1061
+ def obj(self, dtype):
1062
+ i8vals = date_range("2016-01-01", periods=3).asi8
1063
+ idx = Index(i8vals, dtype=dtype)
1064
+ assert idx.dtype == dtype
1065
+ return Series(idx)
1066
+
1067
+ @pytest.fixture(
1068
+ params=[
1069
+ None,
1070
+ np.nan,
1071
+ NaT,
1072
+ np.timedelta64("NaT", "ns"),
1073
+ np.datetime64("NaT", "ns"),
1074
+ ]
1075
+ )
1076
+ def val(self, request):
1077
+ return request.param
1078
+
1079
+ @pytest.fixture
1080
+ def is_inplace(self, val, obj):
1081
+ # td64 -> cast to object iff val is datetime64("NaT")
1082
+ # dt64 -> cast to object iff val is timedelta64("NaT")
1083
+ # dt64tz -> cast to object with anything _but_ NaT
1084
+ return val is NaT or val is None or val is np.nan or obj.dtype == val.dtype
1085
+
1086
+ @pytest.fixture
1087
+ def expected(self, obj, val, is_inplace):
1088
+ dtype = obj.dtype if is_inplace else object
1089
+ expected = Series([val, *list(obj[1:])], dtype=dtype)
1090
+ return expected
1091
+
1092
+ @pytest.fixture
1093
+ def key(self):
1094
+ return 0
1095
+
1096
+ @pytest.fixture
1097
+ def raises(self, is_inplace):
1098
+ return False if is_inplace else True
1099
+
1100
+
1101
+ class TestSetitemMismatchedTZCastsToObject(SetitemCastingEquivalents):
1102
+ # GH#24024
1103
+ @pytest.fixture
1104
+ def obj(self):
1105
+ return Series(date_range("2000", periods=2, tz="US/Central"))
1106
+
1107
+ @pytest.fixture
1108
+ def val(self):
1109
+ return Timestamp("2000", tz="US/Eastern")
1110
+
1111
+ @pytest.fixture
1112
+ def key(self):
1113
+ return 0
1114
+
1115
+ @pytest.fixture
1116
+ def expected(self, obj, val):
1117
+ # pre-2.0 this would cast to object, in 2.0 we cast the val to
1118
+ # the target tz
1119
+ expected = Series(
1120
+ [
1121
+ val.tz_convert("US/Central"),
1122
+ Timestamp("2000-01-02 00:00:00-06:00", tz="US/Central"),
1123
+ ],
1124
+ dtype=obj.dtype,
1125
+ )
1126
+ return expected
1127
+
1128
+ @pytest.fixture
1129
+ def raises(self):
1130
+ return False
1131
+
1132
+
1133
+ @pytest.mark.parametrize(
1134
+ "obj,expected",
1135
+ [
1136
+ # For numeric series, we should coerce to NaN.
1137
+ (Series([1, 2, 3]), Series([np.nan, 2, 3])),
1138
+ (Series([1.0, 2.0, 3.0]), Series([np.nan, 2.0, 3.0])),
1139
+ # For datetime series, we should coerce to NaT.
1140
+ (
1141
+ Series([datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)]),
1142
+ Series([NaT, datetime(2000, 1, 2), datetime(2000, 1, 3)]),
1143
+ ),
1144
+ # For objects, we should preserve the None value.
1145
+ (Series(["foo", "bar", "baz"]), Series([None, "bar", "baz"])),
1146
+ ],
1147
+ )
1148
+ class TestSeriesNoneCoercion(SetitemCastingEquivalents):
1149
+ @pytest.fixture
1150
+ def key(self):
1151
+ return 0
1152
+
1153
+ @pytest.fixture
1154
+ def val(self):
1155
+ return None
1156
+
1157
+ @pytest.fixture
1158
+ def raises(self):
1159
+ return False
1160
+
1161
+
1162
+ class TestSetitemFloatIntervalWithIntIntervalValues(SetitemCastingEquivalents):
1163
+ # GH#44201 Cast to shared IntervalDtype rather than object
1164
+
1165
+ def test_setitem_example(self):
1166
+ # Just a case here to make obvious what this test class is aimed at
1167
+ idx = IntervalIndex.from_breaks(range(4))
1168
+ obj = Series(idx)
1169
+ val = Interval(0.5, 1.5)
1170
+
1171
+ with pytest.raises(TypeError, match="Invalid value"):
1172
+ obj[0] = val
1173
+
1174
+ @pytest.fixture
1175
+ def obj(self):
1176
+ """
1177
+ Fixture to create a Series [(0, 1], (1, 2], (2, 3]]
1178
+ """
1179
+ idx = IntervalIndex.from_breaks(range(4))
1180
+ return Series(idx)
1181
+
1182
+ @pytest.fixture
1183
+ def val(self):
1184
+ """
1185
+ Fixture to get an interval (0.5, 1.5]
1186
+ """
1187
+ return Interval(0.5, 1.5)
1188
+
1189
+ @pytest.fixture
1190
+ def key(self):
1191
+ """
1192
+ Fixture to get a key 0
1193
+ """
1194
+ return 0
1195
+
1196
+ @pytest.fixture
1197
+ def expected(self, obj, val):
1198
+ """
1199
+ Fixture to get a Series [(0.5, 1.5], (1.0, 2.0], (2.0, 3.0]]
1200
+ """
1201
+ data = [val, *list(obj[1:])]
1202
+ idx = IntervalIndex(data, dtype="Interval[float64]")
1203
+ return Series(idx)
1204
+
1205
+ @pytest.fixture
1206
+ def raises(self):
1207
+ """
1208
+ Fixture to enable raising pytest exceptions
1209
+ """
1210
+ return True
1211
+
1212
+
1213
+ class TestSetitemRangeIntoIntegerSeries(SetitemCastingEquivalents):
1214
+ # GH#44261 Setting a range with sufficiently-small integers into
1215
+ # small-itemsize integer dtypes should not need to upcast
1216
+
1217
+ @pytest.fixture
1218
+ def obj(self, any_int_numpy_dtype):
1219
+ dtype = np.dtype(any_int_numpy_dtype)
1220
+ ser = Series(range(5), dtype=dtype)
1221
+ return ser
1222
+
1223
+ @pytest.fixture
1224
+ def val(self):
1225
+ return range(2, 4)
1226
+
1227
+ @pytest.fixture
1228
+ def key(self):
1229
+ return slice(0, 2)
1230
+
1231
+ @pytest.fixture
1232
+ def expected(self, any_int_numpy_dtype):
1233
+ dtype = np.dtype(any_int_numpy_dtype)
1234
+ exp = Series([2, 3, 2, 3, 4], dtype=dtype)
1235
+ return exp
1236
+
1237
+ @pytest.fixture
1238
+ def raises(self):
1239
+ return False
1240
+
1241
+
1242
+ @pytest.mark.parametrize(
1243
+ "val, raises",
1244
+ [
1245
+ (np.array([2.0, 3.0]), False),
1246
+ (np.array([2.5, 3.5]), True),
1247
+ (
1248
+ np.array([2**65, 2**65 + 1], dtype=np.float64),
1249
+ True,
1250
+ ), # all ints, but can't cast
1251
+ ],
1252
+ )
1253
+ class TestSetitemFloatNDarrayIntoIntegerSeries(SetitemCastingEquivalents):
1254
+ @pytest.fixture
1255
+ def obj(self):
1256
+ return Series(range(5), dtype=np.int64)
1257
+
1258
+ @pytest.fixture
1259
+ def key(self):
1260
+ return slice(0, 2)
1261
+
1262
+ @pytest.fixture
1263
+ def expected(self, val):
1264
+ if val[0] == 2:
1265
+ # NB: this condition is based on currently-hardcoded "val" cases
1266
+ dtype = np.int64
1267
+ else:
1268
+ dtype = np.float64
1269
+ res_values = np.array(range(5), dtype=dtype)
1270
+ res_values[:2] = val
1271
+ return Series(res_values)
1272
+
1273
+
1274
+ @pytest.mark.parametrize("val", [512, np.int16(512)])
1275
+ class TestSetitemIntoIntegerSeriesNeedsUpcast(SetitemCastingEquivalents):
1276
+ @pytest.fixture
1277
+ def obj(self):
1278
+ return Series([1, 2, 3], dtype=np.int8)
1279
+
1280
+ @pytest.fixture
1281
+ def key(self):
1282
+ return 1
1283
+
1284
+ @pytest.fixture
1285
+ def expected(self):
1286
+ return Series([1, 512, 3], dtype=np.int16)
1287
+
1288
+ @pytest.fixture
1289
+ def raises(self):
1290
+ return True
1291
+
1292
+
1293
+ @pytest.mark.parametrize("val", [2**33 + 1.0, 2**33 + 1.1, 2**62])
1294
+ class TestSmallIntegerSetitemUpcast(SetitemCastingEquivalents):
1295
+ # https://github.com/pandas-dev/pandas/issues/39584#issuecomment-941212124
1296
+ @pytest.fixture
1297
+ def obj(self):
1298
+ return Series([1, 2, 3], dtype="i4")
1299
+
1300
+ @pytest.fixture
1301
+ def key(self):
1302
+ return 0
1303
+
1304
+ @pytest.fixture
1305
+ def expected(self, val):
1306
+ if val % 1 != 0:
1307
+ dtype = "f8"
1308
+ else:
1309
+ dtype = "i8"
1310
+ return Series([val, 2, 3], dtype=dtype)
1311
+
1312
+ @pytest.fixture
1313
+ def raises(self):
1314
+ return True
1315
+
1316
+
1317
+ class CoercionTest(SetitemCastingEquivalents):
1318
+ # Tests ported from tests.indexing.test_coercion
1319
+
1320
+ @pytest.fixture
1321
+ def key(self):
1322
+ return 1
1323
+
1324
+ @pytest.fixture
1325
+ def expected(self, obj, key, val, exp_dtype):
1326
+ vals = list(obj)
1327
+ vals[key] = val
1328
+ return Series(vals, dtype=exp_dtype)
1329
+
1330
+
1331
+ @pytest.mark.parametrize(
1332
+ "val,exp_dtype,raises",
1333
+ [(np.int32(1), np.int8, None), (np.int16(2**9), np.int16, True)],
1334
+ )
1335
+ class TestCoercionInt8(CoercionTest):
1336
+ # previously test_setitem_series_int8 in tests.indexing.test_coercion
1337
+ @pytest.fixture
1338
+ def obj(self):
1339
+ return Series([1, 2, 3, 4], dtype=np.int8)
1340
+
1341
+
1342
+ @pytest.mark.parametrize("val", [1, 1.1, 1 + 1j, True])
1343
+ @pytest.mark.parametrize("exp_dtype", [object])
1344
+ class TestCoercionObject(CoercionTest):
1345
+ # previously test_setitem_series_object in tests.indexing.test_coercion
1346
+ @pytest.fixture
1347
+ def obj(self):
1348
+ return Series(["a", "b", "c", "d"], dtype=object)
1349
+
1350
+ @pytest.fixture
1351
+ def raises(self):
1352
+ return False
1353
+
1354
+
1355
+ @pytest.mark.parametrize(
1356
+ "val,exp_dtype,raises",
1357
+ [
1358
+ (1, object, True),
1359
+ ("e", StringDtype(na_value=np.nan), False),
1360
+ ],
1361
+ )
1362
+ class TestCoercionString(CoercionTest):
1363
+ @pytest.fixture
1364
+ def obj(self):
1365
+ return Series(["a", "b", "c", "d"], dtype=StringDtype(na_value=np.nan))
1366
+
1367
+
1368
+ @pytest.mark.parametrize(
1369
+ "val,exp_dtype,raises",
1370
+ [
1371
+ (1, np.complex128, False),
1372
+ (1.1, np.complex128, False),
1373
+ (1 + 1j, np.complex128, False),
1374
+ (True, object, True),
1375
+ ],
1376
+ )
1377
+ class TestCoercionComplex(CoercionTest):
1378
+ # previously test_setitem_series_complex128 in tests.indexing.test_coercion
1379
+ @pytest.fixture
1380
+ def obj(self):
1381
+ return Series([1 + 1j, 2 + 2j, 3 + 3j, 4 + 4j])
1382
+
1383
+
1384
+ @pytest.mark.parametrize(
1385
+ "val,exp_dtype,raises",
1386
+ [
1387
+ (1, object, True),
1388
+ ("3", object, True),
1389
+ (3, object, True),
1390
+ (1.1, object, True),
1391
+ (1 + 1j, object, True),
1392
+ (True, bool, False),
1393
+ ],
1394
+ )
1395
+ class TestCoercionBool(CoercionTest):
1396
+ # previously test_setitem_series_bool in tests.indexing.test_coercion
1397
+ @pytest.fixture
1398
+ def obj(self):
1399
+ return Series([True, False, True, False], dtype=bool)
1400
+
1401
+
1402
+ @pytest.mark.parametrize(
1403
+ "val,exp_dtype,raises",
1404
+ [
1405
+ (1, np.int64, False),
1406
+ (1.1, np.float64, True),
1407
+ (1 + 1j, np.complex128, True),
1408
+ (True, object, True),
1409
+ ],
1410
+ )
1411
+ class TestCoercionInt64(CoercionTest):
1412
+ # previously test_setitem_series_int64 in tests.indexing.test_coercion
1413
+ @pytest.fixture
1414
+ def obj(self):
1415
+ return Series([1, 2, 3, 4])
1416
+
1417
+
1418
+ @pytest.mark.parametrize(
1419
+ "val,exp_dtype,raises",
1420
+ [
1421
+ (1, np.float64, False),
1422
+ (1.1, np.float64, False),
1423
+ (1 + 1j, np.complex128, True),
1424
+ (True, object, True),
1425
+ ],
1426
+ )
1427
+ class TestCoercionFloat64(CoercionTest):
1428
+ # previously test_setitem_series_float64 in tests.indexing.test_coercion
1429
+ @pytest.fixture
1430
+ def obj(self):
1431
+ return Series([1.1, 2.2, 3.3, 4.4])
1432
+
1433
+
1434
+ @pytest.mark.parametrize(
1435
+ "val,exp_dtype,raises",
1436
+ [
1437
+ (1, np.float32, False),
1438
+ pytest.param(
1439
+ 1.1,
1440
+ np.float32,
1441
+ False,
1442
+ marks=pytest.mark.xfail(
1443
+ not np_version_gt2,
1444
+ reason="np.float32(1.1) ends up as 1.100000023841858, so "
1445
+ "np_can_hold_element raises and we cast to float64",
1446
+ ),
1447
+ ),
1448
+ (1 + 1j, np.complex128, True),
1449
+ (True, object, True),
1450
+ (np.uint8(2), np.float32, False),
1451
+ (np.uint32(2), np.float32, False),
1452
+ # float32 cannot hold np.iinfo(np.uint32).max exactly
1453
+ # (closest it can hold is 4294967300.0 which off by 5.0), so
1454
+ # we cast to float64
1455
+ (np.uint32(np.iinfo(np.uint32).max), np.float64, True),
1456
+ (np.uint64(2), np.float32, False),
1457
+ (np.int64(2), np.float32, False),
1458
+ ],
1459
+ )
1460
+ class TestCoercionFloat32(CoercionTest):
1461
+ @pytest.fixture
1462
+ def obj(self):
1463
+ return Series([1.1, 2.2, 3.3, 4.4], dtype=np.float32)
1464
+
1465
+ def test_slice_key(self, obj, key, expected, raises, val, indexer_sli, is_inplace):
1466
+ super().test_slice_key(obj, key, expected, raises, val, indexer_sli, is_inplace)
1467
+
1468
+ if isinstance(val, float):
1469
+ # the xfail would xpass bc test_slice_key short-circuits
1470
+ raise AssertionError("xfail not relevant for this test.")
1471
+
1472
+
1473
+ @pytest.mark.parametrize(
1474
+ "exp_dtype",
1475
+ [
1476
+ "M8[ms]",
1477
+ "M8[ms, UTC]",
1478
+ "m8[ms]",
1479
+ ],
1480
+ )
1481
+ class TestCoercionDatetime64HigherReso(CoercionTest):
1482
+ @pytest.fixture
1483
+ def obj(self, exp_dtype):
1484
+ idx = date_range("2011-01-01", freq="D", periods=4, unit="s")
1485
+ if exp_dtype == "m8[ms]":
1486
+ idx = idx - Timestamp("1970-01-01").as_unit("s")
1487
+ assert idx.dtype == "m8[s]"
1488
+ elif exp_dtype == "M8[ms, UTC]":
1489
+ idx = idx.tz_localize("UTC")
1490
+ return Series(idx)
1491
+
1492
+ @pytest.fixture
1493
+ def val(self, exp_dtype):
1494
+ ts = Timestamp("2011-01-02 03:04:05.678").as_unit("ms")
1495
+ if exp_dtype == "m8[ms]":
1496
+ return ts - Timestamp("1970-01-01").as_unit("s")
1497
+ elif exp_dtype == "M8[ms, UTC]":
1498
+ return ts.tz_localize("UTC")
1499
+ return ts
1500
+
1501
+ @pytest.fixture
1502
+ def raises(self):
1503
+ return True
1504
+
1505
+
1506
+ @pytest.mark.parametrize(
1507
+ "val,exp_dtype,raises",
1508
+ [
1509
+ (Timestamp("2012-01-01"), "datetime64[ns]", False),
1510
+ (1, object, True),
1511
+ ("x", object, True),
1512
+ ],
1513
+ )
1514
+ class TestCoercionDatetime64(CoercionTest):
1515
+ # previously test_setitem_series_datetime64 in tests.indexing.test_coercion
1516
+
1517
+ @pytest.fixture
1518
+ def obj(self):
1519
+ return Series(date_range("2011-01-01", freq="D", periods=4, unit="ns"))
1520
+
1521
+ @pytest.fixture
1522
+ def raises(self):
1523
+ return False
1524
+
1525
+
1526
+ @pytest.mark.parametrize(
1527
+ "val,exp_dtype,raises",
1528
+ [
1529
+ (Timestamp("2012-01-01", tz="US/Eastern"), "datetime64[ns, US/Eastern]", False),
1530
+ # pre-2.0, a mis-matched tz would end up casting to object
1531
+ (Timestamp("2012-01-01", tz="US/Pacific"), "datetime64[ns, US/Eastern]", False),
1532
+ (Timestamp("2012-01-01"), object, True),
1533
+ (1, object, True),
1534
+ ],
1535
+ )
1536
+ class TestCoercionDatetime64TZ(CoercionTest):
1537
+ # previously test_setitem_series_datetime64tz in tests.indexing.test_coercion
1538
+ @pytest.fixture
1539
+ def obj(self):
1540
+ tz = "US/Eastern"
1541
+ return Series(date_range("2011-01-01", freq="D", periods=4, tz=tz, unit="ns"))
1542
+
1543
+ @pytest.fixture
1544
+ def raises(self):
1545
+ return False
1546
+
1547
+
1548
+ @pytest.mark.parametrize(
1549
+ "val,exp_dtype,raises",
1550
+ [
1551
+ (Timedelta("12 day"), "timedelta64[us]", False),
1552
+ (1, object, True),
1553
+ ("x", object, True),
1554
+ ],
1555
+ )
1556
+ class TestCoercionTimedelta64(CoercionTest):
1557
+ # previously test_setitem_series_timedelta64 in tests.indexing.test_coercion
1558
+ @pytest.fixture
1559
+ def obj(self):
1560
+ return Series(timedelta_range("1 day", periods=4))
1561
+
1562
+ @pytest.fixture
1563
+ def raises(self):
1564
+ return False
1565
+
1566
+
1567
+ @pytest.mark.parametrize(
1568
+ "val", ["foo", Period("2016", freq="Y"), Interval(1, 2, closed="both")]
1569
+ )
1570
+ @pytest.mark.parametrize("exp_dtype", [object])
1571
+ class TestPeriodIntervalCoercion(CoercionTest):
1572
+ # GH#45768
1573
+ @pytest.fixture(
1574
+ params=[
1575
+ period_range("2016-01-01", periods=3, freq="D"),
1576
+ interval_range(1, 5),
1577
+ ]
1578
+ )
1579
+ def obj(self, request):
1580
+ return Series(request.param)
1581
+
1582
+ @pytest.fixture
1583
+ def raises(self):
1584
+ return True
1585
+
1586
+
1587
+ def test_20643():
1588
+ # closed by GH#45121
1589
+ orig = Series([0, 1, 2], index=["a", "b", "c"])
1590
+
1591
+ ser = orig.copy()
1592
+ with pytest.raises(TypeError, match="Invalid value"):
1593
+ ser.at["b"] = 2.7
1594
+
1595
+ with pytest.raises(TypeError, match="Invalid value"):
1596
+ ser.loc["b"] = 2.7
1597
+
1598
+ with pytest.raises(TypeError, match="Invalid value"):
1599
+ ser["b"] = 2.7
1600
+
1601
+ ser = orig.copy()
1602
+ with pytest.raises(TypeError, match="Invalid value"):
1603
+ ser.iat[1] = 2.7
1604
+
1605
+ with pytest.raises(TypeError, match="Invalid value"):
1606
+ ser.iloc[1] = 2.7
1607
+
1608
+ orig_df = orig.to_frame("A")
1609
+
1610
+ df = orig_df.copy()
1611
+ with pytest.raises(TypeError, match="Invalid value"):
1612
+ df.at["b", "A"] = 2.7
1613
+
1614
+ with pytest.raises(TypeError, match="Invalid value"):
1615
+ df.loc["b", "A"] = 2.7
1616
+
1617
+ with pytest.raises(TypeError, match="Invalid value"):
1618
+ df.iloc[1, 0] = 2.7
1619
+
1620
+ with pytest.raises(TypeError, match="Invalid value"):
1621
+ df.iat[1, 0] = 2.7
1622
+
1623
+
1624
+ def test_20643_comment():
1625
+ # https://github.com/pandas-dev/pandas/issues/20643#issuecomment-431244590
1626
+ # fixed sometime prior to GH#45121
1627
+ orig = Series([0, 1, 2], index=["a", "b", "c"])
1628
+ expected = Series([np.nan, 1, 2], index=["a", "b", "c"])
1629
+
1630
+ ser = orig.copy()
1631
+ ser.iat[0] = None
1632
+ tm.assert_series_equal(ser, expected)
1633
+
1634
+ ser = orig.copy()
1635
+ ser.iloc[0] = None
1636
+ tm.assert_series_equal(ser, expected)
1637
+
1638
+
1639
+ def test_15413():
1640
+ # fixed by GH#45121
1641
+ ser = Series([1, 2, 3])
1642
+
1643
+ with pytest.raises(TypeError, match="Invalid value"):
1644
+ ser[ser == 2] += 0.5
1645
+
1646
+ with pytest.raises(TypeError, match="Invalid value"):
1647
+ ser[1] += 0.5
1648
+
1649
+ with pytest.raises(TypeError, match="Invalid value"):
1650
+ ser.loc[1] += 0.5
1651
+
1652
+ with pytest.raises(TypeError, match="Invalid value"):
1653
+ ser.iloc[1] += 0.5
1654
+
1655
+ with pytest.raises(TypeError, match="Invalid value"):
1656
+ ser.iat[1] += 0.5
1657
+
1658
+ with pytest.raises(TypeError, match="Invalid value"):
1659
+ ser.at[1] += 0.5
1660
+
1661
+
1662
+ def test_32878_int_itemsize():
1663
+ # Fixed by GH#45121
1664
+ arr = np.arange(5).astype("i4")
1665
+ ser = Series(arr)
1666
+ val = np.int64(np.iinfo(np.int64).max)
1667
+ with pytest.raises(TypeError, match="Invalid value"):
1668
+ ser[0] = val
1669
+
1670
+
1671
+ def test_32878_complex_itemsize():
1672
+ arr = np.arange(5).astype("c8")
1673
+ ser = Series(arr)
1674
+ val = np.finfo(np.float64).max
1675
+ val = val.astype("c16")
1676
+
1677
+ # GH#32878 used to coerce val to inf+0.000000e+00j
1678
+ with pytest.raises(TypeError, match="Invalid value"):
1679
+ ser[0] = val
1680
+
1681
+
1682
+ def test_37692(indexer_al):
1683
+ # GH#37692
1684
+ ser = Series([1, 2, 3], index=["a", "b", "c"])
1685
+ with pytest.raises(TypeError, match="Invalid value"):
1686
+ indexer_al(ser)["b"] = "test"
1687
+
1688
+
1689
+ def test_setitem_bool_int_float_consistency(indexer_sli):
1690
+ # GH#21513
1691
+ # bool-with-int and bool-with-float both upcast to object
1692
+ # int-with-float and float-with-int are both non-casting so long
1693
+ # as the setitem can be done losslessly
1694
+ for dtype in [np.float64, np.int64]:
1695
+ ser = Series(0, index=range(3), dtype=dtype)
1696
+ with pytest.raises(TypeError, match="Invalid value"):
1697
+ indexer_sli(ser)[0] = True
1698
+
1699
+ ser = Series(0, index=range(3), dtype=bool)
1700
+ with pytest.raises(TypeError, match="Invalid value"):
1701
+ ser[0] = dtype(1)
1702
+
1703
+ # 1.0 can be held losslessly, so no casting
1704
+ ser = Series(0, index=range(3), dtype=np.int64)
1705
+ indexer_sli(ser)[0] = np.float64(1.0)
1706
+ assert ser.dtype == np.int64
1707
+
1708
+ # 1 can be held losslessly, so no casting
1709
+ ser = Series(0, index=range(3), dtype=np.float64)
1710
+ indexer_sli(ser)[0] = np.int64(1)
1711
+
1712
+
1713
+ def test_setitem_positional_with_casting():
1714
+ # GH#45070 case where in __setitem__ we get a KeyError, then when
1715
+ # we fallback we *also* get a ValueError if we try to set inplace.
1716
+ # As of 3.0 we always treat int keys as labels, so this becomes
1717
+ # setitem-with-expansion
1718
+ ser = Series([1, 2, 3], index=["a", "b", "c"])
1719
+
1720
+ ser[0] = "X"
1721
+ expected = Series([1, 2, 3, "X"], index=["a", "b", "c", 0], dtype=object)
1722
+ tm.assert_series_equal(ser, expected)
1723
+
1724
+
1725
+ def test_setitem_positional_float_into_int_coerces():
1726
+ # Case where we hit a KeyError and then trying to set in-place incorrectly
1727
+ # casts a float to an int;
1728
+ # As of 3.0 we always treat int keys as labels, so this becomes
1729
+ # setitem-with-expansion
1730
+ ser = Series([1, 2, 3], index=["a", "b", "c"])
1731
+
1732
+ ser[0] = 1.5
1733
+ expected = Series([1, 2, 3, 1.5], index=["a", "b", "c", 0])
1734
+ tm.assert_series_equal(ser, expected)
1735
+
1736
+
1737
+ def test_setitem_int_not_positional():
1738
+ # GH#42215 deprecated falling back to positional on __setitem__ with an
1739
+ # int not contained in the index; enforced in 2.0
1740
+ ser = Series([1, 2, 3, 4], index=[1.1, 2.1, 3.0, 4.1])
1741
+ assert not ser.index._should_fallback_to_positional
1742
+ # assert not ser.index.astype(object)._should_fallback_to_positional
1743
+
1744
+ # 3.0 is in our index, so post-enforcement behavior is unchanged
1745
+ ser[3] = 10
1746
+ expected = Series([1, 2, 10, 4], index=ser.index)
1747
+ tm.assert_series_equal(ser, expected)
1748
+
1749
+ # pre-enforcement `ser[5] = 5` raised IndexError
1750
+ ser[5] = 5
1751
+ expected = Series([1, 2, 10, 4, 5], index=[1.1, 2.1, 3.0, 4.1, 5.0])
1752
+ tm.assert_series_equal(ser, expected)
1753
+
1754
+ ii = IntervalIndex.from_breaks(range(10))[::2]
1755
+ ser2 = Series(range(len(ii)), index=ii)
1756
+ exp_index = ii.astype(object).append(Index([4]))
1757
+ expected2 = Series([0, 1, 2, 3, 4, 9], index=exp_index)
1758
+ # pre-enforcement `ser2[4] = 9` interpreted 4 as positional
1759
+ ser2[4] = 9
1760
+ tm.assert_series_equal(ser2, expected2)
1761
+
1762
+ mi = MultiIndex.from_product([ser.index, ["A", "B"]])
1763
+ ser3 = Series(range(len(mi)), index=mi)
1764
+ expected3 = ser3.copy()
1765
+ expected3.loc[4] = 99
1766
+ # pre-enforcement `ser3[4] = 99` interpreted 4 as positional
1767
+ ser3[4] = 99
1768
+ tm.assert_series_equal(ser3, expected3)
1769
+
1770
+
1771
+ def test_setitem_with_bool_indexer():
1772
+ # GH#42530
1773
+
1774
+ df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
1775
+ result = df.pop("b").copy()
1776
+ result[[True, False, False]] = 9
1777
+ expected = Series(data=[9, 5, 6], name="b")
1778
+ tm.assert_series_equal(result, expected)
1779
+
1780
+ df.loc[[True, False, False], "a"] = 10
1781
+ expected = DataFrame({"a": [10, 2, 3]})
1782
+ tm.assert_frame_equal(df, expected)
1783
+
1784
+
1785
+ @pytest.mark.parametrize("size", range(2, 6))
1786
+ @pytest.mark.parametrize(
1787
+ "mask", [[True, False, False, False, False], [True, False], [False]]
1788
+ )
1789
+ @pytest.mark.parametrize(
1790
+ "item", [2.0, np.nan, np.finfo(float).max, np.finfo(float).min]
1791
+ )
1792
+ @pytest.mark.parametrize("box", [np.array, list, tuple])
1793
+ def test_setitem_bool_indexer_dont_broadcast_length1_values(size, mask, item, box):
1794
+ # GH#44265
1795
+ # see also tests.series.indexing.test_where.test_broadcast
1796
+
1797
+ selection = np.resize(mask, size)
1798
+
1799
+ data = np.arange(size, dtype=float)
1800
+
1801
+ ser = Series(data)
1802
+
1803
+ if selection.sum() != 1:
1804
+ msg = (
1805
+ "cannot set using a list-like indexer with a different "
1806
+ "length than the value"
1807
+ )
1808
+ with pytest.raises(ValueError, match=msg):
1809
+ # GH#44265
1810
+ ser[selection] = box([item])
1811
+ else:
1812
+ # In this corner case setting is equivalent to setting with the unboxed
1813
+ # item
1814
+ ser[selection] = box([item])
1815
+
1816
+ expected = Series(np.arange(size, dtype=float))
1817
+ expected[selection] = item
1818
+ tm.assert_series_equal(ser, expected)
1819
+
1820
+
1821
+ def test_setitem_empty_mask_dont_upcast_dt64():
1822
+ dti = date_range("2016-01-01", periods=3)
1823
+ ser = Series(dti)
1824
+ orig = ser.copy()
1825
+ mask = np.zeros(3, dtype=bool)
1826
+
1827
+ ser[mask] = "foo"
1828
+ assert ser.dtype == dti.dtype # no-op -> dont upcast
1829
+ tm.assert_series_equal(ser, orig)
1830
+
1831
+ ser.mask(mask, "foo", inplace=True)
1832
+ assert ser.dtype == dti.dtype # no-op -> dont upcast
1833
+ tm.assert_series_equal(ser, orig)
1834
+
1835
+
1836
+ def test_setitem_bool_dtype_with_boolean_indexer():
1837
+ # GH 57338
1838
+ s1 = Series([True, True, True], dtype=bool)
1839
+ s2 = Series([False, False, False], dtype=bool)
1840
+ condition = [False, True, False]
1841
+ s1[condition] = s2[condition]
1842
+ expected = Series([True, False, True], dtype=bool)
1843
+ tm.assert_series_equal(s1, expected)
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_take.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+
3
+ import pandas as pd
4
+ from pandas import Series
5
+ import pandas._testing as tm
6
+
7
+
8
+ def test_take_validate_axis():
9
+ # GH#51022
10
+ ser = Series([-1, 5, 6, 2, 4])
11
+
12
+ msg = "No axis named foo for object type Series"
13
+ with pytest.raises(ValueError, match=msg):
14
+ ser.take([1, 2], axis="foo")
15
+
16
+
17
+ def test_take():
18
+ ser = Series([-1, 5, 6, 2, 4])
19
+
20
+ actual = ser.take([1, 3, 4])
21
+ expected = Series([5, 2, 4], index=[1, 3, 4])
22
+ tm.assert_series_equal(actual, expected)
23
+
24
+ actual = ser.take([-1, 3, 4])
25
+ expected = Series([4, 2, 4], index=[4, 3, 4])
26
+ tm.assert_series_equal(actual, expected)
27
+
28
+ msg = "indices are out-of-bounds"
29
+ with pytest.raises(IndexError, match=msg):
30
+ ser.take([1, 10])
31
+ with pytest.raises(IndexError, match=msg):
32
+ ser.take([2, 5])
33
+
34
+
35
+ def test_take_categorical():
36
+ # https://github.com/pandas-dev/pandas/issues/20664
37
+ ser = Series(pd.Categorical(["a", "b", "c"]))
38
+ result = ser.take([-2, -2, 0])
39
+ expected = Series(
40
+ pd.Categorical(["b", "b", "a"], categories=["a", "b", "c"]), index=[1, 1, 0]
41
+ )
42
+ tm.assert_series_equal(result, expected)
43
+
44
+
45
+ def test_take_slice_raises():
46
+ ser = Series([-1, 5, 6, 2, 4])
47
+
48
+ msg = "Series.take requires a sequence of integers, not slice"
49
+ with pytest.raises(TypeError, match=msg):
50
+ ser.take(slice(0, 3, 1))
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_where.py ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas.core.dtypes.common import is_integer
5
+
6
+ import pandas as pd
7
+ from pandas import (
8
+ Series,
9
+ Timestamp,
10
+ date_range,
11
+ isna,
12
+ )
13
+ import pandas._testing as tm
14
+
15
+
16
+ def test_where_unsafe_int(any_signed_int_numpy_dtype):
17
+ s = Series(np.arange(10), dtype=any_signed_int_numpy_dtype)
18
+ mask = s < 5
19
+
20
+ s[mask] = range(2, 7)
21
+ expected = Series(
22
+ list(range(2, 7)) + list(range(5, 10)),
23
+ dtype=any_signed_int_numpy_dtype,
24
+ )
25
+
26
+ tm.assert_series_equal(s, expected)
27
+
28
+
29
+ def test_where_unsafe_float(float_numpy_dtype):
30
+ s = Series(np.arange(10), dtype=float_numpy_dtype)
31
+ mask = s < 5
32
+
33
+ s[mask] = range(2, 7)
34
+ data = list(range(2, 7)) + list(range(5, 10))
35
+ expected = Series(data, dtype=float_numpy_dtype)
36
+
37
+ tm.assert_series_equal(s, expected)
38
+
39
+
40
+ @pytest.mark.parametrize(
41
+ "dtype,expected_dtype",
42
+ [
43
+ (np.int8, np.float64),
44
+ (np.int16, np.float64),
45
+ (np.int32, np.float64),
46
+ (np.int64, np.float64),
47
+ (np.float32, np.float32),
48
+ (np.float64, np.float64),
49
+ ],
50
+ )
51
+ def test_where_unsafe_upcast(dtype, expected_dtype):
52
+ # see gh-9743
53
+ s = Series(np.arange(10), dtype=dtype)
54
+ values = [2.5, 3.5, 4.5, 5.5, 6.5]
55
+ mask = s < 5
56
+ if np.dtype(dtype).kind == np.dtype(expected_dtype).kind == "f":
57
+ s[mask] = values
58
+ expected = Series(values + list(range(5, 10)), dtype=expected_dtype)
59
+ tm.assert_series_equal(s, expected)
60
+ else:
61
+ with pytest.raises(TypeError, match="Invalid value"):
62
+ s[mask] = values
63
+
64
+
65
+ def test_where_unsafe():
66
+ # see gh-9731
67
+ s = Series(np.arange(10), dtype="int64")
68
+ values = [2.5, 3.5, 4.5, 5.5]
69
+
70
+ mask = s > 5
71
+ expected = Series(list(range(6)) + values, dtype="float64")
72
+
73
+ with pytest.raises(TypeError, match="Invalid value"):
74
+ s[mask] = values
75
+ s = s.astype("float64")
76
+ s[mask] = values
77
+
78
+ # see gh-3235
79
+ s = Series(np.arange(10), dtype="int64")
80
+ mask = s < 5
81
+ s[mask] = range(2, 7)
82
+ expected = Series(list(range(2, 7)) + list(range(5, 10)), dtype="int64")
83
+ tm.assert_series_equal(s, expected)
84
+ assert s.dtype == expected.dtype
85
+
86
+ s = Series(np.arange(10), dtype="int64")
87
+ mask = s > 5
88
+ s[mask] = [0] * 4
89
+ expected = Series([0, 1, 2, 3, 4, 5] + [0] * 4, dtype="int64")
90
+ tm.assert_series_equal(s, expected)
91
+
92
+ s = Series(np.arange(10))
93
+ mask = s > 5
94
+
95
+ msg = "cannot set using a list-like indexer with a different length than the value"
96
+ with pytest.raises(ValueError, match=msg):
97
+ s[mask] = [5, 4, 3, 2, 1]
98
+
99
+ with pytest.raises(ValueError, match=msg):
100
+ s[mask] = [0] * 5
101
+
102
+ # dtype changes
103
+ s = Series([1, 2, 3, 4])
104
+ result = s.where(s > 2, np.nan)
105
+ expected = Series([np.nan, np.nan, 3, 4])
106
+ tm.assert_series_equal(result, expected)
107
+
108
+ # GH 4667
109
+ # setting with None changes dtype
110
+ s = Series(range(10)).astype(float)
111
+ s[8] = None
112
+ result = s[8]
113
+ assert isna(result)
114
+
115
+ s = Series(range(10)).astype(float)
116
+ s[s > 8] = None
117
+ result = s[isna(s)]
118
+ expected = Series(np.nan, index=[9])
119
+ tm.assert_series_equal(result, expected)
120
+
121
+
122
+ def test_where():
123
+ s = Series(np.random.default_rng(2).standard_normal(5))
124
+ cond = s > 0
125
+
126
+ rs = s.where(cond).dropna()
127
+ rs2 = s[cond]
128
+ tm.assert_series_equal(rs, rs2)
129
+
130
+ rs = s.where(cond, -s)
131
+ tm.assert_series_equal(rs, s.abs())
132
+
133
+ rs = s.where(cond)
134
+ assert s.shape == rs.shape
135
+ assert rs is not s
136
+
137
+ # test alignment
138
+ cond = Series([True, False, False, True, False], index=s.index)
139
+ s2 = -(s.abs())
140
+
141
+ expected = s2[cond].reindex(s2.index[:3]).reindex(s2.index)
142
+ rs = s2.where(cond[:3])
143
+ tm.assert_series_equal(rs, expected)
144
+
145
+ expected = s2.abs()
146
+ expected.iloc[0] = s2[0]
147
+ rs = s2.where(cond[:3], -s2)
148
+ tm.assert_series_equal(rs, expected)
149
+
150
+
151
+ def test_where_error():
152
+ s = Series(np.random.default_rng(2).standard_normal(5))
153
+ cond = s > 0
154
+
155
+ msg = "Array conditional must be same shape as self"
156
+ with pytest.raises(ValueError, match=msg):
157
+ s.where(1)
158
+ with pytest.raises(ValueError, match=msg):
159
+ s.where(cond[:3].values, -s)
160
+
161
+ # GH 2745
162
+ s = Series([1, 2])
163
+ s[[True, False]] = [0, 1]
164
+ expected = Series([0, 2])
165
+ tm.assert_series_equal(s, expected)
166
+
167
+ # failures
168
+ msg = "cannot set using a list-like indexer with a different length than the value"
169
+ with pytest.raises(ValueError, match=msg):
170
+ s[[True, False]] = [0, 2, 3]
171
+
172
+ with pytest.raises(ValueError, match=msg):
173
+ s[[True, False]] = []
174
+
175
+
176
+ @pytest.mark.parametrize("klass", [list, tuple, np.array, Series])
177
+ def test_where_array_like(klass):
178
+ # see gh-15414
179
+ s = Series([1, 2, 3])
180
+ cond = [False, True, True]
181
+ expected = Series([np.nan, 2, 3])
182
+
183
+ result = s.where(klass(cond))
184
+ tm.assert_series_equal(result, expected)
185
+
186
+
187
+ @pytest.mark.parametrize(
188
+ "cond",
189
+ [
190
+ [1, 0, 1],
191
+ Series([2, 5, 7]),
192
+ ["True", "False", "True"],
193
+ [Timestamp("2017-01-01"), pd.NaT, Timestamp("2017-01-02")],
194
+ ],
195
+ )
196
+ def test_where_invalid_input(cond):
197
+ # see gh-15414: only boolean arrays accepted
198
+ s = Series([1, 2, 3])
199
+ msg = "Boolean array expected for the condition"
200
+
201
+ with pytest.raises(TypeError, match=msg):
202
+ s.where(cond)
203
+
204
+ msg = "Array conditional must be same shape as self"
205
+ with pytest.raises(ValueError, match=msg):
206
+ s.where([True])
207
+
208
+
209
+ def test_where_ndframe_align():
210
+ msg = "Array conditional must be same shape as self"
211
+ s = Series([1, 2, 3])
212
+
213
+ cond = [True]
214
+ with pytest.raises(ValueError, match=msg):
215
+ s.where(cond)
216
+
217
+ expected = Series([1, np.nan, np.nan])
218
+
219
+ out = s.where(Series(cond))
220
+ tm.assert_series_equal(out, expected)
221
+
222
+ cond = np.array([False, True, False, True])
223
+ with pytest.raises(ValueError, match=msg):
224
+ s.where(cond)
225
+
226
+ expected = Series([np.nan, 2, np.nan])
227
+
228
+ out = s.where(Series(cond))
229
+ tm.assert_series_equal(out, expected)
230
+
231
+
232
+ def test_where_setitem_invalid():
233
+ # GH 2702
234
+ # make sure correct exceptions are raised on invalid list assignment
235
+
236
+ msg = lambda x: (
237
+ f"cannot set using a {x} indexer with a different length than the value"
238
+ )
239
+ # slice
240
+ s = Series(list("abc"), dtype=object)
241
+
242
+ with pytest.raises(ValueError, match=msg("slice")):
243
+ s[0:3] = list(range(27))
244
+
245
+ s[0:3] = list(range(3))
246
+ expected = Series([0, 1, 2])
247
+ tm.assert_series_equal(s.astype(np.int64), expected)
248
+
249
+ # slice with step
250
+ s = Series(list("abcdef"), dtype=object)
251
+
252
+ with pytest.raises(ValueError, match=msg("slice")):
253
+ s[0:4:2] = list(range(27))
254
+
255
+ s = Series(list("abcdef"), dtype=object)
256
+ s[0:4:2] = list(range(2))
257
+ expected = Series([0, "b", 1, "d", "e", "f"])
258
+ tm.assert_series_equal(s, expected)
259
+
260
+ # neg slices
261
+ s = Series(list("abcdef"), dtype=object)
262
+
263
+ with pytest.raises(ValueError, match=msg("slice")):
264
+ s[:-1] = list(range(27))
265
+
266
+ s[-3:-1] = list(range(2))
267
+ expected = Series(["a", "b", "c", 0, 1, "f"])
268
+ tm.assert_series_equal(s, expected)
269
+
270
+ # list
271
+ s = Series(list("abc"), dtype=object)
272
+
273
+ with pytest.raises(ValueError, match=msg("list-like")):
274
+ s[[0, 1, 2]] = list(range(27))
275
+
276
+ s = Series(list("abc"), dtype=object)
277
+
278
+ with pytest.raises(ValueError, match=msg("list-like")):
279
+ s[[0, 1, 2]] = list(range(2))
280
+
281
+ # scalar
282
+ s = Series(list("abc"), dtype=object)
283
+ s[0] = list(range(10))
284
+ expected = Series([list(range(10)), "b", "c"])
285
+ tm.assert_series_equal(s, expected)
286
+
287
+
288
+ @pytest.mark.parametrize("size", range(2, 6))
289
+ @pytest.mark.parametrize(
290
+ "mask", [[True, False, False, False, False], [True, False], [False]]
291
+ )
292
+ @pytest.mark.parametrize(
293
+ "item", [2.0, np.nan, np.finfo(float).max, np.finfo(float).min]
294
+ )
295
+ @pytest.mark.parametrize("box", [np.array, list, tuple])
296
+ def test_broadcast(size, mask, item, box):
297
+ # GH#8801, GH#4195
298
+ selection = np.resize(mask, size)
299
+
300
+ data = np.arange(size, dtype=float)
301
+
302
+ # Construct the expected series by taking the source
303
+ # data or item based on the selection
304
+ expected = Series(
305
+ [item if use_item else data[i] for i, use_item in enumerate(selection)]
306
+ )
307
+
308
+ s = Series(data)
309
+
310
+ s[selection] = item
311
+ tm.assert_series_equal(s, expected)
312
+
313
+ s = Series(data)
314
+ result = s.where(~selection, box([item]))
315
+ tm.assert_series_equal(result, expected)
316
+
317
+ s = Series(data)
318
+ result = s.mask(selection, box([item]))
319
+ tm.assert_series_equal(result, expected)
320
+
321
+
322
+ def test_where_inplace():
323
+ s = Series(np.random.default_rng(2).standard_normal(5))
324
+ cond = s > 0
325
+
326
+ rs = s.copy()
327
+
328
+ result = rs.where(cond, inplace=True)
329
+ assert result is rs
330
+ tm.assert_series_equal(rs.dropna(), s[cond])
331
+ tm.assert_series_equal(rs, s.where(cond))
332
+
333
+ rs = s.copy()
334
+ result = rs.where(cond, -s, inplace=True)
335
+ assert result is rs
336
+ tm.assert_series_equal(rs, s.where(cond, -s))
337
+
338
+
339
+ def test_where_dups():
340
+ # GH 4550
341
+ # where crashes with dups in index
342
+ s1 = Series(list(range(3)))
343
+ s2 = Series(list(range(3)))
344
+ comb = pd.concat([s1, s2])
345
+ result = comb.where(comb < 2)
346
+ expected = Series([0, 1, np.nan, 0, 1, np.nan], index=[0, 1, 2, 0, 1, 2])
347
+ tm.assert_series_equal(result, expected)
348
+
349
+ # GH 4548
350
+ # inplace updating not working with dups
351
+ comb[comb < 1] = 5
352
+ expected = Series([5, 1, 2, 5, 1, 2], index=[0, 1, 2, 0, 1, 2])
353
+ tm.assert_series_equal(comb, expected)
354
+
355
+ comb[comb < 2] += 10
356
+ expected = Series([5, 11, 2, 5, 11, 2], index=[0, 1, 2, 0, 1, 2])
357
+ tm.assert_series_equal(comb, expected)
358
+
359
+
360
+ def test_where_numeric_with_string():
361
+ # GH 9280
362
+ s = Series([1, 2, 3])
363
+ w = s.where(s > 1, "X")
364
+
365
+ assert not is_integer(w[0])
366
+ assert is_integer(w[1])
367
+ assert is_integer(w[2])
368
+ assert isinstance(w[0], str)
369
+ assert w.dtype == "object"
370
+
371
+ w = s.where(s > 1, ["X", "Y", "Z"])
372
+ assert not is_integer(w[0])
373
+ assert is_integer(w[1])
374
+ assert is_integer(w[2])
375
+ assert isinstance(w[0], str)
376
+ assert w.dtype == "object"
377
+
378
+ w = s.where(s > 1, np.array(["X", "Y", "Z"]))
379
+ assert not is_integer(w[0])
380
+ assert is_integer(w[1])
381
+ assert is_integer(w[2])
382
+ assert isinstance(w[0], str)
383
+ assert w.dtype == "object"
384
+
385
+
386
+ def test_where_datetimetz():
387
+ # GH 15701
388
+ timestamps = ["2016-12-31 12:00:04+00:00", "2016-12-31 12:00:04.010000+00:00"]
389
+ ser = Series([Timestamp(t) for t in timestamps], dtype="datetime64[ns, UTC]")
390
+ rs = ser.where(Series([False, True]))
391
+ expected = Series([pd.NaT, ser[1]], dtype="datetime64[ns, UTC]")
392
+ tm.assert_series_equal(rs, expected)
393
+
394
+
395
+ def test_where_sparse():
396
+ # GH#17198 make sure we dont get an AttributeError for sp_index
397
+ ser = Series(pd.arrays.SparseArray([1, 2]))
398
+ result = ser.where(ser >= 2, 0)
399
+ expected = Series(pd.arrays.SparseArray([0, 2]))
400
+ tm.assert_series_equal(result, expected)
401
+
402
+
403
+ def test_where_empty_series_and_empty_cond_having_non_bool_dtypes():
404
+ # https://github.com/pandas-dev/pandas/issues/34592
405
+ ser = Series([], dtype=float)
406
+ result = ser.where([])
407
+ tm.assert_series_equal(result, ser)
408
+
409
+
410
+ def test_where_categorical(frame_or_series):
411
+ # https://github.com/pandas-dev/pandas/issues/18888
412
+ exp = frame_or_series(
413
+ pd.Categorical(["A", "A", "B", "B", np.nan], categories=["A", "B", "C"]),
414
+ dtype="category",
415
+ )
416
+ df = frame_or_series(["A", "A", "B", "B", "C"], dtype="category")
417
+ res = df.where(df != "C")
418
+ tm.assert_equal(exp, res)
419
+
420
+
421
+ def test_where_datetimelike_categorical(tz_naive_fixture):
422
+ # GH#37682
423
+ tz = tz_naive_fixture
424
+
425
+ dr = date_range("2001-01-01", periods=3, tz=tz)._with_freq(None)
426
+ lvals = pd.DatetimeIndex([dr[0], dr[1], pd.NaT])
427
+ rvals = pd.Categorical([dr[0], pd.NaT, dr[2]])
428
+
429
+ mask = np.array([True, True, False])
430
+
431
+ # DatetimeIndex.where
432
+ res = lvals.where(mask, rvals)
433
+ tm.assert_index_equal(res, dr)
434
+
435
+ # DatetimeArray.where
436
+ res = lvals._data._where(mask, rvals)
437
+ tm.assert_datetime_array_equal(res, dr._data)
438
+
439
+ # Series.where
440
+ res = Series(lvals).where(mask, rvals)
441
+ tm.assert_series_equal(res, Series(dr))
442
+
443
+ # DataFrame.where
444
+ res = pd.DataFrame(lvals).where(mask[:, None], pd.DataFrame(rvals))
445
+
446
+ tm.assert_frame_equal(res, pd.DataFrame(dr))
.venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_xs.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from pandas import (
5
+ MultiIndex,
6
+ Series,
7
+ date_range,
8
+ )
9
+ import pandas._testing as tm
10
+
11
+
12
+ def test_xs_datetimelike_wrapping():
13
+ # GH#31630 a case where we shouldn't wrap datetime64 in Timestamp
14
+ arr = date_range("2016-01-01", periods=3)._data._ndarray
15
+
16
+ ser = Series(arr, dtype=object)
17
+ for i in range(len(ser)):
18
+ ser.iloc[i] = arr[i]
19
+ assert ser.dtype == object
20
+ assert isinstance(ser[0], np.datetime64)
21
+
22
+ result = ser.xs(0)
23
+ assert isinstance(result, np.datetime64)
24
+
25
+
26
+ class TestXSWithMultiIndex:
27
+ def test_xs_level_series(self, multiindex_dataframe_random_data):
28
+ df = multiindex_dataframe_random_data
29
+ ser = df["A"]
30
+ expected = ser[:, "two"]
31
+ result = df.xs("two", level=1)["A"]
32
+ tm.assert_series_equal(result, expected)
33
+
34
+ def test_series_getitem_multiindex_xs_by_label(self):
35
+ # GH#5684
36
+ idx = MultiIndex.from_tuples(
37
+ [("a", "one"), ("a", "two"), ("b", "one"), ("b", "two")]
38
+ )
39
+ ser = Series([1, 2, 3, 4], index=idx)
40
+ return_value = ser.index.set_names(["L1", "L2"], inplace=True)
41
+ assert return_value is None
42
+ expected = Series([1, 3], index=["a", "b"])
43
+ return_value = expected.index.set_names(["L1"], inplace=True)
44
+ assert return_value is None
45
+
46
+ result = ser.xs("one", level="L2")
47
+ tm.assert_series_equal(result, expected)
48
+
49
+ def test_series_getitem_multiindex_xs(self):
50
+ # GH#6258
51
+ dt = list(date_range("20130903", periods=3))
52
+ idx = MultiIndex.from_product([list("AB"), dt])
53
+ ser = Series([1, 3, 4, 1, 3, 4], index=idx)
54
+ expected = Series([1, 1], index=list("AB"))
55
+
56
+ result = ser.xs("20130903", level=1)
57
+ tm.assert_series_equal(result, expected)
58
+
59
+ def test_series_xs_droplevel_false(self):
60
+ # GH: 19056
61
+ mi = MultiIndex.from_tuples(
62
+ [("a", "x"), ("a", "y"), ("b", "x")], names=["level1", "level2"]
63
+ )
64
+ ser = Series([1, 1, 1], index=mi)
65
+ result = ser.xs("a", axis=0, drop_level=False)
66
+ expected = Series(
67
+ [1, 1],
68
+ index=MultiIndex.from_tuples(
69
+ [("a", "x"), ("a", "y")], names=["level1", "level2"]
70
+ ),
71
+ )
72
+ tm.assert_series_equal(result, expected)
73
+
74
+ def test_xs_key_as_list(self):
75
+ # GH#41760
76
+ mi = MultiIndex.from_tuples([("a", "x")], names=["level1", "level2"])
77
+ ser = Series([1], index=mi)
78
+ with pytest.raises(TypeError, match="list keys are not supported"):
79
+ ser.xs(["a", "x"], axis=0, drop_level=False)
80
+
81
+ with pytest.raises(TypeError, match="list keys are not supported"):
82
+ ser.xs(["a"], axis=0, drop_level=False)
.venv/lib/python3.13/site-packages/pandas/tests/series/methods/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """
2
+ Test files dedicated to individual (stand-alone) Series methods
3
+
4
+ Ideally these files/tests should correspond 1-to-1 with tests.frame.methods
5
+
6
+ These may also present opportunities for sharing/de-duplicating test code.
7
+ """