@@ -31,6 +31,7 @@ from cpython.datetime cimport (
31
31
import_datetime()
32
32
33
33
from pandas._libs.tslibs.base cimport ABCTimestamp
34
+ from pandas._libs.tslibs.dtypes cimport periods_per_second
34
35
from pandas._libs.tslibs.np_datetime cimport (
35
36
NPY_DATETIMEUNIT,
36
37
NPY_FR_ns,
@@ -40,11 +41,14 @@ from pandas._libs.tslibs.np_datetime cimport (
40
41
dtstruct_to_dt64,
41
42
get_datetime64_unit,
42
43
get_datetime64_value,
44
+ get_implementation_bounds,
43
45
get_unit_from_dtype,
44
46
npy_datetime,
45
47
npy_datetimestruct,
48
+ npy_datetimestruct_to_datetime,
46
49
pandas_datetime_to_datetimestruct,
47
50
pydatetime_to_dt64,
51
+ pydatetime_to_dtstruct,
48
52
string_to_dts,
49
53
)
50
54
@@ -307,11 +311,15 @@ cdef maybe_localize_tso(_TSObject obj, tzinfo tz, NPY_DATETIMEUNIT reso):
307
311
if obj.value != NPY_NAT:
308
312
# check_overflows needs to run after _localize_tso
309
313
check_dts_bounds(& obj.dts, reso)
310
- check_overflows(obj)
314
+ check_overflows(obj, reso )
311
315
312
316
313
- cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
314
- int32_t nanos = 0 ):
317
+ cdef _TSObject convert_datetime_to_tsobject(
318
+ datetime ts,
319
+ tzinfo tz,
320
+ int32_t nanos = 0 ,
321
+ NPY_DATETIMEUNIT reso = NPY_FR_ns,
322
+ ):
315
323
"""
316
324
Convert a datetime (or Timestamp) input `ts`, along with optional timezone
317
325
object `tz` to a _TSObject.
@@ -327,13 +335,15 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
327
335
timezone for the timezone-aware output
328
336
nanos : int32_t, default is 0
329
337
nanoseconds supplement the precision of the datetime input ts
338
+ reso : NPY_DATETIMEUNIT, default NPY_FR_ns
330
339
331
340
Returns
332
341
-------
333
342
obj : _TSObject
334
343
"""
335
344
cdef:
336
345
_TSObject obj = _TSObject()
346
+ int64_t pps
337
347
338
348
obj.fold = ts.fold
339
349
if tz is not None :
@@ -342,34 +352,35 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
342
352
if ts.tzinfo is not None :
343
353
# Convert the current timezone to the passed timezone
344
354
ts = ts.astimezone(tz)
345
- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
355
+ pydatetime_to_dtstruct (ts, & obj.dts)
346
356
obj.tzinfo = ts.tzinfo
347
357
elif not is_utc(tz):
348
358
ts = _localize_pydatetime(ts, tz)
349
- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
359
+ pydatetime_to_dtstruct (ts, & obj.dts)
350
360
obj.tzinfo = ts.tzinfo
351
361
else :
352
362
# UTC
353
- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
363
+ pydatetime_to_dtstruct (ts, & obj.dts)
354
364
obj.tzinfo = tz
355
365
else :
356
- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
366
+ pydatetime_to_dtstruct (ts, & obj.dts)
357
367
obj.tzinfo = ts.tzinfo
358
368
359
- if obj.tzinfo is not None and not is_utc(obj.tzinfo):
360
- offset = get_utcoffset(obj.tzinfo, ts)
361
- obj.value -= int (offset.total_seconds() * 1e9 )
362
-
363
369
if isinstance (ts, ABCTimestamp):
364
- obj.value += < int64_t> ts.nanosecond
365
370
obj.dts.ps = ts.nanosecond * 1000
366
371
367
372
if nanos:
368
- obj.value += nanos
369
373
obj.dts.ps = nanos * 1000
370
374
371
- check_dts_bounds(& obj.dts)
372
- check_overflows(obj)
375
+ obj.value = npy_datetimestruct_to_datetime(reso, & obj.dts)
376
+
377
+ if obj.tzinfo is not None and not is_utc(obj.tzinfo):
378
+ offset = get_utcoffset(obj.tzinfo, ts)
379
+ pps = periods_per_second(reso)
380
+ obj.value -= int (offset.total_seconds() * pps)
381
+
382
+ check_dts_bounds(& obj.dts, reso)
383
+ check_overflows(obj, reso)
373
384
return obj
374
385
375
386
@@ -401,7 +412,7 @@ cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
401
412
obj.tzinfo = pytz.FixedOffset(tzoffset)
402
413
obj.value = tz_localize_to_utc_single(value, obj.tzinfo)
403
414
if tz is None :
404
- check_overflows(obj)
415
+ check_overflows(obj, NPY_FR_ns )
405
416
return obj
406
417
407
418
cdef:
@@ -515,13 +526,14 @@ cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, str unit,
515
526
return convert_datetime_to_tsobject(dt, tz)
516
527
517
528
518
- cdef inline check_overflows(_TSObject obj):
529
+ cdef inline check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso = NPY_FR_ns ):
519
530
"""
520
531
Check that we haven't silently overflowed in timezone conversion
521
532
522
533
Parameters
523
534
----------
524
535
obj : _TSObject
536
+ reso : NPY_DATETIMEUNIT, default NPY_FR_ns
525
537
526
538
Returns
527
539
-------
@@ -532,15 +544,20 @@ cdef inline check_overflows(_TSObject obj):
532
544
OutOfBoundsDatetime
533
545
"""
534
546
# GH#12677
535
- if obj.dts.year == 1677 :
547
+ cdef:
548
+ npy_datetimestruct lb, ub
549
+
550
+ get_implementation_bounds(reso, & lb, & ub)
551
+
552
+ if obj.dts.year == lb.year:
536
553
if not (obj.value < 0 ):
537
554
from pandas._libs.tslibs.timestamps import Timestamp
538
555
fmt = (f" {obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
539
556
f" {obj.dts.hour:02d}:{obj.dts.min:02d}:{obj.dts.sec:02d}" )
540
557
raise OutOfBoundsDatetime(
541
558
f" Converting {fmt} underflows past {Timestamp.min}"
542
559
)
543
- elif obj.dts.year == 2262 :
560
+ elif obj.dts.year == ub.year :
544
561
if not (obj.value > 0 ):
545
562
from pandas._libs.tslibs.timestamps import Timestamp
546
563
fmt = (f" {obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
0 commit comments