Skip to content

CLN: tighter typing in libconversion #35088

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 2 commits into from
Jul 2, 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
4 changes: 2 additions & 2 deletions pandas/_libs/tslibs/conversion.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ cdef class _TSObject:
bint fold


cdef convert_to_tsobject(object ts, object tz, object unit,
cdef convert_to_tsobject(object ts, tzinfo tz, object unit,
bint dayfirst, bint yearfirst,
int32_t nanos=*)

cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
int32_t nanos=*)

cdef int64_t get_datetime64_nanos(object val) except? -1
Expand Down
36 changes: 14 additions & 22 deletions pandas/_libs/tslibs/conversion.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ cdef class _TSObject:
return self.value


cdef convert_to_tsobject(object ts, object tz, object unit,
cdef convert_to_tsobject(object ts, tzinfo tz, object unit,
bint dayfirst, bint yearfirst, int32_t nanos=0):
"""
Extract datetime and int64 from any of:
Expand All @@ -325,13 +325,10 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
cdef:
_TSObject obj

if tz is not None:
tz = maybe_get_tz(tz)

obj = _TSObject()

if isinstance(ts, str):
return convert_str_to_tsobject(ts, tz, unit, dayfirst, yearfirst)
return _convert_str_to_tsobject(ts, tz, unit, dayfirst, yearfirst)

if ts is None or ts is NaT:
obj.value = NPY_NAT
Expand Down Expand Up @@ -373,16 +370,16 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
f'Timestamp')

if tz is not None:
localize_tso(obj, tz)
_localize_tso(obj, tz)

if obj.value != NPY_NAT:
# check_overflows needs to run after localize_tso
# check_overflows needs to run after _localize_tso
check_dts_bounds(&obj.dts)
check_overflows(obj)
return obj


cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
int32_t nanos=0):
"""
Convert a datetime (or Timestamp) input `ts`, along with optional timezone
Expand Down Expand Up @@ -445,8 +442,8 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
return obj


cdef _TSObject create_tsobject_tz_using_offset(npy_datetimestruct dts,
int tzoffset, tzinfo tz=None):
cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
int tzoffset, tzinfo tz=None):
"""
Convert a datetimestruct `dts`, along with initial timezone offset
`tzoffset` to a _TSObject (with timezone object `tz` - optional).
Expand Down Expand Up @@ -499,9 +496,9 @@ cdef _TSObject create_tsobject_tz_using_offset(npy_datetimestruct dts,
return obj


cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
bint dayfirst=False,
bint yearfirst=False):
cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, object unit,
bint dayfirst=False,
bint yearfirst=False):
"""
Convert a string input `ts`, along with optional timezone object`tz`
to a _TSObject.
Expand Down Expand Up @@ -531,11 +528,6 @@ cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
int out_local = 0, out_tzoffset = 0
bint do_parse_datetime_string = False

if tz is not None:
tz = maybe_get_tz(tz)

assert isinstance(ts, str)

if len(ts) == 0 or ts in nat_strings:
ts = NaT
elif ts == 'now':
Expand All @@ -556,12 +548,12 @@ cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
if not string_to_dts_failed:
check_dts_bounds(&dts)
if out_local == 1:
return create_tsobject_tz_using_offset(dts,
out_tzoffset, tz)
return _create_tsobject_tz_using_offset(dts,
out_tzoffset, tz)
else:
ts = dtstruct_to_dt64(&dts)
if tz is not None:
# shift for localize_tso
# shift for _localize_tso
ts = tz_localize_to_utc(np.array([ts], dtype='i8'), tz,
ambiguous='raise')[0]

Expand Down Expand Up @@ -612,7 +604,7 @@ cdef inline check_overflows(_TSObject obj):
# ----------------------------------------------------------------------
# Localization

cdef inline void localize_tso(_TSObject obj, tzinfo tz):
cdef inline void _localize_tso(_TSObject obj, tzinfo tz):
"""
Given the UTC nanosecond timestamp in obj.value, find the wall-clock
representation of that timestamp in the given timezone.
Expand Down
50 changes: 26 additions & 24 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ from cpython.datetime cimport (
datetime,
time,
tzinfo,
tzinfo as tzinfo_type, # alias bc `tzinfo` is a kwarg below
PyDateTime_Check,
PyDelta_Check,
PyTZInfo_Check,
Expand Down Expand Up @@ -932,7 +933,7 @@ class Timestamp(_Timestamp):
second=None,
microsecond=None,
nanosecond=None,
tzinfo=None,
tzinfo_type tzinfo=None,
*,
fold=None
):
Expand All @@ -957,18 +958,17 @@ class Timestamp(_Timestamp):
#
# Mixing pydatetime positional and keyword arguments is forbidden!

cdef _TSObject ts
cdef:
_TSObject ts
tzinfo_type tzobj

_date_attributes = [year, month, day, hour, minute, second,
microsecond, nanosecond]

if tzinfo is not None:
if not PyTZInfo_Check(tzinfo):
# tzinfo must be a datetime.tzinfo object, GH#17690
raise TypeError(
f"tzinfo must be a datetime.tzinfo object, not {type(tzinfo)}"
)
elif tz is not None:
# GH#17690 tzinfo must be a datetime.tzinfo object, ensured
# by the cython annotation.
if tz is not None:
raise ValueError('Can provide at most one of tz, tzinfo')

# User passed tzinfo instead of tz; avoid silently ignoring
Expand Down Expand Up @@ -1055,7 +1055,8 @@ class Timestamp(_Timestamp):
raise ValueError("Cannot pass a datetime or Timestamp with tzinfo with "
"the tz parameter. Use tz_convert instead.")

ts = convert_to_tsobject(ts_input, tz, unit, 0, 0, nanosecond or 0)
tzobj = maybe_get_tz(tz)
ts = convert_to_tsobject(ts_input, tzobj, unit, 0, 0, nanosecond or 0)

if ts.value == NPY_NAT:
return NaT
Expand Down Expand Up @@ -1378,15 +1379,16 @@ default 'raise'

cdef:
npy_datetimestruct dts
int64_t value, value_tz, offset
object _tzinfo, result, k, v
int64_t value, value_tz
object k, v
datetime ts_input
tzinfo_type tzobj

# set to naive if needed
_tzinfo = self.tzinfo
tzobj = self.tzinfo
value = self.value
if _tzinfo is not None:
value_tz = tz_convert_single(value, _tzinfo, UTC)
if tzobj is not None:
value_tz = tz_convert_single(value, tzobj, UTC)
value += value - value_tz

# setup components
Expand Down Expand Up @@ -1419,30 +1421,30 @@ default 'raise'
if nanosecond is not None:
dts.ps = validate('nanosecond', nanosecond) * 1000
if tzinfo is not object:
_tzinfo = tzinfo
tzobj = tzinfo

# reconstruct & check bounds
if _tzinfo is not None and treat_tz_as_pytz(_tzinfo):
if tzobj is not None and treat_tz_as_pytz(tzobj):
# replacing across a DST boundary may induce a new tzinfo object
# see GH#18319
ts_input = _tzinfo.localize(datetime(dts.year, dts.month, dts.day,
dts.hour, dts.min, dts.sec,
dts.us),
is_dst=not bool(fold))
_tzinfo = ts_input.tzinfo
ts_input = tzobj.localize(datetime(dts.year, dts.month, dts.day,
dts.hour, dts.min, dts.sec,
dts.us),
is_dst=not bool(fold))
tzobj = ts_input.tzinfo
else:
kwargs = {'year': dts.year, 'month': dts.month, 'day': dts.day,
'hour': dts.hour, 'minute': dts.min, 'second': dts.sec,
'microsecond': dts.us, 'tzinfo': _tzinfo,
'microsecond': dts.us, 'tzinfo': tzobj,
'fold': fold}
ts_input = datetime(**kwargs)

ts = convert_datetime_to_tsobject(ts_input, _tzinfo)
ts = convert_datetime_to_tsobject(ts_input, tzobj)
value = ts.value + (dts.ps // 1000)
if value != NPY_NAT:
check_dts_bounds(&dts)

return create_timestamp_from_ts(value, dts, _tzinfo, self.freq, fold)
return create_timestamp_from_ts(value, dts, tzobj, self.freq, fold)

def to_julian_date(self) -> np.float64:
"""
Expand Down
5 changes: 4 additions & 1 deletion pandas/tests/scalar/timestamp/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ def test_constructor_invalid(self):

def test_constructor_invalid_tz(self):
# GH#17690
msg = "must be a datetime.tzinfo"
msg = (
"Argument 'tzinfo' has incorrect type "
r"\(expected datetime.tzinfo, got str\)"
)
with pytest.raises(TypeError, match=msg):
Timestamp("2017-10-22", tzinfo="US/Eastern")

Expand Down