From bb5731dd4282cdf0af4909de0fa849951e0adb88 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 19:22:52 +0100 Subject: [PATCH 01/14] fix bug --- pandas/_libs/tslibs/timestamps.pyx | 6 +++--- pandas/tests/tslibs/test_conversion.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 25b0b4069cf7c..2007df98b2d41 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1187,12 +1187,12 @@ class Timestamp(_Timestamp): value = tz_localize_to_utc(np.array([self.value], dtype='i8'), tz, ambiguous=ambiguous, nonexistent=nonexistent)[0] - return Timestamp(value, tz=tz) + return Timestamp(value, tz=self.tz) else: if tz is None: # reset tz value = tz_convert_single(self.value, UTC, self.tz) - return Timestamp(value, tz=None) + return Timestamp(value, tz=self.tz) else: raise TypeError('Cannot localize tz-aware Timestamp, use ' 'tz_convert for conversions') @@ -1222,7 +1222,7 @@ class Timestamp(_Timestamp): 'tz_localize to localize') else: # Same UTC timestamp, different time zone - return Timestamp(self.value, tz=tz) + return Timestamp(self.value, tz=self.tz) astimezone = tz_convert diff --git a/pandas/tests/tslibs/test_conversion.py b/pandas/tests/tslibs/test_conversion.py index 13398a69b4982..e6b6866578910 100644 --- a/pandas/tests/tslibs/test_conversion.py +++ b/pandas/tests/tslibs/test_conversion.py @@ -66,3 +66,10 @@ def test_length_zero_copy(dtype, copy): arr = np.array([], dtype=dtype) result = conversion.ensure_datetime64ns(arr, copy=copy) assert result.base is (None if copy else arr) + + +def test_tz_convert_freq(): + import pandas as pd + import pytz + t1 = pd.Timestamp('2019-01-01 10:00', freq='H') + assert t1.tz_localize(pytz.utc).freq == t1.freq From 8910104859e1cc23e348a845ecede6b5c8f62cc7 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 19:54:23 +0100 Subject: [PATCH 02/14] add frq --- pandas/_libs/tslibs/timestamps.pyx | 6 +++--- pandas/tests/tslibs/test_conversion.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 2007df98b2d41..3b8ced90eab1e 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1187,12 +1187,12 @@ class Timestamp(_Timestamp): value = tz_localize_to_utc(np.array([self.value], dtype='i8'), tz, ambiguous=ambiguous, nonexistent=nonexistent)[0] - return Timestamp(value, tz=self.tz) + return Timestamp(value, tz=tz, freq=self.freq) else: if tz is None: # reset tz value = tz_convert_single(self.value, UTC, self.tz) - return Timestamp(value, tz=self.tz) + return Timestamp(value, tz=None, freq=self.freq) else: raise TypeError('Cannot localize tz-aware Timestamp, use ' 'tz_convert for conversions') @@ -1222,7 +1222,7 @@ class Timestamp(_Timestamp): 'tz_localize to localize') else: # Same UTC timestamp, different time zone - return Timestamp(self.value, tz=self.tz) + return Timestamp(self.value, tz=tz, freq=self.freq) astimezone = tz_convert diff --git a/pandas/tests/tslibs/test_conversion.py b/pandas/tests/tslibs/test_conversion.py index e6b6866578910..e3ea838ba6822 100644 --- a/pandas/tests/tslibs/test_conversion.py +++ b/pandas/tests/tslibs/test_conversion.py @@ -73,3 +73,5 @@ def test_tz_convert_freq(): import pytz t1 = pd.Timestamp('2019-01-01 10:00', freq='H') assert t1.tz_localize(pytz.utc).freq == t1.freq + t2 = pd.Timestamp('2019-01-02 12:00', tz=pytz.utc, freq='T') + assert t2.tz_convert(pytz.utc).freq == t2.freq From bdd8b2e4fb60daca990b7e4e5ad3bd2bf7d96abf Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 20:24:53 +0100 Subject: [PATCH 03/14] experiment --- pandas/_libs/tslibs/timestamps.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 3b8ced90eab1e..8a95d2494dfa4 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1192,7 +1192,7 @@ class Timestamp(_Timestamp): if tz is None: # reset tz value = tz_convert_single(self.value, UTC, self.tz) - return Timestamp(value, tz=None, freq=self.freq) + return Timestamp(value, tz=tz, freq=self.freq) else: raise TypeError('Cannot localize tz-aware Timestamp, use ' 'tz_convert for conversions') From 64b1a880df94fd4e5562df4592340f56400e8850 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 21:51:42 +0100 Subject: [PATCH 04/14] code change based on review --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/tests/scalar/timestamp/test_timestamp.py | 7 +++++++ pandas/tests/tslibs/test_conversion.py | 9 --------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 4032dc20b2e19..74418929f7a96 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -93,7 +93,7 @@ Timezones ^^^^^^^^^ - Bug in :func:`to_datetime` with ``utc=True`` and datetime strings that would apply previously parsed UTC offsets to subsequent arguments (:issue:`24992`) -- +- Bug in :func:`tz_localize` and :func:`tz_convert` does not propagate freq if ``Timestamp`` is initiated with freq (:issue:`25241`) - Numeric diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index b2c05d1564a48..1de708d72b3be 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -780,6 +780,13 @@ def test_hash_equivalent(self): stamp = Timestamp(datetime(2011, 1, 1)) assert d[stamp] == 5 + def test_tz_convert_freq(self, tz_naive_fixture): + # GH25241 + t1 = Timestamp('2019-01-01 10:00', freq='H') + assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq + t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') + assert t2.tz_convert(tz='UTC') == t2 + class TestTimestampNsOperations(object): diff --git a/pandas/tests/tslibs/test_conversion.py b/pandas/tests/tslibs/test_conversion.py index e3ea838ba6822..13398a69b4982 100644 --- a/pandas/tests/tslibs/test_conversion.py +++ b/pandas/tests/tslibs/test_conversion.py @@ -66,12 +66,3 @@ def test_length_zero_copy(dtype, copy): arr = np.array([], dtype=dtype) result = conversion.ensure_datetime64ns(arr, copy=copy) assert result.base is (None if copy else arr) - - -def test_tz_convert_freq(): - import pandas as pd - import pytz - t1 = pd.Timestamp('2019-01-01 10:00', freq='H') - assert t1.tz_localize(pytz.utc).freq == t1.freq - t2 = pd.Timestamp('2019-01-02 12:00', tz=pytz.utc, freq='T') - assert t2.tz_convert(pytz.utc).freq == t2.freq From 9fc0b0a71f6b0209b3d810d09a29a455452fb7e0 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 21:59:04 +0100 Subject: [PATCH 05/14] correct pytest msg --- pandas/tests/series/indexing/test_indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index a5855f68127f4..8bc8c5bc29340 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -114,7 +114,7 @@ def test_getitem_get(test_data): # missing d = test_data.ts.index[0] - BDay() - with pytest.raises(KeyError, match=r"Timestamp\('1999-12-31 00:00:00'\)"): + with pytest.raises(KeyError, match=r"Timestamp\('1999-12-31 00:00:00', freq='B'\)"): test_data.ts[d] # None From c65a35c393760543362db0081e76ef8853aa69c8 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 22:00:44 +0100 Subject: [PATCH 06/14] fix pep8 --- pandas/tests/series/indexing/test_indexing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 8bc8c5bc29340..dbe667a166d0a 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -114,7 +114,8 @@ def test_getitem_get(test_data): # missing d = test_data.ts.index[0] - BDay() - with pytest.raises(KeyError, match=r"Timestamp\('1999-12-31 00:00:00', freq='B'\)"): + msg = r"Timestamp\('1999-12-31 00:00:00', freq='B'\)" + with pytest.raises(KeyError, match=msg): test_data.ts[d] # None From d4a1515fc5fa562b1ed9c36209c5798926ab3146 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sat, 9 Feb 2019 22:52:34 +0100 Subject: [PATCH 07/14] code change based on review --- pandas/tests/scalar/timestamp/test_timestamp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 1de708d72b3be..12af8a4b6a6ed 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -785,7 +785,7 @@ def test_tz_convert_freq(self, tz_naive_fixture): t1 = Timestamp('2019-01-01 10:00', freq='H') assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') - assert t2.tz_convert(tz='UTC') == t2 + assert t2.tz_convert(tz='UTC').freq == t2.freq class TestTimestampNsOperations(object): From 2734a3b64b7a7b2c84ec2cceea8096c1ba6194a3 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 08:28:58 +0100 Subject: [PATCH 08/14] code change based on review --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/tests/scalar/timestamp/test_timestamp.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 74418929f7a96..ecce54b88fb04 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -93,7 +93,7 @@ Timezones ^^^^^^^^^ - Bug in :func:`to_datetime` with ``utc=True`` and datetime strings that would apply previously parsed UTC offsets to subsequent arguments (:issue:`24992`) -- Bug in :func:`tz_localize` and :func:`tz_convert` does not propagate freq if ``Timestamp`` is initiated with freq (:issue:`25241`) +- Bug in :func:`Timestamp.tz_localize` and :func:`Timestamp.tz_convert` does not propagate ``freq`` (:issue:`25241`) - Numeric diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 12af8a4b6a6ed..3a9bce92fd93b 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -19,7 +19,7 @@ from pandas.errors import OutOfBoundsDatetime import pandas.util._test_decorators as td -from pandas import NaT, Period, Timedelta, Timestamp +from pandas import NaT, Period, Timedelta, Timestamp, DatetimeIndex import pandas.util.testing as tm from pandas.tseries import offsets @@ -780,13 +780,19 @@ def test_hash_equivalent(self): stamp = Timestamp(datetime(2011, 1, 1)) assert d[stamp] == 5 - def test_tz_convert_freq(self, tz_naive_fixture): + def test_tz_conversion_freq(self, tz_naive_fixture): # GH25241 t1 = Timestamp('2019-01-01 10:00', freq='H') assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') assert t2.tz_convert(tz='UTC').freq == t2.freq + # freq propagation doesn't apply to DatetimeIndex + t3 = DatetimeIndex('2019-01-01 10:00', freq='H') + assert t3.freq is None + t4 = DatetimeIndex('2019-01-02 12:00', tz='UTC', freq='T') + assert t4.freq is None + class TestTimestampNsOperations(object): From e514a23d1dbb40c338695f842d30fd5e6ef71091 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 10:13:35 +0100 Subject: [PATCH 09/14] fix test --- pandas/tests/scalar/timestamp/test_timestamp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 3a9bce92fd93b..b362380e14757 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -788,9 +788,9 @@ def test_tz_conversion_freq(self, tz_naive_fixture): assert t2.tz_convert(tz='UTC').freq == t2.freq # freq propagation doesn't apply to DatetimeIndex - t3 = DatetimeIndex('2019-01-01 10:00', freq='H') + t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') assert t3.freq is None - t4 = DatetimeIndex('2019-01-02 12:00', tz='UTC', freq='T') + t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') assert t4.freq is None From 89af1649ac7939cf87eab21840be7d7aff990c26 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 11:51:13 +0100 Subject: [PATCH 10/14] fix test --- pandas/tests/scalar/timestamp/test_timestamp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index b362380e14757..22965f3a04a75 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -789,9 +789,9 @@ def test_tz_conversion_freq(self, tz_naive_fixture): # freq propagation doesn't apply to DatetimeIndex t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') - assert t3.freq is None + assert t3.tz_localize(tz=tz_naive_fixture).freq is None t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') - assert t4.freq is None + assert t4.tz_convert(tz='UTC').freq is None class TestTimestampNsOperations(object): From 45d7b016d3676923007ee42c700f261888d0aad9 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 13:34:13 +0100 Subject: [PATCH 11/14] fix test --- pandas/tests/scalar/timestamp/test_timestamp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 22965f3a04a75..f9c5a79c8140b 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -789,9 +789,9 @@ def test_tz_conversion_freq(self, tz_naive_fixture): # freq propagation doesn't apply to DatetimeIndex t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') - assert t3.tz_localize(tz=tz_naive_fixture).freq is None + assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') - assert t4.tz_convert(tz='UTC').freq is None + assert t4.tz_convert(tz='UTC').freq == t4.freq class TestTimestampNsOperations(object): From 6666ea5c98ff63f5741c742fce317033b81d0c79 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 13:59:35 +0100 Subject: [PATCH 12/14] fix linting error --- pandas/tests/scalar/timestamp/test_timestamp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index f9c5a79c8140b..8573edebfc448 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -19,7 +19,7 @@ from pandas.errors import OutOfBoundsDatetime import pandas.util._test_decorators as td -from pandas import NaT, Period, Timedelta, Timestamp, DatetimeIndex +from pandas import DatetimeIndex, NaT, Period, Timedelta, Timestamp import pandas.util.testing as tm from pandas.tseries import offsets From d923ddf1240fd753f80f108a62879210b5cc5a8e Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 14:00:45 +0100 Subject: [PATCH 13/14] fix linting --- pandas/tests/scalar/timestamp/test_timestamp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 8573edebfc448..ab2188ce09a4c 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -787,7 +787,7 @@ def test_tz_conversion_freq(self, tz_naive_fixture): t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') assert t2.tz_convert(tz='UTC').freq == t2.freq - # freq propagation doesn't apply to DatetimeIndex + # freq propagation error doesn't happen with DatetimeIndex t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') From 115b87c2c945caea661902841ade864ce5277b57 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Sun, 10 Feb 2019 18:20:43 +0100 Subject: [PATCH 14/14] move test --- pandas/tests/indexes/datetimes/test_timezones.py | 7 +++++++ pandas/tests/scalar/timestamp/test_timestamp.py | 8 +------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py index 12c1b15733895..b25918417efcd 100644 --- a/pandas/tests/indexes/datetimes/test_timezones.py +++ b/pandas/tests/indexes/datetimes/test_timezones.py @@ -825,6 +825,13 @@ def test_dti_drop_dont_lose_tz(self): assert ind.tz is not None + def test_dti_tz_conversion_freq(self, tz_naive_fixture): + # GH25241 + t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') + assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq + t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') + assert t4.tz_convert(tz='UTC').freq == t4.freq + def test_drop_dst_boundary(self): # see gh-18031 tz = "Europe/Brussels" diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index ab2188ce09a4c..c27ef3d0662c8 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -19,7 +19,7 @@ from pandas.errors import OutOfBoundsDatetime import pandas.util._test_decorators as td -from pandas import DatetimeIndex, NaT, Period, Timedelta, Timestamp +from pandas import NaT, Period, Timedelta, Timestamp import pandas.util.testing as tm from pandas.tseries import offsets @@ -787,12 +787,6 @@ def test_tz_conversion_freq(self, tz_naive_fixture): t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') assert t2.tz_convert(tz='UTC').freq == t2.freq - # freq propagation error doesn't happen with DatetimeIndex - t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') - assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq - t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') - assert t4.tz_convert(tz='UTC').freq == t4.freq - class TestTimestampNsOperations(object):