@@ -123,7 +123,7 @@ cdef inline object create_timestamp_from_ts(int64_t value,
123123 dts.day, dts.hour, dts.min,
124124 dts.sec, dts.us, tz, fold = fold)
125125 ts_base.value = value
126- ts_base.freq = freq
126+ ts_base._freq = freq
127127 ts_base.nanosecond = dts.ps // 1000
128128
129129 return ts_base
@@ -155,6 +155,21 @@ cdef class _Timestamp(ABCTimestamp):
155155 dayofweek = _Timestamp.day_of_week
156156 dayofyear = _Timestamp.day_of_year
157157
158+ cpdef void _set_freq(self , freq):
159+ # set the ._freq attribute without going through the constructor,
160+ # which would issue a warning
161+ # Caller is responsible for validation
162+ self ._freq = freq
163+
164+ @property
165+ def freq (self ):
166+ warnings.warn(
167+ " Timestamp.freq is deprecated and will be removed in a future version" ,
168+ FutureWarning ,
169+ stacklevel = 1 ,
170+ )
171+ return self ._freq
172+
158173 def __hash__ (_Timestamp self ):
159174 if self .nanosecond:
160175 return hash (self .value)
@@ -263,7 +278,9 @@ cdef class _Timestamp(ABCTimestamp):
263278
264279 if is_any_td_scalar(other):
265280 nanos = delta_to_nanoseconds(other)
266- result = type (self )(self .value + nanos, tz = self .tzinfo, freq = self .freq)
281+ result = type (self )(self .value + nanos, tz = self .tzinfo)
282+ if result is not NaT:
283+ result._set_freq(self ._freq) # avoid warning in constructor
267284 return result
268285
269286 elif is_integer_object(other):
@@ -361,18 +378,17 @@ cdef class _Timestamp(ABCTimestamp):
361378 val = self .value
362379 return val
363380
364- cdef bint _get_start_end_field(self , str field):
381+ cdef bint _get_start_end_field(self , str field, freq ):
365382 cdef:
366383 int64_t val
367384 dict kwds
368385 ndarray[uint8_t, cast= True ] out
369386 int month_kw
370387
371- freq = self .freq
372388 if freq:
373389 kwds = freq.kwds
374390 month_kw = kwds.get(' startingMonth' , kwds.get(' month' , 12 ))
375- freqstr = self .freqstr
391+ freqstr = self ._freqstr
376392 else :
377393 month_kw = 12
378394 freqstr = None
@@ -382,6 +398,31 @@ cdef class _Timestamp(ABCTimestamp):
382398 field, freqstr, month_kw)
383399 return out[0 ]
384400
401+ cdef _warn_on_field_deprecation(self , freq, str field):
402+ """
403+ Warn if the removal of .freq change the value of start/end properties.
404+ """
405+ cdef:
406+ bint needs = False
407+
408+ if freq is not None :
409+ kwds = freq.kwds
410+ month_kw = kwds.get(" startingMonth" , kwds.get(" month" , 12 ))
411+ freqstr = self ._freqstr
412+ if month_kw != 12 :
413+ needs = True
414+ if freqstr.startswith(" B" ):
415+ needs = True
416+
417+ if needs:
418+ warnings.warn(
419+ " Timestamp.freq is deprecated and will be removed in a future "
420+ " version. When you have a freq, use "
421+ f" freq.{field}(timestamp) instead" ,
422+ FutureWarning ,
423+ stacklevel = 1 ,
424+ )
425+
385426 @property
386427 def is_month_start (self ) -> bool:
387428 """
@@ -397,10 +438,11 @@ cdef class _Timestamp(ABCTimestamp):
397438 >>> ts.is_month_start
398439 True
399440 """
400- if self.freq is None:
441+ if self._freq is None:
401442 # fast-path for non-business frequencies
402443 return self.day == 1
403- return self._get_start_end_field("is_month_start")
444+ self._warn_on_field_deprecation(self._freq , "is_month_start")
445+ return self._get_start_end_field("is_month_start", self._freq )
404446
405447 @property
406448 def is_month_end(self ) -> bool:
@@ -417,10 +459,11 @@ cdef class _Timestamp(ABCTimestamp):
417459 >>> ts.is_month_end
418460 True
419461 """
420- if self.freq is None:
462+ if self._freq is None:
421463 # fast-path for non-business frequencies
422464 return self.day == self.days_in_month
423- return self._get_start_end_field("is_month_end")
465+ self._warn_on_field_deprecation(self._freq , "is_month_end")
466+ return self._get_start_end_field("is_month_end", self._freq )
424467
425468 @property
426469 def is_quarter_start(self ) -> bool:
@@ -437,10 +480,11 @@ cdef class _Timestamp(ABCTimestamp):
437480 >>> ts.is_quarter_start
438481 True
439482 """
440- if self.freq is None:
483+ if self._freq is None:
441484 # fast-path for non-business frequencies
442485 return self.day == 1 and self.month % 3 == 1
443- return self._get_start_end_field("is_quarter_start")
486+ self._warn_on_field_deprecation(self._freq , "is_quarter_start")
487+ return self._get_start_end_field("is_quarter_start", self._freq )
444488
445489 @property
446490 def is_quarter_end(self ) -> bool:
@@ -457,10 +501,11 @@ cdef class _Timestamp(ABCTimestamp):
457501 >>> ts.is_quarter_end
458502 True
459503 """
460- if self.freq is None:
504+ if self._freq is None:
461505 # fast-path for non-business frequencies
462506 return (self.month % 3) == 0 and self.day == self.days_in_month
463- return self._get_start_end_field("is_quarter_end")
507+ self._warn_on_field_deprecation(self._freq , "is_quarter_end")
508+ return self._get_start_end_field("is_quarter_end", self._freq )
464509
465510 @property
466511 def is_year_start(self ) -> bool:
@@ -477,10 +522,11 @@ cdef class _Timestamp(ABCTimestamp):
477522 >>> ts.is_year_start
478523 True
479524 """
480- if self.freq is None:
525+ if self._freq is None:
481526 # fast-path for non-business frequencies
482527 return self.day == self.month == 1
483- return self._get_start_end_field("is_year_start")
528+ self._warn_on_field_deprecation(self._freq , "is_year_start")
529+ return self._get_start_end_field("is_year_start", self._freq )
484530
485531 @property
486532 def is_year_end(self ) -> bool:
@@ -497,10 +543,11 @@ cdef class _Timestamp(ABCTimestamp):
497543 >>> ts.is_year_end
498544 True
499545 """
500- if self.freq is None:
546+ if self._freq is None:
501547 # fast-path for non-business frequencies
502548 return self.month == 12 and self.day == 31
503- return self._get_start_end_field("is_year_end")
549+ self._warn_on_field_deprecation(self._freq , "is_year_end")
550+ return self._get_start_end_field("is_year_end", self._freq )
504551
505552 cdef _get_date_name_field(self , str field , object locale ):
506553 cdef:
@@ -673,11 +720,11 @@ cdef class _Timestamp(ABCTimestamp):
673720
674721 def __setstate__ (self , state ):
675722 self .value = state[0 ]
676- self .freq = state[1 ]
723+ self ._freq = state[1 ]
677724 self .tzinfo = state[2 ]
678725
679726 def __reduce__ (self ):
680- object_state = self .value, self .freq , self .tzinfo
727+ object_state = self .value, self ._freq , self .tzinfo
681728 return (Timestamp, object_state)
682729
683730 # -----------------------------------------------------------------
@@ -719,7 +766,7 @@ cdef class _Timestamp(ABCTimestamp):
719766 pass
720767
721768 tz = f" , tz='{zone}'" if zone is not None else " "
722- freq = " " if self .freq is None else f" , freq='{self.freqstr }'"
769+ freq = " " if self ._freq is None else f" , freq='{self._freqstr }'"
723770
724771 return f"Timestamp('{stamp}'{tz}{freq})"
725772
@@ -877,7 +924,13 @@ cdef class _Timestamp(ABCTimestamp):
877924 )
878925
879926 if freq is None :
880- freq = self .freq
927+ freq = self ._freq
928+ warnings.warn(
929+ " In a future version, calling 'Timestamp.to_period()' without "
930+ " passing a 'freq' will raise an exception." ,
931+ FutureWarning ,
932+ stacklevel = 2 ,
933+ )
881934
882935 return Period(self , freq = freq)
883936
@@ -1147,7 +1200,7 @@ class Timestamp(_Timestamp):
11471200 nanosecond = None ,
11481201 tzinfo_type tzinfo = None ,
11491202 *,
1150- fold = None
1203+ fold = None ,
11511204 ):
11521205 # The parameter list folds together legacy parameter names (the first
11531206 # four) and positional and keyword parameter names from pydatetime.
@@ -1276,9 +1329,16 @@ class Timestamp(_Timestamp):
12761329
12771330 if freq is None :
12781331 # GH 22311: Try to extract the frequency of a given Timestamp input
1279- freq = getattr (ts_input, ' freq' , None )
1280- elif not is_offset_object(freq):
1281- freq = to_offset(freq)
1332+ freq = getattr (ts_input, ' _freq' , None )
1333+ else :
1334+ warnings.warn(
1335+ " The 'freq' argument in Timestamp is deprecated and will be "
1336+ " removed in a future version." ,
1337+ FutureWarning ,
1338+ stacklevel = 1 ,
1339+ )
1340+ if not is_offset_object(freq):
1341+ freq = to_offset(freq)
12821342
12831343 return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, freq, ts.fold)
12841344
@@ -1551,12 +1611,21 @@ timedelta}, default 'raise'
15511611 " Use tz_localize() or tz_convert() as appropriate"
15521612 )
15531613
1614+ @property
1615+ def _freqstr (self ):
1616+ return getattr (self ._freq, " freqstr" , self ._freq)
1617+
15541618 @property
15551619 def freqstr (self ):
15561620 """
15571621 Return the total number of days in the month.
15581622 """
1559- return getattr (self .freq, ' freqstr' , self .freq)
1623+ warnings.warn(
1624+ " Timestamp.freqstr is deprecated and will be removed in a future version." ,
1625+ FutureWarning ,
1626+ stacklevel = 1 ,
1627+ )
1628+ return self ._freqstr
15601629
15611630 def tz_localize (self , tz , ambiguous = ' raise' , nonexistent = ' raise' ):
15621631 """
@@ -1647,12 +1716,18 @@ default 'raise'
16471716 value = tz_localize_to_utc_single(self .value, tz,
16481717 ambiguous = ambiguous,
16491718 nonexistent = nonexistent)
1650- return Timestamp(value, tz = tz, freq = self .freq)
1719+ out = Timestamp(value, tz = tz)
1720+ if out is not NaT:
1721+ out._set_freq(self ._freq) # avoid warning in constructor
1722+ return out
16511723 else :
16521724 if tz is None :
16531725 # reset tz
16541726 value = tz_convert_from_utc_single(self .value, self .tz)
1655- return Timestamp(value, tz = tz, freq = self .freq)
1727+ out = Timestamp(value, tz = tz)
1728+ if out is not NaT:
1729+ out._set_freq(self ._freq) # avoid warning in constructor
1730+ return out
16561731 else :
16571732 raise TypeError (
16581733 " Cannot localize tz-aware Timestamp, use tz_convert for conversions"
@@ -1707,7 +1782,10 @@ default 'raise'
17071782 )
17081783 else :
17091784 # Same UTC timestamp, different time zone
1710- return Timestamp(self .value, tz = tz, freq = self .freq)
1785+ out = Timestamp(self .value, tz = tz)
1786+ if out is not NaT:
1787+ out._set_freq(self ._freq) # avoid warning in constructor
1788+ return out
17111789
17121790 astimezone = tz_convert
17131791
@@ -1840,7 +1918,7 @@ default 'raise'
18401918 if value != NPY_NAT:
18411919 check_dts_bounds(& dts)
18421920
1843- return create_timestamp_from_ts(value, dts, tzobj, self .freq , fold)
1921+ return create_timestamp_from_ts(value, dts, tzobj, self ._freq , fold)
18441922
18451923 def to_julian_date (self ) -> np.float64:
18461924 """
0 commit comments