Skip to content

REF: put cast_from_unit in conversion to de-circularize cimports #34287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@ from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime

from pandas._libs.tslibs.parsing import parse_datetime_string

from pandas._libs.tslibs.timedeltas cimport cast_from_unit
from pandas._libs.tslibs.timezones cimport (
get_dst_info,
is_utc,
is_tzlocal,
utc_pytz as UTC,
)
from pandas._libs.tslibs.conversion cimport (
_TSObject, convert_datetime_to_tsobject,
get_datetime64_nanos)
_TSObject,
cast_from_unit,
convert_datetime_to_tsobject,
get_datetime64_nanos,
)

from pandas._libs.tslibs.nattype cimport (
NPY_NAT,
Expand Down
1 change: 1 addition & 0 deletions pandas/_libs/tslibs/conversion.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
cdef int64_t get_datetime64_nanos(object val) except? -1

cpdef datetime localize_pydatetime(datetime dt, object tz)
cdef int64_t cast_from_unit(object ts, str unit) except? -1
77 changes: 75 additions & 2 deletions pandas/_libs/tslibs/conversion.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime
from pandas._libs.tslibs.util cimport (
is_datetime64_object, is_integer_object, is_float_object)

from pandas._libs.tslibs.timedeltas cimport cast_from_unit
from pandas._libs.tslibs.timezones cimport (
is_utc, is_tzlocal, is_fixed_offset, get_utcoffset, get_dst_info,
get_timezone, maybe_get_tz, tz_compare,
Expand Down Expand Up @@ -55,7 +54,78 @@ TD64NS_DTYPE = np.dtype('m8[ns]')


# ----------------------------------------------------------------------
# Misc Helpers
# Unit Conversion Helpers

cdef inline int64_t cast_from_unit(object ts, str unit) except? -1:
""" return a casting of the unit represented to nanoseconds
round the fractional part of a float to our precision, p """
cdef:
int64_t m
int p

m, p = precision_from_unit(unit)

# just give me the unit back
if ts is None:
return m

# cast the unit, multiply base/frace separately
# to avoid precision issues from float -> int
base = <int64_t>ts
frac = ts - base
if p:
frac = round(frac, p)
return <int64_t>(base * m) + <int64_t>(frac * m)


cpdef inline object precision_from_unit(str unit):
"""
Return a casting of the unit represented to nanoseconds + the precision
to round the fractional part.

Notes
-----
The caller is responsible for ensuring that the default value of "ns"
takes the place of None.
"""
cdef:
int64_t m
int p

if unit == "Y":
m = 1_000_000_000 * 31556952
p = 9
elif unit == "M":
m = 1_000_000_000 * 2629746
p = 9
elif unit == "W":
m = 1_000_000_000 * 3600 * 24 * 7
p = 9
elif unit == "D" or unit == "d":
m = 1_000_000_000 * 3600 * 24
p = 9
elif unit == "h":
m = 1_000_000_000 * 3600
p = 9
elif unit == "m":
m = 1_000_000_000 * 60
p = 9
elif unit == "s":
m = 1_000_000_000
p = 9
elif unit == "ms":
m = 1_000_000
p = 6
elif unit == "us":
m = 1000
p = 3
elif unit == "ns" or unit is None:
m = 1
p = 0
else:
raise ValueError(f"cannot cast unit {unit}")
return m, p


cdef inline int64_t get_datetime64_nanos(object val) except? -1:
"""
Expand Down Expand Up @@ -155,6 +225,9 @@ def ensure_timedelta64ns(arr: ndarray, copy: bool=True):
# TODO: check for overflows when going from a lower-resolution to nanos


# ----------------------------------------------------------------------


@cython.boundscheck(False)
@cython.wraparound(False)
def datetime_to_datetime64(ndarray[object] values):
Expand Down
1 change: 0 additions & 1 deletion pandas/_libs/tslibs/timedeltas.pxd
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from numpy cimport int64_t

# Exposed for tslib, not intended for outside use.
cdef int64_t cast_from_unit(object ts, str unit) except? -1
cpdef int64_t delta_to_nanoseconds(delta) except? -1
cdef convert_to_timedelta64(object ts, object unit)
73 changes: 1 addition & 72 deletions pandas/_libs/tslibs/timedeltas.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ from pandas._libs.tslibs.util cimport (

from pandas._libs.tslibs.base cimport ABCTimedelta, ABCTimestamp

from pandas._libs.tslibs.ccalendar cimport DAY_NANOS
from pandas._libs.tslibs.conversion cimport cast_from_unit

from pandas._libs.tslibs.np_datetime cimport (
cmp_scalar, td64_to_tdstruct, pandas_timedeltastruct)
Expand Down Expand Up @@ -260,77 +260,6 @@ def array_to_timedelta64(object[:] values, unit='ns', errors='raise'):
return iresult.base # .base to access underlying np.ndarray


cpdef inline object precision_from_unit(str unit):
"""
Return a casting of the unit represented to nanoseconds + the precision
to round the fractional part.

Notes
-----
The caller is responsible for ensuring that the default value of "ns"
takes the place of None.
"""
cdef:
int64_t m
int p

if unit == 'Y':
m = 1000000000 * 31556952
p = 9
elif unit == 'M':
m = 1000000000 * 2629746
p = 9
elif unit == 'W':
m = DAY_NANOS * 7
p = 9
elif unit == 'D' or unit == 'd':
m = DAY_NANOS
p = 9
elif unit == 'h':
m = 1000000000 * 3600
p = 9
elif unit == 'm':
m = 1000000000 * 60
p = 9
elif unit == 's':
m = 1000000000
p = 9
elif unit == 'ms':
m = 1000000
p = 6
elif unit == 'us':
m = 1000
p = 3
elif unit == 'ns' or unit is None:
m = 1
p = 0
else:
raise ValueError(f"cannot cast unit {unit}")
return m, p


cdef inline int64_t cast_from_unit(object ts, str unit) except? -1:
""" return a casting of the unit represented to nanoseconds
round the fractional part of a float to our precision, p """
cdef:
int64_t m
int p

m, p = precision_from_unit(unit)

# just give me the unit back
if ts is None:
return m

# cast the unit, multiply base/frace separately
# to avoid precision issues from float -> int
base = <int64_t>ts
frac = ts - base
if p:
frac = round(frac, p)
return <int64_t>(base * m) + <int64_t>(frac * m)


cdef inline int64_t parse_timedelta_string(str ts) except? -1:
"""
Parse a regular format timedelta string. Return an int64_t (in ns)
Expand Down
7 changes: 2 additions & 5 deletions pandas/core/arrays/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@

from pandas._libs import lib, tslibs
from pandas._libs.tslibs import NaT, Period, Timedelta, Timestamp, iNaT
from pandas._libs.tslibs.conversion import precision_from_unit
from pandas._libs.tslibs.fields import get_timedelta_field
from pandas._libs.tslibs.timedeltas import (
array_to_timedelta64,
parse_timedelta_unit,
precision_from_unit,
)
from pandas._libs.tslibs.timedeltas import array_to_timedelta64, parse_timedelta_unit
from pandas.compat.numpy import function as nv

from pandas.core.dtypes.common import (
Expand Down