Skip to content

Commit fc13ce3

Browse files
jbrockmendelalanbato
authored andcommitted
Separate out _convert_datetime_to_tsobject (pandas-dev#17715)
1 parent e1c2ad7 commit fc13ce3

File tree

1 file changed

+95
-50
lines changed

1 file changed

+95
-50
lines changed

pandas/_libs/tslib.pyx

+95-50
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,7 @@ class Timestamp(_Timestamp):
708708
# reconstruct & check bounds
709709
ts_input = datetime(dts.year, dts.month, dts.day, dts.hour, dts.min,
710710
dts.sec, dts.us, tzinfo=_tzinfo)
711-
ts = convert_to_tsobject(ts_input, _tzinfo, None, 0, 0)
711+
ts = convert_datetime_to_tsobject(ts_input, _tzinfo)
712712
value = ts.value + (dts.ps // 1000)
713713
if value != NPY_NAT:
714714
_check_dts_bounds(&dts)
@@ -1455,52 +1455,11 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
14551455
obj.value = ts
14561456
pandas_datetime_to_datetimestruct(ts, PANDAS_FR_ns, &obj.dts)
14571457
elif PyDateTime_Check(ts):
1458-
if tz is not None:
1459-
# sort of a temporary hack
1460-
if ts.tzinfo is not None:
1461-
if (hasattr(tz, 'normalize') and
1462-
hasattr(ts.tzinfo, '_utcoffset')):
1463-
ts = tz.normalize(ts)
1464-
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1465-
obj.tzinfo = ts.tzinfo
1466-
else: #tzoffset
1467-
try:
1468-
tz = ts.astimezone(tz).tzinfo
1469-
except:
1470-
pass
1471-
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1472-
ts_offset = get_utcoffset(ts.tzinfo, ts)
1473-
obj.value -= _delta_to_nanoseconds(ts_offset)
1474-
tz_offset = get_utcoffset(tz, ts)
1475-
obj.value += _delta_to_nanoseconds(tz_offset)
1476-
pandas_datetime_to_datetimestruct(obj.value,
1477-
PANDAS_FR_ns, &obj.dts)
1478-
obj.tzinfo = tz
1479-
elif not is_utc(tz):
1480-
ts = _localize_pydatetime(ts, tz)
1481-
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1482-
obj.tzinfo = ts.tzinfo
1483-
else:
1484-
# UTC
1485-
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1486-
obj.tzinfo = pytz.utc
1487-
else:
1488-
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1489-
obj.tzinfo = ts.tzinfo
1490-
1491-
if obj.tzinfo is not None and not is_utc(obj.tzinfo):
1492-
offset = get_utcoffset(obj.tzinfo, ts)
1493-
obj.value -= _delta_to_nanoseconds(offset)
1494-
1495-
if is_timestamp(ts):
1496-
obj.value += ts.nanosecond
1497-
obj.dts.ps = ts.nanosecond * 1000
1498-
_check_dts_bounds(&obj.dts)
1499-
return obj
1458+
return convert_datetime_to_tsobject(ts, tz)
15001459
elif PyDate_Check(ts):
15011460
# Keep the converter same as PyDateTime's
15021461
ts = datetime.combine(ts, datetime_time())
1503-
return convert_to_tsobject(ts, tz, None, 0, 0)
1462+
return convert_datetime_to_tsobject(ts, tz)
15041463
elif getattr(ts, '_typ', None) == 'period':
15051464
raise ValueError(
15061465
"Cannot convert Period to Timestamp "
@@ -1518,6 +1477,83 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
15181477
return obj
15191478

15201479

1480+
cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
1481+
int32_t nanos=0):
1482+
"""
1483+
Convert a datetime (or Timestamp) input `ts`, along with optional timezone
1484+
object `tz` to a _TSObject.
1485+
1486+
The optional argument `nanos` allows for cases where datetime input
1487+
needs to be supplemented with higher-precision information.
1488+
1489+
Parameters
1490+
----------
1491+
ts : datetime or Timestamp
1492+
Value to be converted to _TSObject
1493+
tz : tzinfo or None
1494+
timezone for the timezone-aware output
1495+
nanos : int32_t, default is 0
1496+
nanoseconds supplement the precision of the datetime input ts
1497+
1498+
Returns
1499+
-------
1500+
obj : _TSObject
1501+
"""
1502+
cdef:
1503+
_TSObject obj = _TSObject()
1504+
1505+
if tz is not None:
1506+
tz = maybe_get_tz(tz)
1507+
1508+
# sort of a temporary hack
1509+
if ts.tzinfo is not None:
1510+
if (hasattr(tz, 'normalize') and
1511+
hasattr(ts.tzinfo, '_utcoffset')):
1512+
ts = tz.normalize(ts)
1513+
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1514+
obj.tzinfo = ts.tzinfo
1515+
else:
1516+
# tzoffset
1517+
try:
1518+
tz = ts.astimezone(tz).tzinfo
1519+
except:
1520+
pass
1521+
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1522+
ts_offset = get_utcoffset(ts.tzinfo, ts)
1523+
obj.value -= int(ts_offset.total_seconds() * 1e9)
1524+
tz_offset = get_utcoffset(tz, ts)
1525+
obj.value += int(tz_offset.total_seconds() * 1e9)
1526+
pandas_datetime_to_datetimestruct(obj.value,
1527+
PANDAS_FR_ns, &obj.dts)
1528+
obj.tzinfo = tz
1529+
elif not is_utc(tz):
1530+
ts = _localize_pydatetime(ts, tz)
1531+
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1532+
obj.tzinfo = ts.tzinfo
1533+
else:
1534+
# UTC
1535+
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1536+
obj.tzinfo = pytz.utc
1537+
else:
1538+
obj.value = _pydatetime_to_dts(ts, &obj.dts)
1539+
obj.tzinfo = ts.tzinfo
1540+
1541+
if obj.tzinfo is not None and not is_utc(obj.tzinfo):
1542+
offset = get_utcoffset(obj.tzinfo, ts)
1543+
obj.value -= int(offset.total_seconds() * 1e9)
1544+
1545+
if is_timestamp(ts):
1546+
obj.value += ts.nanosecond
1547+
obj.dts.ps = ts.nanosecond * 1000
1548+
1549+
if nanos:
1550+
obj.value += nanos
1551+
obj.dts.ps = nanos * 1000
1552+
1553+
_check_dts_bounds(&obj.dts)
1554+
return obj
1555+
1556+
15211557
cpdef convert_str_to_tsobject(object ts, object tz, object unit,
15221558
dayfirst=False, yearfirst=False):
15231559
""" ts must be a string """
@@ -1538,11 +1574,12 @@ cpdef convert_str_to_tsobject(object ts, object tz, object unit,
15381574
elif ts == 'now':
15391575
# Issue 9000, we short-circuit rather than going
15401576
# into np_datetime_strings which returns utc
1541-
ts = Timestamp.now(tz)
1577+
ts = datetime.now(tz)
15421578
elif ts == 'today':
15431579
# Issue 9000, we short-circuit rather than going
15441580
# into np_datetime_strings which returns a normalized datetime
1545-
ts = Timestamp.today(tz)
1581+
ts = datetime.now(tz)
1582+
# equiv: datetime.today().replace(tzinfo=tz)
15461583
else:
15471584
try:
15481585
_string_to_dts(ts, &obj.dts, &out_local, &out_tzoffset)
@@ -1557,7 +1594,15 @@ cpdef convert_str_to_tsobject(object ts, object tz, object unit,
15571594
return obj
15581595
else:
15591596
# Keep the converter same as PyDateTime's
1560-
ts = Timestamp(obj.value, tz=obj.tzinfo)
1597+
obj = convert_to_tsobject(obj.value, obj.tzinfo,
1598+
None, 0, 0)
1599+
dtime = datetime(obj.dts.year, obj.dts.month, obj.dts.day,
1600+
obj.dts.hour, obj.dts.min, obj.dts.sec,
1601+
obj.dts.us, obj.tzinfo)
1602+
obj = convert_datetime_to_tsobject(dtime, tz,
1603+
nanos=obj.dts.ps / 1000)
1604+
return obj
1605+
15611606
else:
15621607
ts = obj.value
15631608
if tz is not None:
@@ -1706,7 +1751,7 @@ def datetime_to_datetime64(ndarray[object] values):
17061751
else:
17071752
inferred_tz = get_timezone(val.tzinfo)
17081753

1709-
_ts = convert_to_tsobject(val, None, None, 0, 0)
1754+
_ts = convert_datetime_to_tsobject(val, None)
17101755
iresult[i] = _ts.value
17111756
_check_dts_bounds(&_ts.dts)
17121757
else:
@@ -2026,7 +2071,7 @@ cpdef array_to_datetime(ndarray[object] values, errors='raise',
20262071
seen_datetime=1
20272072
if val.tzinfo is not None:
20282073
if utc_convert:
2029-
_ts = convert_to_tsobject(val, None, 'ns', 0, 0)
2074+
_ts = convert_datetime_to_tsobject(val, None)
20302075
iresult[i] = _ts.value
20312076
try:
20322077
_check_dts_bounds(&_ts.dts)
@@ -2135,7 +2180,7 @@ cpdef array_to_datetime(ndarray[object] values, errors='raise',
21352180
raise TypeError("invalid string coercion to datetime")
21362181

21372182
try:
2138-
_ts = convert_to_tsobject(py_dt, None, None, 0, 0)
2183+
_ts = convert_datetime_to_tsobject(py_dt, None)
21392184
iresult[i] = _ts.value
21402185
except ValueError:
21412186
if is_coerce:

0 commit comments

Comments
 (0)