22"""
33Tests for Timestamp timezone-related methods
44"""
5+ from datetime import date , timedelta
56
7+ from distutils .version import LooseVersion
68import pytest
9+ import pytz
710from pytz .exceptions import AmbiguousTimeError , NonExistentTimeError
11+ import dateutil
12+ from dateutil .tz import gettz , tzoffset
813
914import pandas .util .testing as tm
15+ import pandas .util ._test_decorators as td
16+
1017from pandas import Timestamp , NaT
1118
1219
1320class TestTimestampTZOperations (object ):
1421 # --------------------------------------------------------------
1522 # Timestamp.tz_localize
1623
24+ def test_tz_localize_ambiguous_bool (self ):
25+ # make sure that we are correctly accepting bool values as ambiguous
26+ # GH#14402
27+ ts = Timestamp ('2015-11-01 01:00:03' )
28+ expected0 = Timestamp ('2015-11-01 01:00:03-0500' , tz = 'US/Central' )
29+ expected1 = Timestamp ('2015-11-01 01:00:03-0600' , tz = 'US/Central' )
30+
31+ with pytest .raises (pytz .AmbiguousTimeError ):
32+ ts .tz_localize ('US/Central' )
33+
34+ result = ts .tz_localize ('US/Central' , ambiguous = True )
35+ assert result == expected0
36+
37+ result = ts .tz_localize ('US/Central' , ambiguous = False )
38+ assert result == expected1
39+
1740 def test_tz_localize_ambiguous (self ):
1841 ts = Timestamp ('2014-11-02 01:00' )
1942 ts_dst = ts .tz_localize ('US/Eastern' , ambiguous = True )
@@ -70,6 +93,55 @@ def test_tz_localize_roundtrip(self, stamp, tz):
7093 assert reset == ts
7194 assert reset .tzinfo is None
7295
96+ def test_tz_localize_ambiguous_compat (self ):
97+ # validate that pytz and dateutil are compat for dst
98+ # when the transition happens
99+ naive = Timestamp ('2013-10-27 01:00:00' )
100+
101+ pytz_zone = 'Europe/London'
102+ dateutil_zone = 'dateutil/Europe/London'
103+ result_pytz = naive .tz_localize (pytz_zone , ambiguous = 0 )
104+ result_dateutil = naive .tz_localize (dateutil_zone , ambiguous = 0 )
105+ assert result_pytz .value == result_dateutil .value
106+ assert result_pytz .value == 1382835600000000000
107+
108+ if LooseVersion (dateutil .__version__ ) < LooseVersion ('2.6.0' ):
109+ # dateutil 2.6 buggy w.r.t. ambiguous=0
110+ # see gh-14621
111+ # see https://github.com/dateutil/dateutil/issues/321
112+ assert (result_pytz .to_pydatetime ().tzname () ==
113+ result_dateutil .to_pydatetime ().tzname ())
114+ assert str (result_pytz ) == str (result_dateutil )
115+ elif LooseVersion (dateutil .__version__ ) > LooseVersion ('2.6.0' ):
116+ # fixed ambiguous behavior
117+ assert result_pytz .to_pydatetime ().tzname () == 'GMT'
118+ assert result_dateutil .to_pydatetime ().tzname () == 'BST'
119+ assert str (result_pytz ) != str (result_dateutil )
120+
121+ # 1 hour difference
122+ result_pytz = naive .tz_localize (pytz_zone , ambiguous = 1 )
123+ result_dateutil = naive .tz_localize (dateutil_zone , ambiguous = 1 )
124+ assert result_pytz .value == result_dateutil .value
125+ assert result_pytz .value == 1382832000000000000
126+
127+ # dateutil < 2.6 is buggy w.r.t. ambiguous timezones
128+ if LooseVersion (dateutil .__version__ ) > LooseVersion ('2.5.3' ):
129+ # see gh-14621
130+ assert str (result_pytz ) == str (result_dateutil )
131+ assert (result_pytz .to_pydatetime ().tzname () ==
132+ result_dateutil .to_pydatetime ().tzname ())
133+
134+ @pytest .mark .parametrize ('tz' , [pytz .timezone ('US/Eastern' ),
135+ gettz ('US/Eastern' ),
136+ 'US/Eastern' , 'dateutil/US/Eastern' ])
137+ def test_timestamp_tz_localize (self , tz ):
138+ stamp = Timestamp ('3/11/2012 04:00' )
139+
140+ result = stamp .tz_localize (tz )
141+ expected = Timestamp ('3/11/2012 04:00' , tz = tz )
142+ assert result .hour == expected .hour
143+ assert result == expected
144+
73145 # ------------------------------------------------------------------
74146 # Timestamp.tz_convert
75147
@@ -85,3 +157,120 @@ def test_tz_convert_roundtrip(self, stamp, tz):
85157 assert reset == Timestamp (stamp )
86158 assert reset .tzinfo is None
87159 assert reset == converted .tz_convert ('UTC' ).tz_localize (None )
160+
161+ @pytest .mark .parametrize ('tzstr' , ['US/Eastern' , 'dateutil/US/Eastern' ])
162+ def test_astimezone (self , tzstr ):
163+ # astimezone is an alias for tz_convert, so keep it with
164+ # the tz_convert tests
165+ utcdate = Timestamp ('3/11/2012 22:00' , tz = 'UTC' )
166+ expected = utcdate .tz_convert (tzstr )
167+ result = utcdate .astimezone (tzstr )
168+ assert expected == result
169+ assert isinstance (result , Timestamp )
170+
171+ @td .skip_if_windows
172+ def test_tz_convert_utc_with_system_utc (self ):
173+ from pandas ._libs .tslibs .timezones import maybe_get_tz
174+
175+ # from system utc to real utc
176+ ts = Timestamp ('2001-01-05 11:56' , tz = maybe_get_tz ('dateutil/UTC' ))
177+ # check that the time hasn't changed.
178+ assert ts == ts .tz_convert (dateutil .tz .tzutc ())
179+
180+ # from system utc to real utc
181+ ts = Timestamp ('2001-01-05 11:56' , tz = maybe_get_tz ('dateutil/UTC' ))
182+ # check that the time hasn't changed.
183+ assert ts == ts .tz_convert (dateutil .tz .tzutc ())
184+
185+ # ------------------------------------------------------------------
186+ # Timestamp.__init__ with tz str or tzinfo
187+
188+ def test_timestamp_constructor_tz_utc (self ):
189+ utc_stamp = Timestamp ('3/11/2012 05:00' , tz = 'utc' )
190+ assert utc_stamp .tzinfo is pytz .utc
191+ assert utc_stamp .hour == 5
192+
193+ utc_stamp = Timestamp ('3/11/2012 05:00' ).tz_localize ('utc' )
194+ assert utc_stamp .hour == 5
195+
196+ def test_timestamp_to_datetime_tzoffset (self ):
197+ tzinfo = tzoffset (None , 7200 )
198+ expected = Timestamp ('3/11/2012 04:00' , tz = tzinfo )
199+ result = Timestamp (expected .to_pydatetime ())
200+ assert expected == result
201+
202+ def test_timestamp_constructor_near_dst_boundary (self ):
203+ # GH#11481 & GH#15777
204+ # Naive string timestamps were being localized incorrectly
205+ # with tz_convert_single instead of tz_localize_to_utc
206+
207+ for tz in ['Europe/Brussels' , 'Europe/Prague' ]:
208+ result = Timestamp ('2015-10-25 01:00' , tz = tz )
209+ expected = Timestamp ('2015-10-25 01:00' ).tz_localize (tz )
210+ assert result == expected
211+
212+ with pytest .raises (pytz .AmbiguousTimeError ):
213+ Timestamp ('2015-10-25 02:00' , tz = tz )
214+
215+ result = Timestamp ('2017-03-26 01:00' , tz = 'Europe/Paris' )
216+ expected = Timestamp ('2017-03-26 01:00' ).tz_localize ('Europe/Paris' )
217+ assert result == expected
218+
219+ with pytest .raises (pytz .NonExistentTimeError ):
220+ Timestamp ('2017-03-26 02:00' , tz = 'Europe/Paris' )
221+
222+ # GH#11708
223+ naive = Timestamp ('2015-11-18 10:00:00' )
224+ result = naive .tz_localize ('UTC' ).tz_convert ('Asia/Kolkata' )
225+ expected = Timestamp ('2015-11-18 15:30:00+0530' , tz = 'Asia/Kolkata' )
226+ assert result == expected
227+
228+ # GH#15823
229+ result = Timestamp ('2017-03-26 00:00' , tz = 'Europe/Paris' )
230+ expected = Timestamp ('2017-03-26 00:00:00+0100' , tz = 'Europe/Paris' )
231+ assert result == expected
232+
233+ result = Timestamp ('2017-03-26 01:00' , tz = 'Europe/Paris' )
234+ expected = Timestamp ('2017-03-26 01:00:00+0100' , tz = 'Europe/Paris' )
235+ assert result == expected
236+
237+ with pytest .raises (pytz .NonExistentTimeError ):
238+ Timestamp ('2017-03-26 02:00' , tz = 'Europe/Paris' )
239+
240+ result = Timestamp ('2017-03-26 02:00:00+0100' , tz = 'Europe/Paris' )
241+ naive = Timestamp (result .value )
242+ expected = naive .tz_localize ('UTC' ).tz_convert ('Europe/Paris' )
243+ assert result == expected
244+
245+ result = Timestamp ('2017-03-26 03:00' , tz = 'Europe/Paris' )
246+ expected = Timestamp ('2017-03-26 03:00:00+0200' , tz = 'Europe/Paris' )
247+ assert result == expected
248+
249+ @pytest .mark .parametrize ('tz' , [pytz .timezone ('US/Eastern' ),
250+ gettz ('US/Eastern' ),
251+ 'US/Eastern' , 'dateutil/US/Eastern' ])
252+ def test_timestamp_constructed_by_date_and_tz (self , tz ):
253+ # GH#2993, Timestamp cannot be constructed by datetime.date
254+ # and tz correctly
255+
256+ result = Timestamp (date (2012 , 3 , 11 ), tz = tz )
257+
258+ expected = Timestamp ('3/11/2012' , tz = tz )
259+ assert result .hour == expected .hour
260+ assert result == expected
261+
262+ @pytest .mark .parametrize ('tz' , [pytz .timezone ('US/Eastern' ),
263+ gettz ('US/Eastern' ),
264+ 'US/Eastern' , 'dateutil/US/Eastern' ])
265+ def test_timestamp_add_timedelta_push_over_dst_boundary (self , tz ):
266+ # GH#1389
267+
268+ # 4 hours before DST transition
269+ stamp = Timestamp ('3/10/2012 22:00' , tz = tz )
270+
271+ result = stamp + timedelta (hours = 6 )
272+
273+ # spring forward, + "7" hours
274+ expected = Timestamp ('3/11/2012 05:00' , tz = tz )
275+
276+ assert result == expected
0 commit comments