@@ -190,17 +190,17 @@ class DatetimeLikeArrayMixin(OpsMixin, NDArrayBackedExtensionArray):
190
190
191
191
Assumes that __new__/__init__ defines:
192
192
_ndarray
193
- _freq
194
193
195
- and that the inheriting class has methods :
196
- _generate_range
194
+ and that inheriting subclass implements :
195
+ freq
197
196
"""
198
197
199
198
# _infer_matches -> which infer_dtype strings are close enough to our own
200
199
_infer_matches : tuple [str , ...]
201
200
_is_recognized_dtype : Callable [[DtypeObj ], bool ]
202
201
_recognized_scalars : tuple [type , ...]
203
202
_ndarray : np .ndarray
203
+ freq : BaseOffset | None
204
204
205
205
@cache_readonly
206
206
def _can_hold_na (self ) -> bool :
@@ -896,24 +896,6 @@ def _maybe_mask_results(
896
896
# ------------------------------------------------------------------
897
897
# Frequency Properties/Methods
898
898
899
- @property
900
- def freq (self ):
901
- """
902
- Return the frequency object if it is set, otherwise None.
903
- """
904
- return self ._freq
905
-
906
- @freq .setter
907
- def freq (self , value ) -> None :
908
- if value is not None :
909
- value = to_offset (value )
910
- self ._validate_frequency (self , value )
911
-
912
- if self .ndim > 1 :
913
- raise ValueError ("Cannot set freq with ndim > 1" )
914
-
915
- self ._freq = value
916
-
917
899
@property
918
900
def freqstr (self ) -> str | None :
919
901
"""
@@ -955,51 +937,6 @@ def resolution(self) -> str:
955
937
# error: Item "None" of "Optional[Any]" has no attribute "attrname"
956
938
return self ._resolution_obj .attrname # type: ignore[union-attr]
957
939
958
- @classmethod
959
- def _validate_frequency (cls , index , freq , ** kwargs ):
960
- """
961
- Validate that a frequency is compatible with the values of a given
962
- Datetime Array/Index or Timedelta Array/Index
963
-
964
- Parameters
965
- ----------
966
- index : DatetimeIndex or TimedeltaIndex
967
- The index on which to determine if the given frequency is valid
968
- freq : DateOffset
969
- The frequency to validate
970
- """
971
- # TODO: this is not applicable to PeriodArray, move to correct Mixin
972
- inferred = index .inferred_freq
973
- if index .size == 0 or inferred == freq .freqstr :
974
- return None
975
-
976
- try :
977
- on_freq = cls ._generate_range (
978
- start = index [0 ], end = None , periods = len (index ), freq = freq , ** kwargs
979
- )
980
- if not np .array_equal (index .asi8 , on_freq .asi8 ):
981
- raise ValueError
982
- except ValueError as e :
983
- if "non-fixed" in str (e ):
984
- # non-fixed frequencies are not meaningful for timedelta64;
985
- # we retain that error message
986
- raise e
987
- # GH#11587 the main way this is reached is if the `np.array_equal`
988
- # check above is False. This can also be reached if index[0]
989
- # is `NaT`, in which case the call to `cls._generate_range` will
990
- # raise a ValueError, which we re-raise with a more targeted
991
- # message.
992
- raise ValueError (
993
- f"Inferred frequency { inferred } from passed values "
994
- f"does not conform to passed frequency { freq .freqstr } "
995
- ) from e
996
-
997
- @classmethod
998
- def _generate_range (
999
- cls : type [DatetimeLikeArrayT ], start , end , periods , freq , * args , ** kwargs
1000
- ) -> DatetimeLikeArrayT :
1001
- raise AbstractMethodError (cls )
1002
-
1003
940
# monotonicity/uniqueness properties are called via frequencies.infer_freq,
1004
941
# see GH#23789
1005
942
@@ -1953,6 +1890,68 @@ def __init__(
1953
1890
def _validate_dtype (cls , values , dtype ):
1954
1891
raise AbstractMethodError (cls )
1955
1892
1893
+ @property
1894
+ def freq (self ):
1895
+ """
1896
+ Return the frequency object if it is set, otherwise None.
1897
+ """
1898
+ return self ._freq
1899
+
1900
+ @freq .setter
1901
+ def freq (self , value ) -> None :
1902
+ if value is not None :
1903
+ value = to_offset (value )
1904
+ self ._validate_frequency (self , value )
1905
+
1906
+ if self .ndim > 1 :
1907
+ raise ValueError ("Cannot set freq with ndim > 1" )
1908
+
1909
+ self ._freq = value
1910
+
1911
+ @classmethod
1912
+ def _validate_frequency (cls , index , freq , ** kwargs ):
1913
+ """
1914
+ Validate that a frequency is compatible with the values of a given
1915
+ Datetime Array/Index or Timedelta Array/Index
1916
+
1917
+ Parameters
1918
+ ----------
1919
+ index : DatetimeIndex or TimedeltaIndex
1920
+ The index on which to determine if the given frequency is valid
1921
+ freq : DateOffset
1922
+ The frequency to validate
1923
+ """
1924
+ inferred = index .inferred_freq
1925
+ if index .size == 0 or inferred == freq .freqstr :
1926
+ return None
1927
+
1928
+ try :
1929
+ on_freq = cls ._generate_range (
1930
+ start = index [0 ], end = None , periods = len (index ), freq = freq , ** kwargs
1931
+ )
1932
+ if not np .array_equal (index .asi8 , on_freq .asi8 ):
1933
+ raise ValueError
1934
+ except ValueError as err :
1935
+ if "non-fixed" in str (err ):
1936
+ # non-fixed frequencies are not meaningful for timedelta64;
1937
+ # we retain that error message
1938
+ raise err
1939
+ # GH#11587 the main way this is reached is if the `np.array_equal`
1940
+ # check above is False. This can also be reached if index[0]
1941
+ # is `NaT`, in which case the call to `cls._generate_range` will
1942
+ # raise a ValueError, which we re-raise with a more targeted
1943
+ # message.
1944
+ raise ValueError (
1945
+ f"Inferred frequency { inferred } from passed values "
1946
+ f"does not conform to passed frequency { freq .freqstr } "
1947
+ ) from err
1948
+
1949
+ @classmethod
1950
+ def _generate_range (
1951
+ cls : type [DatetimeLikeArrayT ], start , end , periods , freq , * args , ** kwargs
1952
+ ) -> DatetimeLikeArrayT :
1953
+ raise AbstractMethodError (cls )
1954
+
1956
1955
# --------------------------------------------------------------
1957
1956
1958
1957
@cache_readonly
0 commit comments