1
1
"""
2
- Assertion helpers for offsets tests
2
+ Assertion helpers and base class for offsets tests
3
3
"""
4
+ from datetime import datetime
5
+ from typing import Optional , Type
6
+
7
+ from dateutil .tz .tz import tzlocal
8
+ import pytest
9
+
10
+ from pandas ._libs .tslibs import OutOfBoundsDatetime , Timestamp
11
+ from pandas ._libs .tslibs .offsets import (
12
+ FY5253 ,
13
+ BusinessHour ,
14
+ CustomBusinessHour ,
15
+ DateOffset ,
16
+ FY5253Quarter ,
17
+ LastWeekOfMonth ,
18
+ Week ,
19
+ WeekOfMonth ,
20
+ )
21
+ from pandas .compat import IS64
4
22
5
23
6
24
def assert_offset_equal (offset , base , expected ):
@@ -24,3 +42,156 @@ def assert_is_on_offset(offset, date, expected):
24
42
f"\n Expected: { expected } \n Actual: { actual } \n For Offset: { offset } )"
25
43
f"\n At Date: { date } "
26
44
)
45
+
46
+
47
+ class WeekDay :
48
+ MON = 0
49
+ TUE = 1
50
+ WED = 2
51
+ THU = 3
52
+ FRI = 4
53
+ SAT = 5
54
+ SUN = 6
55
+
56
+
57
+ class Base :
58
+ _offset : Optional [Type [DateOffset ]] = None
59
+ d = Timestamp (datetime (2008 , 1 , 2 ))
60
+
61
+ timezones = [
62
+ None ,
63
+ "UTC" ,
64
+ "Asia/Tokyo" ,
65
+ "US/Eastern" ,
66
+ "dateutil/Asia/Tokyo" ,
67
+ "dateutil/US/Pacific" ,
68
+ ]
69
+
70
+ def _get_offset (self , klass , value = 1 , normalize = False ):
71
+ # create instance from offset class
72
+ if klass is FY5253 :
73
+ klass = klass (
74
+ n = value ,
75
+ startingMonth = 1 ,
76
+ weekday = 1 ,
77
+ variation = "last" ,
78
+ normalize = normalize ,
79
+ )
80
+ elif klass is FY5253Quarter :
81
+ klass = klass (
82
+ n = value ,
83
+ startingMonth = 1 ,
84
+ weekday = 1 ,
85
+ qtr_with_extra_week = 1 ,
86
+ variation = "last" ,
87
+ normalize = normalize ,
88
+ )
89
+ elif klass is LastWeekOfMonth :
90
+ klass = klass (n = value , weekday = 5 , normalize = normalize )
91
+ elif klass is WeekOfMonth :
92
+ klass = klass (n = value , week = 1 , weekday = 5 , normalize = normalize )
93
+ elif klass is Week :
94
+ klass = klass (n = value , weekday = 5 , normalize = normalize )
95
+ elif klass is DateOffset :
96
+ klass = klass (days = value , normalize = normalize )
97
+ else :
98
+ klass = klass (value , normalize = normalize )
99
+ return klass
100
+
101
+ def test_apply_out_of_range (self , tz_naive_fixture ):
102
+ tz = tz_naive_fixture
103
+ if self ._offset is None :
104
+ return
105
+ if isinstance (tz , tzlocal ) and not IS64 :
106
+ pytest .xfail (reason = "OverflowError inside tzlocal past 2038" )
107
+
108
+ # try to create an out-of-bounds result timestamp; if we can't create
109
+ # the offset skip
110
+ try :
111
+ if self ._offset in (BusinessHour , CustomBusinessHour ):
112
+ # Using 10000 in BusinessHour fails in tz check because of DST
113
+ # difference
114
+ offset = self ._get_offset (self ._offset , value = 100000 )
115
+ else :
116
+ offset = self ._get_offset (self ._offset , value = 10000 )
117
+
118
+ result = Timestamp ("20080101" ) + offset
119
+ assert isinstance (result , datetime )
120
+ assert result .tzinfo is None
121
+
122
+ # Check tz is preserved
123
+ t = Timestamp ("20080101" , tz = tz )
124
+ result = t + offset
125
+ assert isinstance (result , datetime )
126
+ assert t .tzinfo == result .tzinfo
127
+
128
+ except OutOfBoundsDatetime :
129
+ pass
130
+ except (ValueError , KeyError ):
131
+ # we are creating an invalid offset
132
+ # so ignore
133
+ pass
134
+
135
+ def test_offsets_compare_equal (self ):
136
+ # root cause of GH#456: __ne__ was not implemented
137
+ if self ._offset is None :
138
+ return
139
+ offset1 = self ._offset ()
140
+ offset2 = self ._offset ()
141
+ assert not offset1 != offset2
142
+ assert offset1 == offset2
143
+
144
+ def test_rsub (self ):
145
+ if self ._offset is None or not hasattr (self , "offset2" ):
146
+ # i.e. skip for TestCommon and YQM subclasses that do not have
147
+ # offset2 attr
148
+ return
149
+ assert self .d - self .offset2 == (- self .offset2 ).apply (self .d )
150
+
151
+ def test_radd (self ):
152
+ if self ._offset is None or not hasattr (self , "offset2" ):
153
+ # i.e. skip for TestCommon and YQM subclasses that do not have
154
+ # offset2 attr
155
+ return
156
+ assert self .d + self .offset2 == self .offset2 + self .d
157
+
158
+ def test_sub (self ):
159
+ if self ._offset is None or not hasattr (self , "offset2" ):
160
+ # i.e. skip for TestCommon and YQM subclasses that do not have
161
+ # offset2 attr
162
+ return
163
+ off = self .offset2
164
+ msg = "Cannot subtract datetime from offset"
165
+ with pytest .raises (TypeError , match = msg ):
166
+ off - self .d
167
+
168
+ assert 2 * off - off == off
169
+ assert self .d - self .offset2 == self .d + self ._offset (- 2 )
170
+ assert self .d - self .offset2 == self .d - (2 * off - off )
171
+
172
+ def testMult1 (self ):
173
+ if self ._offset is None or not hasattr (self , "offset1" ):
174
+ # i.e. skip for TestCommon and YQM subclasses that do not have
175
+ # offset1 attr
176
+ return
177
+ assert self .d + 10 * self .offset1 == self .d + self ._offset (10 )
178
+ assert self .d + 5 * self .offset1 == self .d + self ._offset (5 )
179
+
180
+ def testMult2 (self ):
181
+ if self ._offset is None :
182
+ return
183
+ assert self .d + (- 5 * self ._offset (- 10 )) == self .d + self ._offset (50 )
184
+ assert self .d + (- 3 * self ._offset (- 2 )) == self .d + self ._offset (6 )
185
+
186
+ def test_compare_str (self ):
187
+ # GH#23524
188
+ # comparing to strings that cannot be cast to DateOffsets should
189
+ # not raise for __eq__ or __ne__
190
+ if self ._offset is None :
191
+ return
192
+ off = self ._get_offset (self ._offset )
193
+
194
+ assert not off == "infer"
195
+ assert off != "foo"
196
+ # Note: inequalities are only implemented for Tick subclasses;
197
+ # tests for this are in test_ticks
0 commit comments