diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 597a0c5386cf0..a2a2a87bbdc56 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -486,6 +486,7 @@ Deprecations arguments (:issue:`27573`). - :func:`pandas.api.types.is_categorical` is deprecated and will be removed in a future version; use `:func:pandas.api.types.is_categorical_dtype` instead (:issue:`33385`) +- The :class:`Timestamp` attribute ``freq`` is deprecated and will be removed in a future version; passing ``freq`` to the constructor will raise ``TypeError`` (:issue:`15146`) .. --------------------------------------------------------------------------- diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 53bcf5be2586a..088b51e60f9ae 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -1,3 +1,5 @@ +import warnings + import cython from cpython.datetime cimport ( @@ -163,6 +165,13 @@ def ints_to_pydatetime( elif box == "timestamp": func_create = create_timestamp_from_ts + if freq is not None: + warnings.warn( + "Passing `freq` to Timestamp is deprecated and will raise " + "TypeError in a future version.", + FutureWarning, + ) + if isinstance(freq, str): freq = to_offset(freq) elif box == "time": diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 7858072407a35..076ad11e45df8 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -484,6 +484,13 @@ class Timestamp(_Timestamp): if ts.value == NPY_NAT: return NaT + if freq is not None: + warnings.warn( + "Passing `freq` to Timestamp is deprecated and will raise " + "TypeError in a future version.", + FutureWarning, + ) + if freq is None: # GH 22311: Try to extract the frequency of a given Timestamp input freq = getattr(ts_input, 'freq', None) diff --git a/pandas/conftest.py b/pandas/conftest.py index 16b6d40645547..6427b738ef843 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -39,6 +39,10 @@ from pandas.core import ops from pandas.core.indexes.api import Index, MultiIndex +silence_freq_deprecation = pytest.mark.filterwarnings( + "ignore:Passing `freq` to Timestamp is deprecated" +) + # ---------------------------------------------------------------- # Configuration / Settings @@ -78,6 +82,13 @@ def pytest_runtest_setup(item): pytest.skip("skipping high memory test since --run-high-memory was not set") +def pytest_collection_modifyitems(config, items): + for item in items: + # By adding this marker here, we do not need to silence it throughout + # the tests + item.add_marker(silence_freq_deprecation) + + # Hypothesis hypothesis.settings.register_profile( "ci", diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 8c937064c0493..7bd5553e2a4f5 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -967,7 +967,10 @@ def test_dt64arr_sub_dt64object_array(self, box_with_array, tz_naive_fixture): warn = PerformanceWarning if box_with_array is not pd.DataFrame else None with tm.assert_produces_warning(warn): - result = obj - obj.astype(object) + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = obj - obj.astype(object) tm.assert_equal(result, expected) def test_dt64arr_naive_sub_dt64ndarray(self, box_with_array): @@ -1457,11 +1460,15 @@ def test_dt64arr_add_sub_offset_array( # GH#10699 array of offsets tz = tz_naive_fixture - dti = pd.date_range("2017-01-01", periods=2, tz=tz) - dtarr = tm.box_expected(dti, box_with_array) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + dti = pd.date_range("2017-01-01", periods=2, tz=tz) + dtarr = tm.box_expected(dti, box_with_array) other = np.array([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - expected = DatetimeIndex([op(dti[n], other[n]) for n in range(len(dti))]) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + expected = DatetimeIndex([op(dti[n], other[n]) for n in range(len(dti))]) expected = tm.box_expected(expected, box_with_array) if box_other: @@ -1471,7 +1478,9 @@ def test_dt64arr_add_sub_offset_array( if box_with_array is pd.DataFrame and not (tz is None and not box_other): warn = None with tm.assert_produces_warning(warn): - res = op(dtarr, other) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + res = op(dtarr, other) tm.assert_equal(res, expected) @@ -2391,10 +2400,15 @@ def test_dti_addsub_offset_arraylike( xbox = get_upcast_box(box, other) with tm.assert_produces_warning(PerformanceWarning): - res = op(dti, other) + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + res = op(dti, other) expected = DatetimeIndex( - [op(dti[n], other[n]) for n in range(len(dti))], name=names[2], freq="infer" + [op(dti[n], other[n]) for n in range(len(dti))], + name=names[2], + freq="infer", ) expected = tm.box_expected(expected, xbox) tm.assert_equal(res, expected) @@ -2418,14 +2432,20 @@ def test_dti_addsub_object_arraylike( warn = None with tm.assert_produces_warning(warn): - result = dtarr + other + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = dtarr + other tm.assert_equal(result, expected) expected = pd.DatetimeIndex(["2016-12-31", "2016-12-29"], tz=tz_naive_fixture) expected = tm.box_expected(expected, xbox) with tm.assert_produces_warning(warn): - result = dtarr - other + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = dtarr - other tm.assert_equal(result, expected) diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index 42a72125ba411..c193b87a4be73 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -242,40 +242,42 @@ def test_datetimeindex_accessors(self): assert dti.is_month_start[0] == 1 - tests = [ - (Timestamp("2013-06-01", freq="M").is_month_start, 1), - (Timestamp("2013-06-01", freq="BM").is_month_start, 0), - (Timestamp("2013-06-03", freq="M").is_month_start, 0), - (Timestamp("2013-06-03", freq="BM").is_month_start, 1), - (Timestamp("2013-02-28", freq="Q-FEB").is_month_end, 1), - (Timestamp("2013-02-28", freq="Q-FEB").is_quarter_end, 1), - (Timestamp("2013-02-28", freq="Q-FEB").is_year_end, 1), - (Timestamp("2013-03-01", freq="Q-FEB").is_month_start, 1), - (Timestamp("2013-03-01", freq="Q-FEB").is_quarter_start, 1), - (Timestamp("2013-03-01", freq="Q-FEB").is_year_start, 1), - (Timestamp("2013-03-31", freq="QS-FEB").is_month_end, 1), - (Timestamp("2013-03-31", freq="QS-FEB").is_quarter_end, 0), - (Timestamp("2013-03-31", freq="QS-FEB").is_year_end, 0), - (Timestamp("2013-02-01", freq="QS-FEB").is_month_start, 1), - (Timestamp("2013-02-01", freq="QS-FEB").is_quarter_start, 1), - (Timestamp("2013-02-01", freq="QS-FEB").is_year_start, 1), - (Timestamp("2013-06-30", freq="BQ").is_month_end, 0), - (Timestamp("2013-06-30", freq="BQ").is_quarter_end, 0), - (Timestamp("2013-06-30", freq="BQ").is_year_end, 0), - (Timestamp("2013-06-28", freq="BQ").is_month_end, 1), - (Timestamp("2013-06-28", freq="BQ").is_quarter_end, 1), - (Timestamp("2013-06-28", freq="BQ").is_year_end, 0), - (Timestamp("2013-06-30", freq="BQS-APR").is_month_end, 0), - (Timestamp("2013-06-30", freq="BQS-APR").is_quarter_end, 0), - (Timestamp("2013-06-30", freq="BQS-APR").is_year_end, 0), - (Timestamp("2013-06-28", freq="BQS-APR").is_month_end, 1), - (Timestamp("2013-06-28", freq="BQS-APR").is_quarter_end, 1), - (Timestamp("2013-03-29", freq="BQS-APR").is_year_end, 1), - (Timestamp("2013-11-01", freq="AS-NOV").is_year_start, 1), - (Timestamp("2013-10-31", freq="AS-NOV").is_year_end, 1), - (Timestamp("2012-02-01").days_in_month, 29), - (Timestamp("2013-02-01").days_in_month, 28), - ] + with tm.assert_produces_warning(FutureWarning): + # passing freq to Timestamp + tests = [ + (Timestamp("2013-06-01", freq="M").is_month_start, 1), + (Timestamp("2013-06-01", freq="BM").is_month_start, 0), + (Timestamp("2013-06-03", freq="M").is_month_start, 0), + (Timestamp("2013-06-03", freq="BM").is_month_start, 1), + (Timestamp("2013-02-28", freq="Q-FEB").is_month_end, 1), + (Timestamp("2013-02-28", freq="Q-FEB").is_quarter_end, 1), + (Timestamp("2013-02-28", freq="Q-FEB").is_year_end, 1), + (Timestamp("2013-03-01", freq="Q-FEB").is_month_start, 1), + (Timestamp("2013-03-01", freq="Q-FEB").is_quarter_start, 1), + (Timestamp("2013-03-01", freq="Q-FEB").is_year_start, 1), + (Timestamp("2013-03-31", freq="QS-FEB").is_month_end, 1), + (Timestamp("2013-03-31", freq="QS-FEB").is_quarter_end, 0), + (Timestamp("2013-03-31", freq="QS-FEB").is_year_end, 0), + (Timestamp("2013-02-01", freq="QS-FEB").is_month_start, 1), + (Timestamp("2013-02-01", freq="QS-FEB").is_quarter_start, 1), + (Timestamp("2013-02-01", freq="QS-FEB").is_year_start, 1), + (Timestamp("2013-06-30", freq="BQ").is_month_end, 0), + (Timestamp("2013-06-30", freq="BQ").is_quarter_end, 0), + (Timestamp("2013-06-30", freq="BQ").is_year_end, 0), + (Timestamp("2013-06-28", freq="BQ").is_month_end, 1), + (Timestamp("2013-06-28", freq="BQ").is_quarter_end, 1), + (Timestamp("2013-06-28", freq="BQ").is_year_end, 0), + (Timestamp("2013-06-30", freq="BQS-APR").is_month_end, 0), + (Timestamp("2013-06-30", freq="BQS-APR").is_quarter_end, 0), + (Timestamp("2013-06-30", freq="BQS-APR").is_year_end, 0), + (Timestamp("2013-06-28", freq="BQS-APR").is_month_end, 1), + (Timestamp("2013-06-28", freq="BQS-APR").is_quarter_end, 1), + (Timestamp("2013-03-29", freq="BQS-APR").is_year_end, 1), + (Timestamp("2013-11-01", freq="AS-NOV").is_year_start, 1), + (Timestamp("2013-10-31", freq="AS-NOV").is_year_end, 1), + (Timestamp("2012-02-01").days_in_month, 29), + (Timestamp("2013-02-01").days_in_month, 28), + ] for ts, value in tests: assert ts == value diff --git a/pandas/tests/indexes/datetimes/test_shift.py b/pandas/tests/indexes/datetimes/test_shift.py index 8724bfeb05c4d..3848311f374c4 100644 --- a/pandas/tests/indexes/datetimes/test_shift.py +++ b/pandas/tests/indexes/datetimes/test_shift.py @@ -1,4 +1,5 @@ from datetime import datetime +import warnings import pytest import pytz @@ -149,5 +150,9 @@ def test_shift_bmonth(self): rng = date_range(START, END, freq=pd.offsets.BMonthEnd()) with tm.assert_produces_warning(pd.errors.PerformanceWarning): - shifted = rng.shift(1, freq=pd.offsets.CDay()) - assert shifted[0] == rng[0] + pd.offsets.CDay() + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + + shifted = rng.shift(1, freq=pd.offsets.CDay()) + assert shifted[0] == rng[0] + pd.offsets.CDay() diff --git a/pandas/tests/indexes/datetimes/test_to_period.py b/pandas/tests/indexes/datetimes/test_to_period.py index d82fc1ef6743b..e90713dfad908 100644 --- a/pandas/tests/indexes/datetimes/test_to_period.py +++ b/pandas/tests/indexes/datetimes/test_to_period.py @@ -147,8 +147,11 @@ def test_to_period_tz(self, tz): with tm.assert_produces_warning(UserWarning): # GH#21333 warning that timezone info will be lost - result = ts.to_period()[0] - expected = ts[0].to_period() + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = ts.to_period()[0] + expected = ts[0].to_period() assert result == expected @@ -165,9 +168,13 @@ def test_to_period_tz_utc_offset_consistency(self, tz): # GH#22905 ts = date_range("1/1/2000", "2/1/2000", tz="Etc/GMT-1") with tm.assert_produces_warning(UserWarning): - result = ts.to_period()[0] - expected = ts[0].to_period() - assert result == expected + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = ts.to_period()[0] + expected = ts[0].to_period() + + assert result == expected def test_to_period_nofreq(self): idx = DatetimeIndex(["2000-01-01", "2000-01-02", "2000-01-04"]) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 7b42e9646918e..36576b9c0c6bd 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -4,6 +4,7 @@ import math import operator import re +import warnings import numpy as np import pytest @@ -1924,12 +1925,20 @@ def test_outer_join_sort(self): right_index = tm.makeDateIndex(10) with tm.assert_produces_warning(RuntimeWarning): - result = left_index.join(right_index, how="outer") + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + + result = left_index.join(right_index, how="outer") # right_index in this case because DatetimeIndex has join precedence # over Int64Index with tm.assert_produces_warning(RuntimeWarning): - expected = right_index.astype(object).union(left_index.astype(object)) + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + + expected = right_index.astype(object).union(left_index.astype(object)) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/scalar/timestamp/test_constructors.py b/pandas/tests/scalar/timestamp/test_constructors.py index 770753f42a4c8..a391af54010ec 100644 --- a/pandas/tests/scalar/timestamp/test_constructors.py +++ b/pandas/tests/scalar/timestamp/test_constructors.py @@ -10,6 +10,7 @@ from pandas.errors import OutOfBoundsDatetime from pandas import Period, Timedelta, Timestamp, compat +import pandas._testing as tm from pandas.tseries import offsets @@ -187,7 +188,8 @@ def test_constructor_invalid_tz(self): # GH#5168 # case where user tries to pass tz as an arg, not kwarg, gets # interpreted as a `freq` - Timestamp("2012-01-01", "US/Pacific") + with tm.assert_produces_warning(FutureWarning): + Timestamp("2012-01-01", "US/Pacific") def test_constructor_strptime(self): # GH25016 @@ -271,7 +273,8 @@ def test_constructor_keyword(self): def test_constructor_fromordinal(self): base = datetime(2000, 1, 1) - ts = Timestamp.fromordinal(base.toordinal(), freq="D") + with tm.assert_produces_warning(FutureWarning): + ts = Timestamp.fromordinal(base.toordinal(), freq="D") assert base == ts assert ts.freq == "D" assert base.toordinal() == ts.toordinal() @@ -495,7 +498,8 @@ def test_construct_with_different_string_format(self, arg): def test_construct_timestamp_preserve_original_frequency(self): # GH 22311 - result = Timestamp(Timestamp("2010-08-08", freq="D")).freq + with tm.assert_produces_warning(FutureWarning): + result = Timestamp(Timestamp("2010-08-08", freq="D")).freq expected = offsets.Day() assert result == expected @@ -503,7 +507,12 @@ def test_constructor_invalid_frequency(self): # GH 22311 msg = "Invalid frequency:" with pytest.raises(ValueError, match=msg): - Timestamp("2012-01-01", freq=[]) + with tm.assert_produces_warning(FutureWarning): + Timestamp("2012-01-01", freq=[]) + + def test_freq_deprecated(self): + with tm.assert_produces_warning(FutureWarning): + Timestamp("2012-01-01", freq="D") @pytest.mark.parametrize("box", [datetime, Timestamp]) def test_raise_tz_and_tzinfo_in_datetime_input(self, box): diff --git a/pandas/tests/series/test_timeseries.py b/pandas/tests/series/test_timeseries.py index 15b6481c08a61..716d74cdc5419 100644 --- a/pandas/tests/series/test_timeseries.py +++ b/pandas/tests/series/test_timeseries.py @@ -1,3 +1,5 @@ +import warnings + import numpy as np import pytest @@ -115,7 +117,12 @@ def test_asarray_object_dt64(self, tz): with tm.assert_produces_warning(None): # Future behavior (for tzaware case) with no warning - result = np.asarray(ser, dtype=object) + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + # FIXME: this makes the assert_produces_warning(None) toothless + + result = np.asarray(ser, dtype=object) expected = np.array( [pd.Timestamp("2000-01-01", tz=tz), pd.Timestamp("2000-01-02", tz=tz)] diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 5fe62f41e49ff..dc73016f3aaf7 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -608,7 +608,11 @@ def apply(self, other): # shift by n days plus 2 to get past the weekend days = n + 2 - result = other + timedelta(days=7 * weeks + days) + with warnings.catch_warnings(): + # Passing freq to Timestamp constructor + warnings.simplefilter("ignore", FutureWarning) + result = other + timedelta(days=7 * weeks + days) + if self.offset: result = result + self.offset return result