From a803ca4edfc85a5769d44b162a7d2e0f70198afc Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 4 Jul 2018 14:28:22 -0700 Subject: [PATCH 1/4] move ints_to_pytimedelta, normalize_date, fixup imports and tests --- pandas/_libs/tslib.pyx | 53 +------------------- pandas/_libs/tslibs/__init__.py | 8 ++- pandas/_libs/tslibs/conversion.pyx | 29 +++++++++++ pandas/_libs/tslibs/timedeltas.pyx | 37 ++++++++++++++ pandas/_libs/tslibs/timestamps.pyx | 1 - pandas/core/generic.py | 2 - pandas/tests/tseries/offsets/test_offsets.py | 7 +-- pandas/tests/tslibs/test_tslib.py | 5 ++ 8 files changed, 80 insertions(+), 62 deletions(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 61cf582300034..db3a47bc32fda 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -35,11 +35,10 @@ from cython cimport Py_ssize_t import pytz -UTC = pytz.utc from tslibs.timedeltas cimport cast_from_unit -from tslibs.timedeltas import Timedelta +from tslibs.timedeltas import Timedelta, ints_to_pytimedelta # noqa:F841 from tslibs.timezones cimport (is_utc, is_tzlocal, is_fixed_offset, treat_tz_as_pytz, get_dst_info) from tslibs.conversion cimport (tz_convert_single, _TSObject, @@ -185,29 +184,6 @@ def ints_to_pydatetime(ndarray[int64_t] arr, tz=None, freq=None, return result -def ints_to_pytimedelta(ndarray[int64_t] arr, box=False): - # convert an i8 repr to an ndarray of timedelta or Timedelta (if box == - # True) - - cdef: - Py_ssize_t i, n = len(arr) - int64_t value - ndarray[object] result = np.empty(n, dtype=object) - - for i in range(n): - - value = arr[i] - if value == NPY_NAT: - result[i] = NaT - else: - if box: - result[i] = Timedelta(value) - else: - result[i] = timedelta(microseconds=int(value) / 1000) - - return result - - def _test_parse_iso8601(object ts): """ TESTING ONLY: Parse string into Timestamp using iso8601 parser. Used @@ -740,30 +716,3 @@ cdef inline bint _parse_today_now(str val, int64_t* iresult): iresult[0] = Timestamp.today().value return True return False - -# ---------------------------------------------------------------------- -# Some general helper functions - - -cpdef normalize_date(object dt): - """ - Normalize datetime.datetime value to midnight. Returns datetime.date as a - datetime.datetime at midnight - - Returns - ------- - normalized : datetime.datetime or Timestamp - """ - if PyDateTime_Check(dt): - if not PyDateTime_CheckExact(dt): - # i.e. a Timestamp object - return dt.replace(hour=0, minute=0, second=0, microsecond=0, - nanosecond=0) - else: - # regular datetime object - return dt.replace(hour=0, minute=0, second=0, microsecond=0) - # TODO: Make sure DST crossing is handled correctly here - elif PyDate_Check(dt): - return datetime(dt.year, dt.month, dt.day) - else: - raise TypeError('Unrecognized type: %s' % type(dt)) diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index f3aa0424f0376..64767b2e7a7bc 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -1,2 +1,8 @@ # -*- coding: utf-8 -*- -# cython: profile=False +# flake8: noqa + +from conversion import normalize_date, localize_pydatetime, tz_convert_single +from nattype import NaT, iNaT +from np_datetime import OutOfBoundsDatetime +from timestamps import Timestamp +from timedeltas import delta_to_nanoseconds, ints_to_pytimedelta, Timedelta diff --git a/pandas/_libs/tslibs/conversion.pyx b/pandas/_libs/tslibs/conversion.pyx index f825a4c8ed2c1..fd4b0b11065ed 100644 --- a/pandas/_libs/tslibs/conversion.pyx +++ b/pandas/_libs/tslibs/conversion.pyx @@ -1025,6 +1025,35 @@ cdef inline str _render_tstamp(int64_t val): # ---------------------------------------------------------------------- # Normalization + +def normalize_date(object dt): + """ + Normalize datetime.datetime value to midnight. Returns datetime.date as a + datetime.datetime at midnight + + Parameters + ---------- + dt : date, datetime, or Timestamp + + Returns + ------- + normalized : datetime.datetime or Timestamp + """ + if PyDateTime_Check(dt): + if not PyDateTime_CheckExact(dt): + # i.e. a Timestamp object + return dt.replace(hour=0, minute=0, second=0, microsecond=0, + nanosecond=0) + else: + # regular datetime object + return dt.replace(hour=0, minute=0, second=0, microsecond=0) + # TODO: Make sure DST crossing is handled correctly here + elif PyDate_Check(dt): + return datetime(dt.year, dt.month, dt.day) + else: + raise TypeError('Unrecognized type: %s' % type(dt)) + + @cython.wraparound(False) @cython.boundscheck(False) def date_normalize(ndarray[int64_t] stamps, tz=None): diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 27066af0be3b9..02430e6758dfe 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -79,6 +79,43 @@ cdef dict timedelta_abbrevs = { 'D': 'd', _no_input = object() +# ---------------------------------------------------------------------- +# API + +def ints_to_pytimedelta(ndarray[int64_t] arr, box=False): + """ + convert an i8 repr to an ndarray of timedelta or Timedelta (if box == + True) + + Parameters + ---------- + arr : ndarray[int64_t] + box : bool, default False + + Returns + ------- + result : ndarray[object] + array of Timedelta or timedeltas objects + """ + cdef: + Py_ssize_t i, n = len(arr) + int64_t value + ndarray[object] result = np.empty(n, dtype=object) + + for i in range(n): + + value = arr[i] + if value == NPY_NAT: + result[i] = NaT + else: + if box: + result[i] = Timedelta(value) + else: + result[i] = timedelta(microseconds=int(value) / 1000) + + return result + + # ---------------------------------------------------------------------- cpdef int64_t delta_to_nanoseconds(delta) except? -1: diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 54d29cea44555..401ba76e341b2 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -645,7 +645,6 @@ class Timestamp(_Timestamp): return NaT if is_string_object(freq): - from pandas.tseries.frequencies import to_offset freq = to_offset(freq) return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, freq) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 26c23b84a9c04..818dd1b408518 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7156,7 +7156,6 @@ def first(self, offset): at_time : Select values at a particular time of the day between_time : Select values between particular times of the day """ - from pandas.tseries.frequencies import to_offset if not isinstance(self.index, DatetimeIndex): raise TypeError("'first' only supports a DatetimeIndex index") @@ -7220,7 +7219,6 @@ def last(self, offset): at_time : Select values at a particular time of the day between_time : Select values between particular times of the day """ - from pandas.tseries.frequencies import to_offset if not isinstance(self.index, DatetimeIndex): raise TypeError("'last' only supports a DatetimeIndex index") diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 0c5470e7bd932..a5cd839c1472f 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -33,7 +33,7 @@ import pandas.tseries.offsets as offsets from pandas.io.pickle import read_pickle from pandas._libs.tslibs import timezones -from pandas._libs.tslib import normalize_date, NaT, Timestamp +from pandas._libs.tslib import NaT, Timestamp import pandas._libs.tslib as tslib import pandas.util.testing as tm from pandas.tseries.holiday import USFederalHolidayCalendar @@ -59,11 +59,6 @@ def test_ole2datetime(): ole2datetime(60) -def test_normalize_date(): - actual = normalize_date(datetime(2007, 10, 1, 1, 12, 5, 10)) - assert actual == datetime(2007, 10, 1) - - def test_to_m8(): valb = datetime(2007, 10, 1) valu = _to_m8(valb) diff --git a/pandas/tests/tslibs/test_tslib.py b/pandas/tests/tslibs/test_tslib.py index 2641c016e8674..484ec71cdc4c1 100644 --- a/pandas/tests/tslibs/test_tslib.py +++ b/pandas/tests/tslibs/test_tslib.py @@ -16,3 +16,8 @@ def test_normalize_date(): result = tslib.normalize_date(value) assert (result == datetime(2012, 9, 7)) + + value = datetime(2007, 10, 1, 1, 12, 5, 10) + + actual = tslib.normalize_date(value) + assert actual == datetime(2007, 10, 1) From feae5f332035005f7604d83ffcd54baa1e7ed8da Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 4 Jul 2018 16:08:22 -0700 Subject: [PATCH 2/4] extra import --- pandas/_libs/tslib.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index db3a47bc32fda..c1a0e58a4fb1a 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -45,7 +45,7 @@ from tslibs.conversion cimport (tz_convert_single, _TSObject, convert_datetime_to_tsobject, get_datetime64_nanos, tz_convert_utc_to_tzlocal) -from tslibs.conversion import tz_convert_single +from tslibs.conversion import tz_convert_single, normalize_date # noqa:F841 from tslibs.nattype import NaT, nat_strings, iNaT from tslibs.nattype cimport checknull_with_nat, NPY_NAT From 6a9afa4c6bac4047d069169473973267d6ea3829 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 4 Jul 2018 17:48:14 -0700 Subject: [PATCH 3/4] make imports explicit --- pandas/_libs/tslibs/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/__init__.py b/pandas/_libs/tslibs/__init__.py index 64767b2e7a7bc..22307f70ebe52 100644 --- a/pandas/_libs/tslibs/__init__.py +++ b/pandas/_libs/tslibs/__init__.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # flake8: noqa -from conversion import normalize_date, localize_pydatetime, tz_convert_single -from nattype import NaT, iNaT -from np_datetime import OutOfBoundsDatetime -from timestamps import Timestamp -from timedeltas import delta_to_nanoseconds, ints_to_pytimedelta, Timedelta +from .conversion import normalize_date, localize_pydatetime, tz_convert_single +from .nattype import NaT, iNaT +from .np_datetime import OutOfBoundsDatetime +from .timestamps import Timestamp +from .timedeltas import delta_to_nanoseconds, ints_to_pytimedelta, Timedelta From 94239a00587e4cd73fd022eb46650b71d2771e4a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 4 Jul 2018 19:12:07 -0700 Subject: [PATCH 4/4] whitespace fixup --- pandas/_libs/tslibs/timedeltas.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 02430e6758dfe..76849f2116123 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -79,6 +79,7 @@ cdef dict timedelta_abbrevs = { 'D': 'd', _no_input = object() + # ---------------------------------------------------------------------- # API