@@ -31,6 +31,7 @@ from cpython.datetime cimport (
3131import_datetime()
3232
3333from pandas._libs.tslibs.base cimport ABCTimestamp
34+ from pandas._libs.tslibs.dtypes cimport periods_per_second
3435from pandas._libs.tslibs.np_datetime cimport (
3536 NPY_DATETIMEUNIT,
3637 NPY_FR_ns,
@@ -40,11 +41,14 @@ from pandas._libs.tslibs.np_datetime cimport (
4041 dtstruct_to_dt64,
4142 get_datetime64_unit,
4243 get_datetime64_value,
44+ get_implementation_bounds,
4345 get_unit_from_dtype,
4446 npy_datetime,
4547 npy_datetimestruct,
48+ npy_datetimestruct_to_datetime,
4649 pandas_datetime_to_datetimestruct,
4750 pydatetime_to_dt64,
51+ pydatetime_to_dtstruct,
4852 string_to_dts,
4953)
5054
@@ -307,11 +311,15 @@ cdef maybe_localize_tso(_TSObject obj, tzinfo tz, NPY_DATETIMEUNIT reso):
307311 if obj.value != NPY_NAT:
308312 # check_overflows needs to run after _localize_tso
309313 check_dts_bounds(& obj.dts, reso)
310- check_overflows(obj)
314+ check_overflows(obj, reso )
311315
312316
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+ ):
315323 """
316324 Convert a datetime (or Timestamp) input `ts`, along with optional timezone
317325 object `tz` to a _TSObject.
@@ -327,13 +335,15 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
327335 timezone for the timezone-aware output
328336 nanos : int32_t, default is 0
329337 nanoseconds supplement the precision of the datetime input ts
338+ reso : NPY_DATETIMEUNIT, default NPY_FR_ns
330339
331340 Returns
332341 -------
333342 obj : _TSObject
334343 """
335344 cdef:
336345 _TSObject obj = _TSObject()
346+ int64_t pps
337347
338348 obj.fold = ts.fold
339349 if tz is not None :
@@ -342,34 +352,35 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
342352 if ts.tzinfo is not None :
343353 # Convert the current timezone to the passed timezone
344354 ts = ts.astimezone(tz)
345- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
355+ pydatetime_to_dtstruct (ts, & obj.dts)
346356 obj.tzinfo = ts.tzinfo
347357 elif not is_utc(tz):
348358 ts = _localize_pydatetime(ts, tz)
349- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
359+ pydatetime_to_dtstruct (ts, & obj.dts)
350360 obj.tzinfo = ts.tzinfo
351361 else :
352362 # UTC
353- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
363+ pydatetime_to_dtstruct (ts, & obj.dts)
354364 obj.tzinfo = tz
355365 else :
356- obj.value = pydatetime_to_dt64 (ts, & obj.dts)
366+ pydatetime_to_dtstruct (ts, & obj.dts)
357367 obj.tzinfo = ts.tzinfo
358368
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-
363369 if isinstance (ts, ABCTimestamp):
364- obj.value += < int64_t> ts.nanosecond
365370 obj.dts.ps = ts.nanosecond * 1000
366371
367372 if nanos:
368- obj.value += nanos
369373 obj.dts.ps = nanos * 1000
370374
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)
373384 return obj
374385
375386
@@ -401,7 +412,7 @@ cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
401412 obj.tzinfo = pytz.FixedOffset(tzoffset)
402413 obj.value = tz_localize_to_utc_single(value, obj.tzinfo)
403414 if tz is None :
404- check_overflows(obj)
415+ check_overflows(obj, NPY_FR_ns )
405416 return obj
406417
407418 cdef:
@@ -515,13 +526,14 @@ cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, str unit,
515526 return convert_datetime_to_tsobject(dt, tz)
516527
517528
518- cdef inline check_overflows(_TSObject obj):
529+ cdef inline check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso = NPY_FR_ns ):
519530 """
520531 Check that we haven't silently overflowed in timezone conversion
521532
522533 Parameters
523534 ----------
524535 obj : _TSObject
536+ reso : NPY_DATETIMEUNIT, default NPY_FR_ns
525537
526538 Returns
527539 -------
@@ -532,15 +544,20 @@ cdef inline check_overflows(_TSObject obj):
532544 OutOfBoundsDatetime
533545 """
534546 # 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:
536553 if not (obj.value < 0 ):
537554 from pandas._libs.tslibs.timestamps import Timestamp
538555 fmt = (f" {obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
539556 f" {obj.dts.hour:02d}:{obj.dts.min:02d}:{obj.dts.sec:02d}" )
540557 raise OutOfBoundsDatetime(
541558 f" Converting {fmt} underflows past {Timestamp.min}"
542559 )
543- elif obj.dts.year == 2262 :
560+ elif obj.dts.year == ub.year :
544561 if not (obj.value > 0 ):
545562 from pandas._libs.tslibs.timestamps import Timestamp
546563 fmt = (f" {obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "
0 commit comments