11"""
2- Assertion helpers for offsets tests
2+ Assertion helpers and base class for offsets tests
33"""
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
422
523
624def assert_offset_equal (offset , base , expected ):
@@ -24,3 +42,156 @@ def assert_is_on_offset(offset, date, expected):
2442 f"\n Expected: { expected } \n Actual: { actual } \n For Offset: { offset } )"
2543 f"\n At Date: { date } "
2644 )
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