From ff34210ff1dfd1a03748faf7f5e88ea0d67f7a5e Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 9 Oct 2017 18:27:24 -0700 Subject: [PATCH 1/7] implement tslibs.offsets, cut/paste basics --- pandas/_libs/tslibs/offsets.pyx | 207 ++++++++++++++++++++++++++++++++ pandas/tseries/frequencies.py | 47 +------- pandas/tseries/offsets.py | 137 ++------------------- setup.py | 19 ++- 4 files changed, 227 insertions(+), 183 deletions(-) create mode 100644 pandas/_libs/tslibs/offsets.pyx diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx new file mode 100644 index 0000000000000..2cac7f023ac37 --- /dev/null +++ b/pandas/_libs/tslibs/offsets.pyx @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# cython: profile=False + +cimport cython + +import time +from cpython.datetime cimport time as dt_time + +import numpy as np +cimport numpy as np +np.import_array() + + +from util cimport is_string_object + + +from pandas._libs.tslib import pydt_to_i8, tz_convert_single + +# --------------------------------------------------------------------- +# Constants + +# Duplicated in tslib +_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', + 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] +_int_to_month = {(k + 1): v for k, v in enumerate(_MONTHS)} +_month_to_int = dict((v, k) for k, v in _int_to_month.items()) + +class WeekDay(object): + MON = 0 + TUE = 1 + WED = 2 + THU = 3 + FRI = 4 + SAT = 5 + SUN = 6 + + +_int_to_weekday = { + WeekDay.MON: 'MON', + WeekDay.TUE: 'TUE', + WeekDay.WED: 'WED', + WeekDay.THU: 'THU', + WeekDay.FRI: 'FRI', + WeekDay.SAT: 'SAT', + WeekDay.SUN: 'SUN'} + +_weekday_to_int = {_int_to_weekday[key]: key for key in _int_to_weekday} + + +_offset_to_period_map = { + 'WEEKDAY': 'D', + 'EOM': 'M', + 'BM': 'M', + 'BQS': 'Q', + 'QS': 'Q', + 'BQ': 'Q', + 'BA': 'A', + 'AS': 'A', + 'BAS': 'A', + 'MS': 'M', + 'D': 'D', + 'C': 'C', + 'B': 'B', + 'T': 'T', + 'S': 'S', + 'L': 'L', + 'U': 'U', + 'N': 'N', + 'H': 'H', + 'Q': 'Q', + 'A': 'A', + 'W': 'W', + 'M': 'M', + 'Y': 'A', + 'BY': 'A', + 'YS': 'A', + 'BYS': 'A' + } + +need_suffix = ['QS', 'BQ', 'BQS', 'YS', 'AS', 'BY', 'BA', 'BYS', 'BAS'] + + +for __prefix in need_suffix: + for _m in _MONTHS: + key = '%s-%s' % (__prefix, _m) + _offset_to_period_map[key] = _offset_to_period_map[__prefix] + +for __prefix in ['A', 'Q']: + for _m in _MONTHS: + _alias = '%s-%s' % (__prefix, _m) + _offset_to_period_map[_alias] = _alias + +_days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] +for _d in _days: + _offset_to_period_map['W-%s' % _d] = 'W-%s' % _d + + +# --------------------------------------------------------------------- +# Misc Helpers + +def as_datetime(obj): + f = getattr(obj, 'to_pydatetime', None) + if f is not None: + obj = f() + return obj + + +def _is_normalized(dt): + if (dt.hour != 0 or dt.minute != 0 or dt.second != 0 or + dt.microsecond != 0 or getattr(dt, 'nanosecond', 0) != 0): + return False + return True + + +# --------------------------------------------------------------------- +# Business Helpers + +def _get_firstbday(wkday): + """ + wkday is the result of monthrange(year, month) + + If it's a saturday or sunday, increment first business day to reflect this + """ + first = 1 + if wkday == 5: # on Saturday + first = 3 + elif wkday == 6: # on Sunday + first = 2 + return first + + +def _get_calendar(weekmask, holidays, calendar): + """Generate busdaycalendar""" + if isinstance(calendar, np.busdaycalendar): + if not holidays: + holidays = tuple(calendar.holidays) + elif not isinstance(holidays, tuple): + holidays = tuple(holidays) + else: + # trust that calendar.holidays and holidays are + # consistent + pass + return calendar, holidays + + if holidays is None: + holidays = [] + try: + holidays = holidays + calendar.holidays().tolist() + except AttributeError: + pass + holidays = [_to_dt64(dt, dtype='datetime64[D]') for dt in holidays] + holidays = tuple(sorted(holidays)) + + kwargs = {'weekmask': weekmask} + if holidays: + kwargs['holidays'] = holidays + + busdaycalendar = np.busdaycalendar(**kwargs) + return busdaycalendar, holidays + + +def _to_dt64(dt, dtype='datetime64'): + # Currently + # > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]') + # numpy.datetime64('2013-05-01T02:00:00.000000+0200') + # Thus astype is needed to cast datetime to datetime64[D] + if getattr(dt, 'tzinfo', None) is not None: + i8 = pydt_to_i8(dt) + dt = tz_convert_single(i8, 'UTC', dt.tzinfo) + dt = np.int64(dt).astype('datetime64[ns]') + else: + dt = np.datetime64(dt) + if dt.dtype.name != dtype: + dt = dt.astype(dtype) + return dt + + +# --------------------------------------------------------------------- +# Validation + + +def _validate_business_time(t_input): + if is_string_object(t_input): + try: + t = time.strptime(t_input, '%H:%M') + return dt_time(hour=t.tm_hour, minute=t.tm_min) + except ValueError: + 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") + return t_input + else: + raise ValueError("time data must be string or datetime.time") + +# --------------------------------------------------------------------- +# Mixins & Singletons + +class ApplyTypeError(TypeError): + # sentinel class for catching the apply error to return NotImplemented + pass + + +# TODO: unused. remove? +class CacheableOffset(object): + _cacheable = True diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index b055c4b4cb27f..763e6547ea2cb 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -312,7 +312,7 @@ def _get_freq_str(base, mult=1): # --------------------------------------------------------------------- # Offset names ("time rules") and related functions - +from pandas._libs.tslibs.offsets import _offset_to_period_map from pandas.tseries.offsets import (Nano, Micro, Milli, Second, # noqa Minute, Hour, Day, BDay, CDay, Week, MonthBegin, @@ -328,51 +328,6 @@ def _get_freq_str(base, mult=1): #: cache of previously seen offsets _offset_map = {} -_offset_to_period_map = { - 'WEEKDAY': 'D', - 'EOM': 'M', - 'BM': 'M', - 'BQS': 'Q', - 'QS': 'Q', - 'BQ': 'Q', - 'BA': 'A', - 'AS': 'A', - 'BAS': 'A', - 'MS': 'M', - 'D': 'D', - 'C': 'C', - 'B': 'B', - 'T': 'T', - 'S': 'S', - 'L': 'L', - 'U': 'U', - 'N': 'N', - 'H': 'H', - 'Q': 'Q', - 'A': 'A', - 'W': 'W', - 'M': 'M', - 'Y': 'A', - 'BY': 'A', - 'YS': 'A', - 'BYS': 'A', -} - -need_suffix = ['QS', 'BQ', 'BQS', 'YS', 'AS', 'BY', 'BA', 'BYS', 'BAS'] -for __prefix in need_suffix: - for _m in tslib._MONTHS: - _alias = '{prefix}-{month}'.format(prefix=__prefix, month=_m) - _offset_to_period_map[_alias] = _offset_to_period_map[__prefix] -for __prefix in ['A', 'Q']: - for _m in tslib._MONTHS: - _alias = '{prefix}-{month}'.format(prefix=__prefix, month=_m) - _offset_to_period_map[_alias] = _alias - -_days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] -for _d in _days: - _alias = 'W-{day}'.format(day=_d) - _offset_to_period_map[_alias] = _alias - def get_period_alias(offset_str): """ alias to closest period strings BQ->Q etc""" diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index c65691618e654..f9d90a0afab39 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -14,6 +14,14 @@ from pandas._libs import tslib, Timestamp, OutOfBoundsDatetime, Timedelta from pandas.util._decorators import cache_readonly +from pandas._libs.tslib import _delta_to_nanoseconds +from pandas._libs.tslibs.offsets import ( + ApplyTypeError, CacheableOffset, + _determine_offset, + as_datetime, _is_normalized, + _get_firstbday, _get_calendar, _to_dt64, _validate_business_time, + _int_to_weekday, _weekday_to_int, WeekDay) + import functools import operator @@ -43,13 +51,6 @@ def as_timestamp(obj): return obj -def as_datetime(obj): - f = getattr(obj, 'to_pydatetime', None) - if f is not None: - obj = f() - return obj - - def apply_wraps(func): @functools.wraps(func) def wrapper(self, other): @@ -115,25 +116,10 @@ def wrapper(self, other): return wrapper -def _is_normalized(dt): - if (dt.hour != 0 or dt.minute != 0 or dt.second != 0 or - dt.microsecond != 0 or getattr(dt, 'nanosecond', 0) != 0): - return False - return True - # --------------------------------------------------------------------- # DateOffset -class ApplyTypeError(TypeError): - # sentinel class for catching the apply error to return NotImplemented - pass - - -class CacheableOffset(object): - _cacheable = True - - class DateOffset(object): """ Standard kind of date increment used for a date range. @@ -697,28 +683,11 @@ class BusinessHourMixin(BusinessMixin): def __init__(self, start='09:00', end='17:00', offset=timedelta(0)): # must be validated here to equality check kwds = {'offset': offset} - self.start = kwds['start'] = self._validate_time(start) - self.end = kwds['end'] = self._validate_time(end) + self.start = kwds['start'] = _validate_business_time(start) + self.end = kwds['end'] = _validate_business_time(end) self.kwds = kwds self._offset = offset - def _validate_time(self, t_input): - from datetime import time as dt_time - import time - if isinstance(t_input, compat.string_types): - try: - t = time.strptime(t_input, '%H:%M') - return dt_time(hour=t.tm_hour, minute=t.tm_min) - except ValueError: - 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") - return t_input - else: - raise ValueError("time data must be string or datetime.time") - def _get_daytime_flag(self): if self.start == self.end: raise ValueError('start and end must not be the same') @@ -1617,29 +1586,6 @@ def _from_name(cls, suffix=None): return cls(weekday=weekday) -class WeekDay(object): - MON = 0 - TUE = 1 - WED = 2 - THU = 3 - FRI = 4 - SAT = 5 - SUN = 6 - - -_int_to_weekday = { - WeekDay.MON: 'MON', - WeekDay.TUE: 'TUE', - WeekDay.WED: 'WED', - WeekDay.THU: 'THU', - WeekDay.FRI: 'FRI', - WeekDay.SAT: 'SAT', - WeekDay.SUN: 'SUN' -} - -_weekday_to_int = dict((v, k) for k, v in _int_to_weekday.items()) - - class WeekOfMonth(DateOffset): """ Describes monthly dates like "the Tuesday of the 2nd week of each month" @@ -2802,9 +2748,6 @@ def _delta_to_tick(delta): return Nano(nanos) -_delta_to_nanoseconds = tslib._delta_to_nanoseconds - - class Day(Tick): _inc = Timedelta(days=1) _prefix = 'D' @@ -2848,66 +2791,6 @@ class Nano(Tick): CDay = CustomBusinessDay # --------------------------------------------------------------------- -# Business Calendar helpers - - -def _get_calendar(weekmask, holidays, calendar): - """Generate busdaycalendar""" - if isinstance(calendar, np.busdaycalendar): - if not holidays: - holidays = tuple(calendar.holidays) - elif not isinstance(holidays, tuple): - holidays = tuple(holidays) - else: - # trust that calendar.holidays and holidays are - # consistent - pass - return calendar, holidays - - if holidays is None: - holidays = [] - try: - holidays = holidays + calendar.holidays().tolist() - except AttributeError: - pass - holidays = [_to_dt64(dt, dtype='datetime64[D]') for dt in holidays] - holidays = tuple(sorted(holidays)) - - kwargs = {'weekmask': weekmask} - if holidays: - kwargs['holidays'] = holidays - - busdaycalendar = np.busdaycalendar(**kwargs) - return busdaycalendar, holidays - - -def _to_dt64(dt, dtype='datetime64'): - # Currently - # > np.datetime64(dt.datetime(2013,5,1),dtype='datetime64[D]') - # numpy.datetime64('2013-05-01T02:00:00.000000+0200') - # Thus astype is needed to cast datetime to datetime64[D] - if getattr(dt, 'tzinfo', None) is not None: - i8 = tslib.pydt_to_i8(dt) - dt = tslib.tz_convert_single(i8, 'UTC', dt.tzinfo) - dt = Timestamp(dt) - dt = np.datetime64(dt) - if dt.dtype.name != dtype: - dt = dt.astype(dtype) - return dt - - -def _get_firstbday(wkday): - """ - wkday is the result of monthrange(year, month) - - If it's a saturday or sunday, increment first business day to reflect this - """ - first = 1 - if wkday == 5: # on Saturday - first = 3 - elif wkday == 6: # on Sunday - first = 2 - return first def generate_range(start=None, end=None, periods=None, diff --git a/setup.py b/setup.py index 365d387dc54d6..e8d966a126174 100755 --- a/setup.py +++ b/setup.py @@ -344,6 +344,7 @@ class CheckSDist(sdist_class): 'pandas/_libs/tslibs/strptime.pyx', 'pandas/_libs/tslibs/timezones.pyx', 'pandas/_libs/tslibs/fields.pyx', + 'pandas/_libs/tslibs/offsets.pyx', 'pandas/_libs/tslibs/frequencies.pyx', 'pandas/_libs/tslibs/parsing.pyx', 'pandas/io/sas/sas.pyx'] @@ -465,6 +466,8 @@ def pxd(name): tseries_depends = ['pandas/_libs/src/datetime/np_datetime.h', 'pandas/_libs/src/datetime/np_datetime_strings.h', 'pandas/_libs/src/datetime.pxd'] +dtstrings = ['pandas/_libs/src/datetime/np_datetime.c', + 'pandas/_libs/src/datetime/np_datetime_strings.c'] # some linux distros require it libraries = ['m'] if not is_platform_windows() else [] @@ -479,31 +482,27 @@ def pxd(name): + _pxi_dep['hashtable'])}, '_libs.tslibs.strptime': {'pyxfile': '_libs/tslibs/strptime', 'depends': tseries_depends, - 'sources': ['pandas/_libs/src/datetime/np_datetime.c', - 'pandas/_libs/src/datetime/np_datetime_strings.c']}, + 'sources': dtstrings}, '_libs.tslib': {'pyxfile': '_libs/tslib', 'pxdfiles': ['_libs/src/util', '_libs/lib'], 'depends': tseries_depends, - 'sources': ['pandas/_libs/src/datetime/np_datetime.c', - 'pandas/_libs/src/datetime/np_datetime_strings.c']}, + 'sources': dtstrings}, + '_libs.tslibs.offsets': {'pyxfile': '_libs/tslibs/offsets'}, '_libs.tslibs.timezones': {'pyxfile': '_libs/tslibs/timezones'}, '_libs.tslibs.fields': {'pyxfile': '_libs/tslibs/fields', 'depends': tseries_depends, - 'sources': ['pandas/_libs/src/datetime/np_datetime.c', - 'pandas/_libs/src/datetime/np_datetime_strings.c']}, + 'sources': dtstrings}, '_libs.period': {'pyxfile': '_libs/period', 'depends': (tseries_depends + ['pandas/_libs/src/period_helper.h']), - 'sources': ['pandas/_libs/src/datetime/np_datetime.c', - 'pandas/_libs/src/datetime/np_datetime_strings.c', + 'sources': dtstrings + [ 'pandas/_libs/src/period_helper.c']}, '_libs.tslibs.parsing': {'pyxfile': '_libs/tslibs/parsing', 'pxdfiles': ['_libs/src/util']}, '_libs.tslibs.frequencies': {'pyxfile': '_libs/tslibs/frequencies', 'pxdfiles': ['_libs/src/util']}, '_libs.index': {'pyxfile': '_libs/index', - 'sources': ['pandas/_libs/src/datetime/np_datetime.c', - 'pandas/_libs/src/datetime/np_datetime_strings.c'], + 'sources': dtstrings, 'pxdfiles': ['_libs/src/util', '_libs/hashtable'], 'depends': _pxi_dep['index']}, '_libs.algos': {'pyxfile': '_libs/algos', From 9c2bf8feef6be4d4bad31967aee9d633fc88bce1 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 9 Oct 2017 18:28:44 -0700 Subject: [PATCH 2/7] flake8 fixup --- pandas/_libs/tslibs/offsets.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 2cac7f023ac37..9959b053707c7 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -25,6 +25,7 @@ _MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', _int_to_month = {(k + 1): v for k, v in enumerate(_MONTHS)} _month_to_int = dict((v, k) for k, v in _int_to_month.items()) + class WeekDay(object): MON = 0 TUE = 1 @@ -74,8 +75,7 @@ _offset_to_period_map = { 'Y': 'A', 'BY': 'A', 'YS': 'A', - 'BYS': 'A' - } + 'BYS': 'A'} need_suffix = ['QS', 'BQ', 'BQS', 'YS', 'AS', 'BY', 'BA', 'BYS', 'BAS'] @@ -169,7 +169,7 @@ def _to_dt64(dt, dtype='datetime64'): dt = tz_convert_single(i8, 'UTC', dt.tzinfo) dt = np.int64(dt).astype('datetime64[ns]') else: - dt = np.datetime64(dt) + dt = np.datetime64(dt) if dt.dtype.name != dtype: dt = dt.astype(dtype) return dt @@ -197,6 +197,7 @@ def _validate_business_time(t_input): # --------------------------------------------------------------------- # Mixins & Singletons + class ApplyTypeError(TypeError): # sentinel class for catching the apply error to return NotImplemented pass From 84597a02863da7f51996030f5983b9bdc4a94e9f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 9 Oct 2017 18:31:32 -0700 Subject: [PATCH 3/7] fixup remove undefined --- pandas/tseries/offsets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index f9d90a0afab39..f42a3525776b5 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -15,9 +15,8 @@ from pandas.util._decorators import cache_readonly from pandas._libs.tslib import _delta_to_nanoseconds -from pandas._libs.tslibs.offsets import ( +from pandas._libs.tslibs.offsets import ( # noqa ApplyTypeError, CacheableOffset, - _determine_offset, as_datetime, _is_normalized, _get_firstbday, _get_calendar, _to_dt64, _validate_business_time, _int_to_weekday, _weekday_to_int, WeekDay) From 0a9d16f882c184d4be68496e587c1e02cf587c74 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 27 Oct 2017 16:25:50 -0700 Subject: [PATCH 4/7] update imports --- pandas/tests/tseries/test_offsets.py | 5 +++-- pandas/tseries/offsets.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/tests/tseries/test_offsets.py b/pandas/tests/tseries/test_offsets.py index c0e682c978610..4b3edeec0ade5 100644 --- a/pandas/tests/tseries/test_offsets.py +++ b/pandas/tests/tseries/test_offsets.py @@ -17,9 +17,10 @@ get_offset, get_standard_freq) from pandas.core.indexes.datetimes import ( _to_m8, DatetimeIndex, _daterange_cache) +from pandas._libs.tseries offsets import WeekDay, CacheableOffset from pandas.tseries.offsets import (BDay, CDay, BQuarterEnd, BMonthEnd, BusinessHour, WeekOfMonth, CBMonthEnd, - CustomBusinessHour, WeekDay, + CustomBusinessHour, CBMonthBegin, BYearEnd, MonthEnd, MonthBegin, SemiMonthBegin, SemiMonthEnd, BYearBegin, QuarterBegin, BQuarterBegin, @@ -27,7 +28,7 @@ YearEnd, Hour, Minute, Second, Day, Micro, QuarterEnd, BusinessMonthEnd, FY5253, Milli, Nano, Easter, FY5253Quarter, - LastWeekOfMonth, CacheableOffset) + LastWeekOfMonth) from pandas.core.tools.datetimes import ( format, ole2datetime, parse_time_string, to_datetime, DateParseError) diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py index f42a3525776b5..88c878c9cb9a6 100644 --- a/pandas/tseries/offsets.py +++ b/pandas/tseries/offsets.py @@ -15,11 +15,11 @@ from pandas.util._decorators import cache_readonly from pandas._libs.tslib import _delta_to_nanoseconds -from pandas._libs.tslibs.offsets import ( # noqa - ApplyTypeError, CacheableOffset, +from pandas._libs.tslibs.offsets import ( + ApplyTypeError, as_datetime, _is_normalized, _get_firstbday, _get_calendar, _to_dt64, _validate_business_time, - _int_to_weekday, _weekday_to_int, WeekDay) + _int_to_weekday, _weekday_to_int) import functools import operator From 92d7e2901a94d42167df039b55d1384350c6ecc2 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 27 Oct 2017 21:46:33 -0700 Subject: [PATCH 5/7] typo fixup --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 240fc467ce84b..8b3ae40f01a10 100755 --- a/setup.py +++ b/setup.py @@ -491,7 +491,6 @@ def pxd(name): 'depends': tseries_depends, 'sources': npdt_srces}, '_libs.tslibs.offsets': {'pyxfile': '_libs/tslibs/offsets'}, - 'sources': npdt_srces}, '_libs.tslib': {'pyxfile': '_libs/tslib', 'pxdfiles': ['_libs/src/util', '_libs/lib'], 'depends': tseries_depends, From 167bebd912c4fa98bb54203460956de5b4d9d431 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 27 Oct 2017 23:03:33 -0700 Subject: [PATCH 6/7] whatsnew note --- doc/source/whatsnew/v0.22.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index e85ba505887b4..cbd094ec4ef49 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -42,7 +42,7 @@ Other API Changes - ``NaT`` division with :class:`datetime.timedelta` will now return ``NaN`` instead of raising (:issue:`17876`) - :class:`Timestamp` will no longer silently ignore unused or invalid `tz` or `tzinfo` arguments (:issue:`17690`) -- +- :class:`CacheableOffset` and :class:`WeekDay` are no longer available in the `tseries.offsets` module (:issue:`17830`) - .. _whatsnew_0220.deprecations: From ced2c8d24b3d5f2fbbb0d20843b1007e6ca49e40 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Fri, 27 Oct 2017 23:04:41 -0700 Subject: [PATCH 7/7] typo fixup ImportError --- pandas/tests/tseries/test_offsets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/tseries/test_offsets.py b/pandas/tests/tseries/test_offsets.py index 4b3edeec0ade5..4fd3bba01602f 100644 --- a/pandas/tests/tseries/test_offsets.py +++ b/pandas/tests/tseries/test_offsets.py @@ -17,7 +17,7 @@ get_offset, get_standard_freq) from pandas.core.indexes.datetimes import ( _to_m8, DatetimeIndex, _daterange_cache) -from pandas._libs.tseries offsets import WeekDay, CacheableOffset +from pandas._libs.tslibs.offsets import WeekDay, CacheableOffset from pandas.tseries.offsets import (BDay, CDay, BQuarterEnd, BMonthEnd, BusinessHour, WeekOfMonth, CBMonthEnd, CustomBusinessHour,