tslib.pyx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import warnings
  2. from pandas.util._exceptions import find_stack_level
  3. cimport cython
  4. from datetime import timezone
  5. from cpython.datetime cimport (
  6. PyDate_Check,
  7. PyDateTime_Check,
  8. datetime,
  9. import_datetime,
  10. timedelta,
  11. tzinfo,
  12. )
  13. from cpython.object cimport PyObject
  14. # import datetime C API
  15. import_datetime()
  16. cimport numpy as cnp
  17. from numpy cimport (
  18. int64_t,
  19. ndarray,
  20. )
  21. import numpy as np
  22. cnp.import_array()
  23. from pandas._libs.tslibs.np_datetime cimport (
  24. NPY_DATETIMEUNIT,
  25. NPY_FR_ns,
  26. check_dts_bounds,
  27. npy_datetimestruct,
  28. npy_datetimestruct_to_datetime,
  29. pandas_datetime_to_datetimestruct,
  30. pydate_to_dt64,
  31. string_to_dts,
  32. )
  33. from pandas._libs.tslibs.strptime cimport parse_today_now
  34. from pandas._libs.util cimport (
  35. is_datetime64_object,
  36. is_float_object,
  37. is_integer_object,
  38. )
  39. from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime
  40. from pandas._libs.tslibs.conversion cimport (
  41. _TSObject,
  42. cast_from_unit,
  43. convert_str_to_tsobject,
  44. convert_timezone,
  45. get_datetime64_nanos,
  46. parse_pydatetime,
  47. precision_from_unit,
  48. )
  49. from pandas._libs.tslibs.nattype cimport (
  50. NPY_NAT,
  51. c_NaT as NaT,
  52. c_nat_strings as nat_strings,
  53. )
  54. from pandas._libs.tslibs.timestamps cimport _Timestamp
  55. from pandas._libs.tslibs import (
  56. Resolution,
  57. get_resolution,
  58. )
  59. from pandas._libs.tslibs.timestamps import Timestamp
  60. # Note: this is the only non-tslibs intra-pandas dependency here
  61. from pandas._libs.missing cimport checknull_with_nat_and_na
  62. from pandas._libs.tslibs.tzconversion cimport tz_localize_to_utc_single
  63. def _test_parse_iso8601(ts: str):
  64. """
  65. TESTING ONLY: Parse string into Timestamp using iso8601 parser. Used
  66. only for testing, actual construction uses `convert_str_to_tsobject`
  67. """
  68. cdef:
  69. _TSObject obj
  70. int out_local = 0, out_tzoffset = 0
  71. NPY_DATETIMEUNIT out_bestunit
  72. obj = _TSObject()
  73. string_to_dts(ts, &obj.dts, &out_bestunit, &out_local, &out_tzoffset, True)
  74. obj.value = npy_datetimestruct_to_datetime(NPY_FR_ns, &obj.dts)
  75. check_dts_bounds(&obj.dts)
  76. if out_local == 1:
  77. obj.tzinfo = timezone(timedelta(minutes=out_tzoffset))
  78. obj.value = tz_localize_to_utc_single(obj.value, obj.tzinfo)
  79. return Timestamp(obj.value, tz=obj.tzinfo)
  80. else:
  81. return Timestamp(obj.value)
  82. @cython.wraparound(False)
  83. @cython.boundscheck(False)
  84. def format_array_from_datetime(
  85. ndarray values,
  86. tzinfo tz=None,
  87. str format=None,
  88. na_rep: str | float = "NaT",
  89. NPY_DATETIMEUNIT reso=NPY_FR_ns,
  90. ) -> np.ndarray:
  91. """
  92. return a np object array of the string formatted values
  93. Parameters
  94. ----------
  95. values : ndarray[int64_t], arbitrary ndim
  96. tz : tzinfo or None, default None
  97. format : str or None, default None
  98. a strftime capable string
  99. na_rep : optional, default is None
  100. a nat format
  101. reso : NPY_DATETIMEUNIT, default NPY_FR_ns
  102. Returns
  103. -------
  104. np.ndarray[object]
  105. """
  106. cdef:
  107. int64_t val, ns, N = values.size
  108. bint show_ms = False, show_us = False, show_ns = False
  109. bint basic_format = False, basic_format_day = False
  110. _Timestamp ts
  111. object res
  112. npy_datetimestruct dts
  113. # Note that `result` (and thus `result_flat`) is C-order and
  114. # `it` iterates C-order as well, so the iteration matches
  115. # See discussion at
  116. # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
  117. ndarray result = cnp.PyArray_EMPTY(values.ndim, values.shape, cnp.NPY_OBJECT, 0)
  118. object[::1] res_flat = result.ravel() # should NOT be a copy
  119. cnp.flatiter it = cnp.PyArray_IterNew(values)
  120. if tz is None:
  121. # if we don't have a format nor tz, then choose
  122. # a format based on precision
  123. basic_format = format is None
  124. if basic_format:
  125. reso_obj = get_resolution(values, tz=tz, reso=reso)
  126. show_ns = reso_obj == Resolution.RESO_NS
  127. show_us = reso_obj == Resolution.RESO_US
  128. show_ms = reso_obj == Resolution.RESO_MS
  129. elif format == "%Y-%m-%d %H:%M:%S":
  130. # Same format as default, but with hardcoded precision (s)
  131. basic_format = True
  132. show_ns = show_us = show_ms = False
  133. elif format == "%Y-%m-%d %H:%M:%S.%f":
  134. # Same format as default, but with hardcoded precision (us)
  135. basic_format = show_us = True
  136. show_ns = show_ms = False
  137. elif format == "%Y-%m-%d":
  138. # Default format for dates
  139. basic_format_day = True
  140. assert not (basic_format_day and basic_format)
  141. for i in range(N):
  142. # Analogous to: utc_val = values[i]
  143. val = (<int64_t*>cnp.PyArray_ITER_DATA(it))[0]
  144. if val == NPY_NAT:
  145. res = na_rep
  146. elif basic_format_day:
  147. pandas_datetime_to_datetimestruct(val, reso, &dts)
  148. res = f"{dts.year}-{dts.month:02d}-{dts.day:02d}"
  149. elif basic_format:
  150. pandas_datetime_to_datetimestruct(val, reso, &dts)
  151. res = (f"{dts.year}-{dts.month:02d}-{dts.day:02d} "
  152. f"{dts.hour:02d}:{dts.min:02d}:{dts.sec:02d}")
  153. if show_ns:
  154. ns = dts.ps // 1000
  155. res += f".{ns + dts.us * 1000:09d}"
  156. elif show_us:
  157. res += f".{dts.us:06d}"
  158. elif show_ms:
  159. res += f".{dts.us // 1000:03d}"
  160. else:
  161. ts = Timestamp._from_value_and_reso(val, reso=reso, tz=tz)
  162. if format is None:
  163. # Use datetime.str, that returns ts.isoformat(sep=' ')
  164. res = str(ts)
  165. else:
  166. # invalid format string
  167. # requires dates > 1900
  168. try:
  169. # Note: dispatches to pydatetime
  170. res = ts.strftime(format)
  171. except ValueError:
  172. # Use datetime.str, that returns ts.isoformat(sep=' ')
  173. res = str(ts)
  174. # Note: we can index result directly instead of using PyArray_MultiIter_DATA
  175. # like we do for the other functions because result is known C-contiguous
  176. # and is the first argument to PyArray_MultiIterNew2. The usual pattern
  177. # does not seem to work with object dtype.
  178. # See discussion at
  179. # github.com/pandas-dev/pandas/pull/46886#discussion_r860261305
  180. res_flat[i] = res
  181. cnp.PyArray_ITER_NEXT(it)
  182. return result
  183. def array_with_unit_to_datetime(
  184. ndarray[object] values,
  185. str unit,
  186. str errors="coerce"
  187. ):
  188. """
  189. Convert the ndarray to datetime according to the time unit.
  190. This function converts an array of objects into a numpy array of
  191. datetime64[ns]. It returns the converted array
  192. and also returns the timezone offset
  193. if errors:
  194. - raise: return converted values or raise OutOfBoundsDatetime
  195. if out of range on the conversion or
  196. ValueError for other conversions (e.g. a string)
  197. - ignore: return non-convertible values as the same unit
  198. - coerce: NaT for non-convertibles
  199. Parameters
  200. ----------
  201. values : ndarray
  202. Date-like objects to convert.
  203. unit : str
  204. Time unit to use during conversion.
  205. errors : str, default 'raise'
  206. Error behavior when parsing.
  207. Returns
  208. -------
  209. result : ndarray of m8 values
  210. tz : parsed timezone offset or None
  211. """
  212. cdef:
  213. Py_ssize_t i, n=len(values)
  214. int64_t mult
  215. bint is_ignore = errors == "ignore"
  216. bint is_coerce = errors == "coerce"
  217. bint is_raise = errors == "raise"
  218. ndarray[int64_t] iresult
  219. tzinfo tz = None
  220. float fval
  221. assert is_ignore or is_coerce or is_raise
  222. if unit == "ns":
  223. result, tz = array_to_datetime(
  224. values.astype(object, copy=False),
  225. errors=errors,
  226. )
  227. return result, tz
  228. mult, _ = precision_from_unit(unit)
  229. result = np.empty(n, dtype="M8[ns]")
  230. iresult = result.view("i8")
  231. for i in range(n):
  232. val = values[i]
  233. try:
  234. if checknull_with_nat_and_na(val):
  235. iresult[i] = NPY_NAT
  236. elif is_integer_object(val) or is_float_object(val):
  237. if val != val or val == NPY_NAT:
  238. iresult[i] = NPY_NAT
  239. else:
  240. iresult[i] = cast_from_unit(val, unit)
  241. elif isinstance(val, str):
  242. if len(val) == 0 or val in nat_strings:
  243. iresult[i] = NPY_NAT
  244. else:
  245. try:
  246. fval = float(val)
  247. except ValueError:
  248. raise ValueError(
  249. f"non convertible value {val} with the unit '{unit}'"
  250. )
  251. warnings.warn(
  252. "The behavior of 'to_datetime' with 'unit' when parsing "
  253. "strings is deprecated. In a future version, strings will "
  254. "be parsed as datetime strings, matching the behavior "
  255. "without a 'unit'. To retain the old behavior, explicitly "
  256. "cast ints or floats to numeric type before calling "
  257. "to_datetime.",
  258. FutureWarning,
  259. stacklevel=find_stack_level(),
  260. )
  261. iresult[i] = cast_from_unit(fval, unit)
  262. else:
  263. # TODO: makes more sense as TypeError, but that would be an
  264. # API change.
  265. raise ValueError(
  266. f"unit='{unit}' not valid with non-numerical val='{val}'"
  267. )
  268. except (ValueError, OutOfBoundsDatetime, TypeError) as err:
  269. if is_raise:
  270. err.args = (f"{err}, at position {i}",)
  271. raise
  272. elif is_ignore:
  273. # we have hit an exception
  274. # and are in ignore mode
  275. # redo as object
  276. return _array_with_unit_to_datetime_object_fallback(values, unit)
  277. else:
  278. # is_coerce
  279. iresult[i] = NPY_NAT
  280. return result, tz
  281. cdef _array_with_unit_to_datetime_object_fallback(ndarray[object] values, str unit):
  282. cdef:
  283. Py_ssize_t i, n = len(values)
  284. ndarray[object] oresult
  285. tzinfo tz = None
  286. # TODO: fix subtle differences between this and no-unit code
  287. oresult = cnp.PyArray_EMPTY(values.ndim, values.shape, cnp.NPY_OBJECT, 0)
  288. for i in range(n):
  289. val = values[i]
  290. if checknull_with_nat_and_na(val):
  291. oresult[i] = <object>NaT
  292. elif is_integer_object(val) or is_float_object(val):
  293. if val != val or val == NPY_NAT:
  294. oresult[i] = <object>NaT
  295. else:
  296. try:
  297. oresult[i] = Timestamp(val, unit=unit)
  298. except OutOfBoundsDatetime:
  299. oresult[i] = val
  300. elif isinstance(val, str):
  301. if len(val) == 0 or val in nat_strings:
  302. oresult[i] = <object>NaT
  303. else:
  304. oresult[i] = val
  305. return oresult, tz
  306. @cython.wraparound(False)
  307. @cython.boundscheck(False)
  308. def first_non_null(values: ndarray) -> int:
  309. """Find position of first non-null value, return -1 if there isn't one."""
  310. cdef:
  311. Py_ssize_t n = len(values)
  312. Py_ssize_t i
  313. for i in range(n):
  314. val = values[i]
  315. if checknull_with_nat_and_na(val):
  316. continue
  317. if (
  318. isinstance(val, str)
  319. and
  320. (len(val) == 0 or val in nat_strings or val in ("now", "today"))
  321. ):
  322. continue
  323. return i
  324. else:
  325. return -1
  326. @cython.wraparound(False)
  327. @cython.boundscheck(False)
  328. cpdef array_to_datetime(
  329. ndarray values, # object dtype, arbitrary ndim
  330. str errors="raise",
  331. bint dayfirst=False,
  332. bint yearfirst=False,
  333. bint utc=False,
  334. ):
  335. """
  336. Converts a 1D array of date-like values to a numpy array of either:
  337. 1) datetime64[ns] data
  338. 2) datetime.datetime objects, if OutOfBoundsDatetime or TypeError
  339. is encountered
  340. Also returns a fixed-offset tzinfo object if an array of strings with the same
  341. timezone offset is passed and utc=True is not passed. Otherwise, None
  342. is returned
  343. Handles datetime.date, datetime.datetime, np.datetime64 objects, numeric,
  344. strings
  345. Parameters
  346. ----------
  347. values : ndarray of object
  348. date-like objects to convert
  349. errors : str, default 'raise'
  350. error behavior when parsing
  351. dayfirst : bool, default False
  352. dayfirst parsing behavior when encountering datetime strings
  353. yearfirst : bool, default False
  354. yearfirst parsing behavior when encountering datetime strings
  355. utc : bool, default False
  356. indicator whether the dates should be UTC
  357. Returns
  358. -------
  359. np.ndarray
  360. May be datetime64[ns] or object dtype
  361. tzinfo or None
  362. """
  363. cdef:
  364. Py_ssize_t i, n = values.size
  365. object val, tz
  366. ndarray[int64_t] iresult
  367. npy_datetimestruct dts
  368. bint utc_convert = bool(utc)
  369. bint seen_datetime_offset = False
  370. bint is_raise = errors == "raise"
  371. bint is_ignore = errors == "ignore"
  372. bint is_coerce = errors == "coerce"
  373. bint is_same_offsets
  374. _TSObject _ts
  375. float tz_offset
  376. set out_tzoffset_vals = set()
  377. tzinfo tz_out = None
  378. bint found_tz = False, found_naive = False
  379. cnp.broadcast mi
  380. # specify error conditions
  381. assert is_raise or is_ignore or is_coerce
  382. result = np.empty((<object>values).shape, dtype="M8[ns]")
  383. mi = cnp.PyArray_MultiIterNew2(result, values)
  384. iresult = result.view("i8").ravel()
  385. for i in range(n):
  386. # Analogous to `val = values[i]`
  387. val = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
  388. try:
  389. if checknull_with_nat_and_na(val):
  390. iresult[i] = NPY_NAT
  391. elif PyDateTime_Check(val):
  392. if val.tzinfo is not None:
  393. found_tz = True
  394. else:
  395. found_naive = True
  396. tz_out = convert_timezone(
  397. val.tzinfo,
  398. tz_out,
  399. found_naive,
  400. found_tz,
  401. utc_convert,
  402. )
  403. iresult[i] = parse_pydatetime(val, &dts, utc_convert)
  404. elif PyDate_Check(val):
  405. iresult[i] = pydate_to_dt64(val, &dts)
  406. check_dts_bounds(&dts)
  407. elif is_datetime64_object(val):
  408. iresult[i] = get_datetime64_nanos(val, NPY_FR_ns)
  409. elif is_integer_object(val) or is_float_object(val):
  410. # these must be ns unit by-definition
  411. if val != val or val == NPY_NAT:
  412. iresult[i] = NPY_NAT
  413. else:
  414. # we now need to parse this as if unit='ns'
  415. iresult[i] = cast_from_unit(val, "ns")
  416. elif isinstance(val, str):
  417. # string
  418. if type(val) is not str:
  419. # GH#32264 np.str_ object
  420. val = str(val)
  421. if parse_today_now(val, &iresult[i], utc):
  422. # We can't _quite_ dispatch this to convert_str_to_tsobject
  423. # bc there isn't a nice way to pass "utc"
  424. cnp.PyArray_MultiIter_NEXT(mi)
  425. continue
  426. _ts = convert_str_to_tsobject(
  427. val, None, unit="ns", dayfirst=dayfirst, yearfirst=yearfirst
  428. )
  429. _ts.ensure_reso(NPY_FR_ns, val)
  430. iresult[i] = _ts.value
  431. tz = _ts.tzinfo
  432. if tz is not None:
  433. # dateutil timezone objects cannot be hashed, so
  434. # store the UTC offsets in seconds instead
  435. nsecs = tz.utcoffset(None).total_seconds()
  436. out_tzoffset_vals.add(nsecs)
  437. # need to set seen_datetime_offset *after* the
  438. # potentially-raising timezone(timedelta(...)) call,
  439. # otherwise we can go down the is_same_offsets path
  440. # bc len(out_tzoffset_vals) == 0
  441. seen_datetime_offset = True
  442. else:
  443. # Add a marker for naive string, to track if we are
  444. # parsing mixed naive and aware strings
  445. out_tzoffset_vals.add("naive")
  446. else:
  447. raise TypeError(f"{type(val)} is not convertible to datetime")
  448. cnp.PyArray_MultiIter_NEXT(mi)
  449. except (TypeError, OverflowError, ValueError) as ex:
  450. ex.args = (f"{ex}, at position {i}",)
  451. if is_coerce:
  452. iresult[i] = NPY_NAT
  453. cnp.PyArray_MultiIter_NEXT(mi)
  454. continue
  455. elif is_raise:
  456. raise
  457. return values, None
  458. if seen_datetime_offset and not utc_convert:
  459. # GH#17697
  460. # 1) If all the offsets are equal, return one offset for
  461. # the parsed dates to (maybe) pass to DatetimeIndex
  462. # 2) If the offsets are different, then force the parsing down the
  463. # object path where an array of datetimes
  464. # (with individual dateutil.tzoffsets) are returned
  465. is_same_offsets = len(out_tzoffset_vals) == 1
  466. if not is_same_offsets:
  467. return _array_to_datetime_object(values, errors, dayfirst, yearfirst)
  468. else:
  469. tz_offset = out_tzoffset_vals.pop()
  470. tz_out = timezone(timedelta(seconds=tz_offset))
  471. return result, tz_out
  472. @cython.wraparound(False)
  473. @cython.boundscheck(False)
  474. cdef _array_to_datetime_object(
  475. ndarray[object] values,
  476. str errors,
  477. bint dayfirst=False,
  478. bint yearfirst=False,
  479. ):
  480. """
  481. Fall back function for array_to_datetime
  482. Attempts to parse datetime strings with dateutil to return an array
  483. of datetime objects
  484. Parameters
  485. ----------
  486. values : ndarray[object]
  487. date-like objects to convert
  488. errors : str
  489. error behavior when parsing
  490. dayfirst : bool, default False
  491. dayfirst parsing behavior when encountering datetime strings
  492. yearfirst : bool, default False
  493. yearfirst parsing behavior when encountering datetime strings
  494. Returns
  495. -------
  496. np.ndarray[object]
  497. Literal[None]
  498. """
  499. cdef:
  500. Py_ssize_t i, n = values.size
  501. object val
  502. bint is_ignore = errors == "ignore"
  503. bint is_coerce = errors == "coerce"
  504. bint is_raise = errors == "raise"
  505. ndarray oresult_nd
  506. ndarray[object] oresult
  507. npy_datetimestruct dts
  508. cnp.broadcast mi
  509. _TSObject tsobj
  510. assert is_raise or is_ignore or is_coerce
  511. oresult_nd = cnp.PyArray_EMPTY(values.ndim, values.shape, cnp.NPY_OBJECT, 0)
  512. mi = cnp.PyArray_MultiIterNew2(oresult_nd, values)
  513. oresult = oresult_nd.ravel()
  514. # We return an object array and only attempt to parse:
  515. # 1) NaT or NaT-like values
  516. # 2) datetime strings, which we return as datetime.datetime
  517. # 3) special strings - "now" & "today"
  518. for i in range(n):
  519. # Analogous to: val = values[i]
  520. val = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
  521. if checknull_with_nat_and_na(val) or PyDateTime_Check(val):
  522. # GH 25978. No need to parse NaT-like or datetime-like vals
  523. oresult[i] = val
  524. elif isinstance(val, str):
  525. if type(val) is not str:
  526. # GH#32264 np.str_ objects
  527. val = str(val)
  528. if len(val) == 0 or val in nat_strings:
  529. oresult[i] = "NaT"
  530. cnp.PyArray_MultiIter_NEXT(mi)
  531. continue
  532. try:
  533. tsobj = convert_str_to_tsobject(
  534. val, None, unit="ns", dayfirst=dayfirst, yearfirst=yearfirst
  535. )
  536. tsobj.ensure_reso(NPY_FR_ns, val)
  537. dts = tsobj.dts
  538. oresult[i] = datetime(
  539. dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.us,
  540. tzinfo=tsobj.tzinfo,
  541. fold=tsobj.fold,
  542. )
  543. except (ValueError, OverflowError) as ex:
  544. ex.args = (f"{ex}, at position {i}", )
  545. if is_coerce:
  546. oresult[i] = <object>NaT
  547. cnp.PyArray_MultiIter_NEXT(mi)
  548. continue
  549. if is_raise:
  550. raise
  551. return values, None
  552. else:
  553. if is_raise:
  554. raise
  555. return values, None
  556. cnp.PyArray_MultiIter_NEXT(mi)
  557. return oresult_nd, None
  558. def array_to_datetime_with_tz(ndarray values, tzinfo tz):
  559. """
  560. Vectorized analogue to pd.Timestamp(value, tz=tz)
  561. values has object-dtype, unrestricted ndim.
  562. Major differences between this and array_to_datetime with utc=True
  563. - np.datetime64 objects are treated as _wall_ times.
  564. - tznaive datetimes are treated as _wall_ times.
  565. """
  566. cdef:
  567. ndarray result = cnp.PyArray_EMPTY(values.ndim, values.shape, cnp.NPY_INT64, 0)
  568. cnp.broadcast mi = cnp.PyArray_MultiIterNew2(result, values)
  569. Py_ssize_t i, n = values.size
  570. object item
  571. int64_t ival
  572. datetime ts
  573. for i in range(n):
  574. # Analogous to `item = values[i]`
  575. item = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
  576. if checknull_with_nat_and_na(item):
  577. # this catches pd.NA which would raise in the Timestamp constructor
  578. ival = NPY_NAT
  579. else:
  580. ts = Timestamp(item)
  581. if ts is NaT:
  582. ival = NPY_NAT
  583. else:
  584. if ts.tz is not None:
  585. ts = ts.tz_convert(tz)
  586. else:
  587. # datetime64, tznaive pydatetime, int, float
  588. ts = ts.tz_localize(tz)
  589. ts = ts.as_unit("ns")
  590. ival = ts._value
  591. # Analogous to: result[i] = ival
  592. (<int64_t*>cnp.PyArray_MultiIter_DATA(mi, 0))[0] = ival
  593. cnp.PyArray_MultiIter_NEXT(mi)
  594. return result