diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 526595e3a2eda..26fea593cc703 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -251,13 +251,66 @@ def _validate_business_time(t_input): raise ValueError("time data must match '%H:%M' format") elif isinstance(t_input, dt_time): if t_input.second != 0 or t_input.microsecond != 0: - raise ValueError( - "time data must be specified only with hour and minute") + raise ValueError("time data must be specified only with " + "hour and minute") return t_input else: raise ValueError("time data must be string or datetime.time") +def _validate_n(n): + """ + Require that `n` be a nonzero integer. + + Parameters + ---------- + n : int + + Raises + ------ + ValueError + """ + if n == 0 or not isinstance(n, int): + raise ValueError("N cannot be 0") + + +def _validate_month(month): + """ + Require that `month` be an integer between 1 and 12 inclusive. + + Parameters + ---------- + month : int + + Raises + ------ + ValueError + """ + if not isinstance(month, int) or not 1 <= month <= 12: + raise ValueError("Month must go from 1 to 12") + + +def _validate_weekday(weekday, allow_none=False): + """ + Require that `weekday` be an integer between 0 and 6, inclusive, or that + None be explicitly allowed. + + Parameters + ---------- + weekday : int (or None) + allow_none : bool, default False + + Raises + ------ + ValueError + """ + if allow_none and weekday is None: + pass + elif not isinstance(weekday, int) or not 0 <= weekday <= 6: + raise ValueError("weekday must be 0<=weekday<=6, got " + "{day}".format(day=weekday)) + + # --------------------------------------------------------------------- # Constructor Helpers diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 6821017c89c3a..8bf4dd92b1ece 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -2180,7 +2180,7 @@ def test_repr(self): def test_corner(self): pytest.raises(ValueError, Week, weekday=7) tm.assert_raises_regex( - ValueError, "Day must be", Week, weekday=-1) + ValueError, "weekday must be", Week, weekday=-1) def test_isAnchored(self): assert Week(weekday=0).isAnchored() @@ -2251,9 +2251,9 @@ def test_constructor(self): n=1, week=4, weekday=0) tm.assert_raises_regex(ValueError, "^Week", WeekOfMonth, n=1, week=-1, weekday=0) - tm.assert_raises_regex(ValueError, "^Day", WeekOfMonth, + tm.assert_raises_regex(ValueError, "^weekday", WeekOfMonth, n=1, week=0, weekday=-1) - tm.assert_raises_regex(ValueError, "^Day", WeekOfMonth, + tm.assert_raises_regex(ValueError, "^weekday", WeekOfMonth, n=1, week=0, weekday=7) def test_repr(self): @@ -2335,10 +2335,10 @@ def test_constructor(self): tm.assert_raises_regex(ValueError, "^N cannot be 0", LastWeekOfMonth, n=0, weekday=1) - tm.assert_raises_regex(ValueError, "^Day", LastWeekOfMonth, n=1, + tm.assert_raises_regex(ValueError, "^weekday", LastWeekOfMonth, n=1, weekday=-1) tm.assert_raises_regex( - ValueError, "^Day", LastWeekOfMonth, n=1, weekday=7) + ValueError, "^weekday", LastWeekOfMonth, n=1, weekday=7) def test_offset(self): # Saturday diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index 021d636042954..dea6f6427a410 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -22,7 +22,8 @@ from pandas._libs.tslibs.offsets import ( ApplyTypeError, as_datetime, _is_normalized, - _get_calendar, _to_dt64, _validate_business_time, + _get_calendar, _to_dt64, + _validate_business_time, _validate_n, _validate_month, _validate_weekday, _int_to_weekday, _weekday_to_int, _determine_offset, apply_index_wraps, @@ -1350,10 +1351,7 @@ def __init__(self, n=1, normalize=False, weekday=None): self.normalize = normalize self.weekday = weekday - if self.weekday is not None: - if self.weekday < 0 or self.weekday > 6: - raise ValueError('Day must be 0<=day<=6, got {day}' - .format(day=self.weekday)) + _validate_weekday(weekday, allow_none=True) self.kwds = {'weekday': weekday} @@ -1430,12 +1428,8 @@ def __init__(self, n=1, normalize=False, week=None, weekday=None): self.weekday = weekday self.week = week - if self.n == 0: - raise ValueError('N cannot be 0') - - if self.weekday < 0 or self.weekday > 6: - raise ValueError('Day must be 0<=day<=6, got {day}' - .format(day=self.weekday)) + _validate_n(n) + _validate_weekday(weekday) if self.week < 0 or self.week > 3: raise ValueError('Week must be 0<=week<=3, got {week}' .format(week=self.week)) @@ -1514,12 +1508,8 @@ def __init__(self, n=1, normalize=False, weekday=None): self.normalize = normalize self.weekday = weekday - if self.n == 0: - raise ValueError('N cannot be 0') - - if self.weekday < 0 or self.weekday > 6: - raise ValueError('Day must be 0<=day<=6, got {day}' - .format(day=self.weekday)) + _validate_n(n) + _validate_weekday(weekday) self.kwds = {'weekday': weekday} @@ -1717,8 +1707,7 @@ def __init__(self, n=1, normalize=False, month=None): month = month if month is not None else self._default_month self.month = month - if self.month < 1 or self.month > 12: - raise ValueError('Month must go from 1 to 12') + _validate_month(month) DateOffset.__init__(self, n=n, normalize=normalize, month=month) @@ -1834,8 +1823,7 @@ def __init__(self, n=1, normalize=False, weekday=0, startingMonth=1, self.kwds = {'weekday': weekday, 'startingMonth': startingMonth, 'variation': variation} - if self.n == 0: - raise ValueError('N cannot be 0') + _validate_n(n) if self.variation not in ["nearest", "last"]: raise ValueError('{variation} is not a valid variation' @@ -2086,8 +2074,7 @@ def __init__(self, n=1, normalize=False, weekday=0, startingMonth=1, 'qtr_with_extra_week': qtr_with_extra_week, 'variation': variation} - if self.n == 0: - raise ValueError('N cannot be 0') + _validate_n(n) @cache_readonly def _offset(self):