Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/tools/datetimes.py: 14%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

500 statements  

1from __future__ import annotations 

2 

3from collections import abc 

4from datetime import date 

5from functools import partial 

6from itertools import islice 

7from typing import ( 

8 TYPE_CHECKING, 

9 TypeAlias, 

10 TypedDict, 

11 Union, 

12 cast, 

13 overload, 

14) 

15import warnings 

16 

17import numpy as np 

18 

19from pandas._libs import ( 

20 lib, 

21 tslib, 

22) 

23from pandas._libs.tslibs import ( 

24 NaT, 

25 OutOfBoundsDatetime, 

26 OutOfBoundsTimedelta, 

27 Timedelta, 

28 Timestamp, 

29 astype_overflowsafe, 

30 iNaT, 

31 is_supported_dtype, 

32 periods_per_second, 

33 timezones as libtimezones, 

34) 

35from pandas._libs.tslibs.conversion import ( 

36 cast_from_unit_vectorized, 

37 datetime_from_fields, 

38) 

39from pandas._libs.tslibs.dtypes import get_default_reso 

40from pandas._libs.tslibs.parsing import ( 

41 DateParseError, 

42 guess_datetime_format, 

43) 

44from pandas._libs.tslibs.strptime import ( 

45 array_strptime, 

46 format_is_iso, 

47) 

48from pandas._typing import ( 

49 AnyArrayLike, 

50 ArrayLike, 

51 DateTimeErrorChoices, 

52) 

53from pandas.util._decorators import set_module 

54from pandas.util._exceptions import find_stack_level 

55 

56from pandas.core.dtypes.common import ( 

57 ensure_object, 

58 is_bool_dtype, 

59 is_float, 

60 is_float_dtype, 

61 is_integer, 

62 is_integer_dtype, 

63 is_list_like, 

64 is_numeric_dtype, 

65) 

66from pandas.core.dtypes.dtypes import ( 

67 ArrowDtype, 

68 DatetimeTZDtype, 

69) 

70from pandas.core.dtypes.generic import ( 

71 ABCDataFrame, 

72 ABCSeries, 

73) 

74 

75from pandas.arrays import ( 

76 DatetimeArray, 

77 IntegerArray, 

78 NumpyExtensionArray, 

79) 

80from pandas.core.algorithms import unique 

81from pandas.core.arrays.base import ExtensionArray 

82from pandas.core.arrays.datetimes import ( 

83 maybe_convert_dtype, 

84 objects_to_datetime64, 

85 tz_to_dtype, 

86) 

87from pandas.core.construction import extract_array 

88from pandas.core.indexes.base import Index 

89from pandas.core.indexes.datetimes import DatetimeIndex 

90from pandas.core.tools.timedeltas import to_timedelta 

91 

92if TYPE_CHECKING: 

93 from collections.abc import ( 

94 Callable, 

95 Hashable, 

96 ) 

97 

98 from pandas._libs.tslibs.nattype import NaTType 

99 from pandas._libs.tslibs.timedeltas import UnitChoices 

100 from pandas._typing import TimeUnit 

101 

102 from pandas import ( 

103 DataFrame, 

104 Series, 

105 ) 

106 from pandas.core.arrays import ( 

107 ArrowExtensionArray, 

108 TimedeltaArray, 

109 ) 

110 

111# --------------------------------------------------------------------- 

112# types used in annotations 

113 

114ArrayConvertible: TypeAlias = list | tuple | AnyArrayLike 

115Scalar: TypeAlias = float | str 

116DatetimeScalar: TypeAlias = Scalar | date | np.datetime64 

117 

118DatetimeScalarOrArrayConvertible: TypeAlias = DatetimeScalar | ArrayConvertible 

119DatetimeDictArg: TypeAlias = list[Scalar] | tuple[Scalar, ...] | AnyArrayLike 

120 

121 

122class YearMonthDayDict(TypedDict, total=True): 

123 year: DatetimeDictArg 

124 month: DatetimeDictArg 

125 day: DatetimeDictArg 

126 

127 

128class FulldatetimeDict(YearMonthDayDict, total=False): 

129 hour: DatetimeDictArg 

130 hours: DatetimeDictArg 

131 minute: DatetimeDictArg 

132 minutes: DatetimeDictArg 

133 second: DatetimeDictArg 

134 seconds: DatetimeDictArg 

135 ms: DatetimeDictArg 

136 us: DatetimeDictArg 

137 ns: DatetimeDictArg 

138 

139 

140DictConvertible = Union[FulldatetimeDict, "DataFrame"] 

141start_caching_at = 50 

142 

143 

144# --------------------------------------------------------------------- 

145 

146 

147def _guess_datetime_format_for_array(arr, dayfirst: bool | None = False) -> str | None: 

148 # Try to guess the format based on the first non-NaN element, return None if can't 

149 if (first_non_null := tslib.first_non_null(arr)) != -1: 

150 if type(first_non_nan_element := arr[first_non_null]) is str: 

151 # GH#32264 np.str_ object 

152 guessed_format = guess_datetime_format( 

153 first_non_nan_element, dayfirst=dayfirst 

154 ) 

155 if guessed_format is not None: 

156 return guessed_format 

157 # If there are multiple non-null elements, warn about 

158 # how parsing might not be consistent 

159 if tslib.first_non_null(arr[first_non_null + 1 :]) != -1: 

160 warnings.warn( 

161 "Could not infer format, so each element will be parsed " 

162 "individually, falling back to `dateutil`. To ensure parsing is " 

163 "consistent and as-expected, please specify a format.", 

164 UserWarning, 

165 stacklevel=find_stack_level(), 

166 ) 

167 return None 

168 

169 

170def should_cache( 

171 arg: ArrayConvertible, 

172 unique_share: float = 0.7, 

173 check_count: int | None = None, 

174 format: str | None = None, 

175 unit: str | None = None, 

176) -> bool: 

177 """ 

178 Decides whether to do caching. 

179 

180 If the percent of unique elements among `check_count` elements less 

181 than `unique_share * 100` then we can do caching. 

182 

183 Parameters 

184 ---------- 

185 arg: listlike, tuple, 1-d array, Series 

186 unique_share: float, default=0.7, optional 

187 0 < unique_share < 1 

188 check_count: int, optional 

189 0 <= check_count <= len(arg) 

190 format : str or None, default None 

191 Strftime format to parse time. 

192 unit : str or None, default None 

193 The unit of the arg (e.g. 's', 'ms'). 

194 

195 Returns 

196 ------- 

197 do_caching: bool 

198 

199 Notes 

200 ----- 

201 By default for a sequence of less than 50 items in size, we don't do 

202 caching; for the number of elements less than 5000, we take ten percent of 

203 all elements to check for a uniqueness share; if the sequence size is more 

204 than 5000, then we check only the first 500 elements. 

205 All constants were chosen empirically by. 

206 """ 

207 # GH#65380 O(1) bail for input shapes where caching cannot help: a 

208 # numeric+unit cast, an already-datetime dtype, or an ISO 8601 format 

209 # (parsed by the C parser rather than strptime) is a vectorized 

210 # conversion, so deduplicating the input only adds overhead. 

211 # NB: a non-ISO ``format`` is intentionally *not* a bail condition -- 

212 # strptime parsing of highly-duplicated strings is exactly where caching 

213 # pays off (GH#65380 originally bailed on any format and regressed those). 

214 if unit is not None: 

215 return False 

216 if format is not None and format_is_iso(format): 

217 return False 

218 arg_dtype = getattr(arg, "dtype", None) 

219 if ( 

220 lib.is_np_dtype(arg_dtype, "M") 

221 or isinstance(arg_dtype, DatetimeTZDtype) 

222 or (isinstance(arg_dtype, ArrowDtype) and arg_dtype.type is Timestamp) 

223 ): 

224 return False 

225 

226 do_caching = True 

227 

228 # default realization 

229 if check_count is None: 

230 # in this case, the gain from caching is negligible 

231 if len(arg) <= start_caching_at: 

232 return False 

233 

234 if len(arg) <= 5000: 

235 check_count = len(arg) // 10 

236 else: 

237 check_count = 500 

238 else: 

239 assert 0 <= check_count <= len(arg), ( 

240 "check_count must be in next bounds: [0; len(arg)]" 

241 ) 

242 if check_count == 0: 

243 return False 

244 

245 assert 0 < unique_share < 1, "unique_share must be in next bounds: (0; 1)" 

246 

247 try: 

248 # We can't cache if the items are not hashable. 

249 unique_elements = set(islice(arg, check_count)) 

250 except TypeError: 

251 return False 

252 if len(unique_elements) > check_count * unique_share: 

253 do_caching = False 

254 return do_caching 

255 

256 

257def _maybe_cache( 

258 arg: ArrayConvertible, 

259 format: str | None, 

260 cache: bool, 

261 convert_listlike: Callable, 

262 unit: str | None = None, 

263) -> Series: 

264 """ 

265 Create a cache of unique dates from an array of dates 

266 

267 Parameters 

268 ---------- 

269 arg : listlike, tuple, 1-d array, Series 

270 format : string 

271 Strftime format to parse time 

272 cache : bool 

273 True attempts to create a cache of converted values 

274 convert_listlike : function 

275 Conversion function to apply on dates 

276 unit : str, optional 

277 The unit of the arg. 

278 

279 Returns 

280 ------- 

281 cache_array : Series 

282 Cache of converted, unique dates. Can be empty 

283 """ 

284 from pandas import Series 

285 

286 cache_array = Series(dtype=object) 

287 

288 if cache: 

289 # Perform a quicker unique check 

290 if not should_cache(arg, format=format, unit=unit): 

291 return cache_array 

292 

293 if not isinstance(arg, (np.ndarray, ExtensionArray, Index, ABCSeries)): 

294 arg = np.array(arg) 

295 

296 unique_dates = unique(arg) 

297 if len(unique_dates) < len(arg): 

298 cache_dates = convert_listlike(unique_dates, format) 

299 # GH#45319 

300 try: 

301 cache_array = Series(cache_dates, index=unique_dates, copy=False) 

302 except OutOfBoundsDatetime: 

303 return cache_array 

304 # GH#39882 and GH#35888 in case of None and NaT we get duplicates 

305 if not cache_array.index.is_unique: 

306 cache_array = cache_array[~cache_array.index.duplicated()] 

307 return cache_array 

308 

309 

310def _box_as_indexlike( 

311 dt_array: ArrayLike, utc: bool = False, name: Hashable | None = None 

312) -> Index: 

313 """ 

314 Properly boxes the ndarray of datetimes to DatetimeIndex 

315 if it is possible or to generic Index instead 

316 

317 Parameters 

318 ---------- 

319 dt_array: 1-d array 

320 Array of datetimes to be wrapped in an Index. 

321 utc : bool 

322 Whether to convert/localize timestamps to UTC. 

323 name : string, default None 

324 Name for a resulting index 

325 

326 Returns 

327 ------- 

328 result : datetime of converted dates 

329 - DatetimeIndex if convertible to sole datetime64 type 

330 - general Index otherwise 

331 """ 

332 

333 if lib.is_np_dtype(dt_array.dtype, "M"): 

334 tz = "utc" if utc else None 

335 return DatetimeIndex(dt_array, tz=tz, name=name) 

336 return Index(dt_array, name=name, dtype=dt_array.dtype) 

337 

338 

339def _convert_and_box_cache( 

340 arg: DatetimeScalarOrArrayConvertible, 

341 cache_array: Series, 

342 name: Hashable | None = None, 

343) -> Index: 

344 """ 

345 Convert array of dates with a cache and wrap the result in an Index. 

346 

347 Parameters 

348 ---------- 

349 arg : integer, float, string, datetime, list, tuple, 1-d array, Series 

350 cache_array : Series 

351 Cache of converted, unique dates 

352 name : string, default None 

353 Name for a DatetimeIndex 

354 

355 Returns 

356 ------- 

357 result : Index-like of converted dates 

358 """ 

359 from pandas import Series 

360 

361 # map can give back the input's own container, e.g. a Categorical, or object 

362 # dtype, so cast to what the uncached conversion produced (GH#28629) 

363 result = Series(arg, dtype=cache_array.index.dtype).map(cache_array) 

364 result = result.astype(cache_array.dtype) 

365 return _box_as_indexlike(result._values, utc=False, name=name) 

366 

367 

368def _convert_listlike_datetimes( 

369 arg, 

370 format: str | None, 

371 name: Hashable | None = None, 

372 utc: bool = False, 

373 unit: str | None = None, 

374 errors: DateTimeErrorChoices = "raise", 

375 dayfirst: bool | None = None, 

376 yearfirst: bool | None = None, 

377 exact: bool = True, 

378): 

379 """ 

380 Helper function for to_datetime. Performs the conversions of 1D listlike 

381 of dates 

382 

383 Parameters 

384 ---------- 

385 arg : list, tuple, ndarray, Series, Index 

386 date to be parsed 

387 name : object 

388 None or string for the Index name 

389 utc : bool 

390 Whether to convert/localize timestamps to UTC. 

391 unit : str 

392 None or string of the frequency of the passed data 

393 errors : str 

394 error handing behaviors from to_datetime, 'raise', 'coerce' 

395 dayfirst : bool 

396 dayfirst parsing behavior from to_datetime 

397 yearfirst : bool 

398 yearfirst parsing behavior from to_datetime 

399 exact : bool, default True 

400 exact format matching behavior from to_datetime 

401 

402 Returns 

403 ------- 

404 Index-like of parsed dates 

405 """ 

406 if isinstance(arg, (list, tuple)): 

407 arg = np.array(arg, dtype="O") 

408 elif isinstance(arg, NumpyExtensionArray): 

409 arg = np.array(arg) 

410 

411 arg_dtype = getattr(arg, "dtype", None) 

412 # these are shortcutable 

413 tz = "utc" if utc else None 

414 if isinstance(arg_dtype, DatetimeTZDtype): 

415 if not isinstance(arg, (DatetimeArray, DatetimeIndex)): 

416 return DatetimeIndex(arg, tz=tz, name=name) 

417 if utc: 

418 arg = arg.tz_convert(None).tz_localize("utc") 

419 return arg 

420 

421 elif isinstance(arg_dtype, ArrowDtype) and arg_dtype.type is Timestamp: 

422 # TODO: Combine with above if DTI/DTA supports Arrow timestamps 

423 if utc: 

424 # pyarrow uses UTC, not lowercase utc 

425 if isinstance(arg, Index): 

426 arg_array = cast("ArrowExtensionArray", arg.array) 

427 if arg_dtype.pyarrow_dtype.tz is not None: 

428 arg_array = arg_array._dt_tz_convert("UTC") 

429 else: 

430 arg_array = arg_array._dt_tz_localize("UTC") 

431 arg = Index(arg_array, copy=False) 

432 # ArrowExtensionArray 

433 elif arg_dtype.pyarrow_dtype.tz is not None: 

434 arg = arg._dt_tz_convert("UTC") 

435 else: 

436 arg = arg._dt_tz_localize("UTC") 

437 return arg 

438 

439 elif lib.is_np_dtype(arg_dtype, "M"): 

440 if not is_supported_dtype(arg_dtype): 

441 # We go to closest supported reso, i.e. "s" 

442 arg = astype_overflowsafe( 

443 np.asarray(arg), 

444 np.dtype("M8[s]"), 

445 is_coerce=errors == "coerce", 

446 ) 

447 

448 if not isinstance(arg, (DatetimeArray, DatetimeIndex)): 

449 return DatetimeIndex(arg, tz=tz, name=name) 

450 elif utc: 

451 # DatetimeArray, DatetimeIndex 

452 return arg.tz_localize("utc") 

453 

454 return arg 

455 

456 elif unit is not None: 

457 if format is not None: 

458 raise ValueError("cannot specify both format and unit") 

459 return _to_datetime_with_unit( 

460 arg, unit, name, utc, errors, dayfirst=dayfirst, yearfirst=yearfirst 

461 ) 

462 elif getattr(arg, "ndim", 1) > 1: 

463 raise TypeError( 

464 "arg must be a string, datetime, list, tuple, 1-d array, or Series" 

465 ) 

466 

467 # warn if passing timedelta64, raise for PeriodDtype 

468 # NB: this must come after unit transformation 

469 try: 

470 arg, _ = maybe_convert_dtype(arg, copy=False, tz=libtimezones.maybe_get_tz(tz)) 

471 except TypeError: 

472 if errors == "coerce": 

473 npvalues = np.full(len(arg), np.datetime64("NaT", "ns")) 

474 return DatetimeIndex(npvalues, name=name) 

475 raise 

476 

477 arg = ensure_object(arg) 

478 

479 if format is None: 

480 format = _guess_datetime_format_for_array(arg, dayfirst=dayfirst) 

481 

482 # `format` could be inferred, or user didn't ask for mixed-format parsing. 

483 if format is not None and format != "mixed": 

484 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors) 

485 

486 result, tz_parsed = objects_to_datetime64( 

487 arg, 

488 dayfirst=dayfirst, 

489 yearfirst=yearfirst, 

490 utc=utc, 

491 errors=errors, 

492 ) 

493 

494 if tz_parsed is not None: 

495 # We can take a shortcut since the datetime64 numpy array 

496 # is in UTC 

497 out_unit = np.datetime_data(result.dtype)[0] 

498 out_unit = cast("TimeUnit", out_unit) 

499 dtype = tz_to_dtype(tz_parsed, out_unit) 

500 dt64_values = result.view(f"M8[{dtype.unit}]") 

501 dta = DatetimeArray._simple_new(dt64_values, dtype=dtype) 

502 return DatetimeIndex._simple_new(dta, name=name) 

503 

504 return _box_as_indexlike(result, utc=utc, name=name) 

505 

506 

507def _array_strptime_with_fallback( 

508 arg, 

509 name, 

510 utc: bool, 

511 fmt: str, 

512 exact: bool, 

513 errors: str, 

514) -> Index: 

515 """ 

516 Call array_strptime, with fallback behavior depending on 'errors'. 

517 """ 

518 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc) 

519 if tz_out is not None: 

520 unit = np.datetime_data(result.dtype)[0] 

521 unit = cast("TimeUnit", unit) 

522 dtype = DatetimeTZDtype(tz=tz_out, unit=unit) 

523 dta = DatetimeArray._simple_new(result, dtype=dtype) 

524 if utc: 

525 dta = dta.tz_convert("UTC") 

526 return Index(dta, name=name, copy=False) 

527 elif result.dtype != object and utc: 

528 unit = np.datetime_data(result.dtype)[0] 

529 unit = cast("TimeUnit", unit) 

530 res = Index(result, dtype=f"M8[{unit}, UTC]", name=name, copy=False) 

531 return res 

532 return Index(result, dtype=result.dtype, name=name, copy=False) 

533 

534 

535def _to_datetime_with_unit( 

536 arg, 

537 unit, 

538 name, 

539 utc: bool, 

540 errors: str, 

541 dayfirst, 

542 yearfirst, 

543) -> Index: 

544 """ 

545 to_datetime specalized to the case where a 'unit' is passed. 

546 """ 

547 arg = extract_array(arg, extract_numpy=True) 

548 

549 # convert to an ndarray because tslib.array_to_datetime expects an ndarray argument 

550 if isinstance(arg, IntegerArray): 

551 mask = arg._mask if arg._hasna else None 

552 arg = arg._data 

553 if mask is not None: 

554 # set to 0 to avoid OOB on masked values (setting iNaT only works for int64) 

555 # will get converted to NaT later 

556 arg = arg.copy() 

557 arg[mask] = 0 

558 else: 

559 mask = None 

560 arg = np.asarray(arg) 

561 

562 if arg.dtype.kind in "iu": 

563 # Note we can't do "f" here because that could induce unwanted 

564 # rounding GH#14156, GH#20445 

565 

566 # GH#60677 unsigned integers > int64 max overflow silently 

567 # when cast to datetime64 (which is backed by int64) 

568 if arg.dtype == np.dtype("uint64"): 

569 mask_oob = arg > np.iinfo(np.int64).max 

570 if mask_oob.any(): 

571 if errors == "raise": 

572 raise OutOfBoundsDatetime( 

573 f"cannot convert input with unit '{unit}'" 

574 ) 

575 

576 arg = arg.astype(object) 

577 if mask is not None: 

578 arg[mask] = None 

579 return _to_datetime_with_unit( 

580 arg, unit, name, utc, errors, dayfirst, yearfirst 

581 ) 

582 arr = arg.astype(f"datetime64[{unit}]", copy=False) 

583 if unit not in ["us", "ns"]: 

584 out_unit = get_default_reso(unit) 

585 try: 

586 arr = astype_overflowsafe(arr, np.dtype(f"M8[{out_unit}]"), copy=False) 

587 except OutOfBoundsDatetime: 

588 if errors == "raise": 

589 raise 

590 arg = arg.astype(object) 

591 if mask is not None: 

592 arg[mask] = None 

593 return _to_datetime_with_unit( 

594 arg, unit, name, utc, errors, dayfirst, yearfirst 

595 ) 

596 if mask is not None: 

597 arr[mask] = iNaT 

598 tz_parsed = None 

599 

600 elif arg.dtype.kind == "f": 

601 mask = np.isnan(arg) 

602 with np.errstate(invalid="ignore"): 

603 int_values = arg.astype(np.int64) 

604 # On ARM, float-to-int64 overflow saturates to INT64_MAX 

605 # instead of wrapping, which makes the arg == int_values 

606 # check pass incorrectly for OOB values like float(2**63). 

607 # Exclude values outside the int64 domain from the check. 

608 i64 = np.iinfo(np.int64) 

609 in_int64_range = (arg >= np.float64(i64.min)) & (arg < np.float64(i64.max)) 

610 if (mask | (in_int64_range & (arg == int_values))).all(): 

611 # With all-round-or-NaN entries, we give the requested unit 

612 # back like with integers 

613 result = _to_datetime_with_unit( 

614 int_values, 

615 unit=unit, 

616 name=name, 

617 utc=utc, 

618 errors=errors, 

619 dayfirst=dayfirst, 

620 yearfirst=yearfirst, 

621 ) 

622 result._data[mask] = NaT 

623 return result 

624 

625 arg = arg.astype("float64", copy=False) 

626 with np.errstate(over="raise"): 

627 try: 

628 arr = cast_from_unit_vectorized(arg, unit=unit) 

629 except OutOfBoundsDatetime as err: 

630 if errors != "raise": 

631 return _to_datetime_with_unit( 

632 arg.astype(object), 

633 unit, 

634 name, 

635 utc, 

636 errors, 

637 dayfirst, 

638 yearfirst, 

639 ) 

640 raise OutOfBoundsDatetime( 

641 f"cannot convert input with unit '{unit}'" 

642 ) from err 

643 

644 arr = arr.view("M8[ns]") 

645 tz_parsed = None 

646 else: 

647 arg = arg.astype(object, copy=False) 

648 arr, tz_parsed = tslib.array_to_datetime( 

649 arg, 

650 utc=utc, 

651 errors=errors, 

652 dayfirst=dayfirst, 

653 yearfirst=yearfirst, 

654 unit_for_numerics=unit, 

655 ) 

656 

657 result = DatetimeIndex(arr, name=name) 

658 if not isinstance(result, DatetimeIndex): 

659 return result 

660 

661 # GH#23758: We may still need to localize the result with tz 

662 # GH#25546: Apply tz_parsed first (from arg), then tz (from caller) 

663 # result will be naive but in UTC 

664 result = result.tz_localize("UTC").tz_convert(tz_parsed) 

665 

666 if utc: 

667 if result.tz is None: 

668 result = result.tz_localize("utc") 

669 else: 

670 result = result.tz_convert("utc") 

671 return result 

672 

673 

674def _adjust_to_origin(arg, origin, unit, errors: DateTimeErrorChoices = "raise"): 

675 """ 

676 Helper function for to_datetime. 

677 Adjust input argument to the specified origin 

678 

679 Parameters 

680 ---------- 

681 arg : list, tuple, ndarray, Series, Index 

682 date to be adjusted 

683 origin : 'julian' or Timestamp 

684 origin offset for the arg 

685 unit : str 

686 passed unit from to_datetime, must be 'D' 

687 errors : {'raise', 'coerce'}, default 'raise' 

688 under ``"coerce"``, values for which ``origin + value`` overflows 

689 datetime64 become ``NaT`` instead of raising. 

690 

691 Returns 

692 ------- 

693 DatetimeArray, ndarray, or scalar of adjusted date(s) 

694 """ 

695 if origin == "julian": 

696 original = arg 

697 j0 = Timestamp(0).to_julian_date() 

698 if unit != "D": 

699 raise ValueError("unit must be 'D' for origin='julian'") 

700 try: 

701 arg = arg - j0 

702 except TypeError as err: 

703 raise ValueError( 

704 "incompatible 'arg' type for given 'origin'='julian'" 

705 ) from err 

706 

707 # preemptively check this for a nice range 

708 j_max = Timestamp.max.to_julian_date() - j0 

709 j_min = Timestamp.min.to_julian_date() - j0 

710 if np.any(arg > j_max) or np.any(arg < j_min): 

711 raise OutOfBoundsDatetime( 

712 f"{original} is Out of Bounds for origin='julian'" 

713 ) 

714 else: 

715 # arg must be numeric 

716 if not ( 

717 (is_integer(arg) or is_float(arg)) or is_numeric_dtype(np.asarray(arg)) 

718 ): 

719 raise ValueError( 

720 f"'{arg}' is not compatible with origin='{origin}'; " 

721 "it must be numeric with a unit specified" 

722 ) 

723 

724 # we are going to offset back to unix / epoch time 

725 try: 

726 if lib.is_integer(origin) or lib.is_float(origin): 

727 offset = Timestamp(origin, unit=unit) 

728 else: 

729 offset = Timestamp(origin) 

730 except OutOfBoundsDatetime as err: 

731 raise OutOfBoundsDatetime(f"origin {origin} is Out of Bounds") from err 

732 except ValueError as err: 

733 raise ValueError( 

734 f"origin {origin} cannot be converted to a Timestamp" 

735 ) from err 

736 

737 if offset.tz is not None: 

738 raise ValueError(f"origin offset {offset} must be tz-naive") 

739 tda = extract_array(to_timedelta(arg, unit=unit, errors=errors)) 

740 try: 

741 arg = offset + tda 

742 except (OutOfBoundsDatetime, OutOfBoundsTimedelta, OverflowError) as err: 

743 # GH#63419 the timedeltas fit their own resolution, but 

744 # offset + tda overflows datetime64 

745 if errors == "raise": 

746 if isinstance(err, OutOfBoundsDatetime): 

747 raise 

748 raise OutOfBoundsDatetime( 

749 f"cannot add values to origin {origin} without overflow" 

750 ) from err 

751 if isinstance(tda, Timedelta): 

752 arg = NaT 

753 else: 

754 arg = _coerce_origin_overflow(offset, tda) 

755 return arg 

756 

757 

758def _coerce_origin_overflow(offset: Timestamp, tda: TimedeltaArray) -> DatetimeArray: 

759 """ 

760 Redo ``offset + tda`` with entries that cannot be represented in the 

761 resolution the addition would produce coerced to NaT. 

762 """ 

763 res_unit = tda.unit if tda._creso >= offset._creso else offset.unit 

764 pps_res = periods_per_second(max(tda._creso, offset._creso)) 

765 # exact conversion factors as Python ints to avoid intermediate overflow 

766 td_factor = pps_res // periods_per_second(tda._creso) 

767 off_factor = pps_res // periods_per_second(offset._creso) 

768 offset_i8 = int(offset._value) * off_factor 

769 

770 i8max = np.iinfo(np.int64).max 

771 if not -i8max <= offset_i8 <= i8max: 

772 # the origin itself is not representable in the result resolution, 

773 # so no entry can be computed 

774 nat_vals = np.full(len(tda), iNaT).view(f"M8[{res_unit}]") 

775 return DatetimeArray._simple_new(nat_vals, dtype=nat_vals.dtype) 

776 

777 # valid iff the sum stays within [-i8max, i8max] (-2**63 is the NaT 

778 # sentinel) and the timedelta itself is castable to the result unit 

779 hi = min((i8max - offset_i8) // td_factor, i8max // td_factor) 

780 lo = max(-((i8max + offset_i8) // td_factor), -(i8max // td_factor)) 

781 

782 i8vals = tda.asi8 

783 mask = (i8vals < lo) | (i8vals > hi) 

784 new_vals = np.where(mask, iNaT, i8vals).view(tda.dtype) 

785 tda = type(tda)._simple_new(new_vals, dtype=tda.dtype) 

786 return offset + tda 

787 

788 

789@overload 

790def to_datetime( 

791 arg: DatetimeScalar, 

792 errors: DateTimeErrorChoices = ..., 

793 dayfirst: bool = ..., 

794 yearfirst: bool = ..., 

795 utc: bool = ..., 

796 format: str | None = ..., 

797 exact: bool = ..., 

798 unit: str | None = ..., 

799 origin=..., 

800 cache: bool = ..., 

801) -> Timestamp: ... 

802 

803 

804@overload 

805def to_datetime( 

806 arg: Series | DictConvertible, 

807 errors: DateTimeErrorChoices = ..., 

808 dayfirst: bool = ..., 

809 yearfirst: bool = ..., 

810 utc: bool = ..., 

811 format: str | None = ..., 

812 exact: bool = ..., 

813 unit: str | None = ..., 

814 origin=..., 

815 cache: bool = ..., 

816) -> Series: ... 

817 

818 

819@overload 

820def to_datetime( 

821 arg: list | tuple | Index | ArrayLike, 

822 errors: DateTimeErrorChoices = ..., 

823 dayfirst: bool = ..., 

824 yearfirst: bool = ..., 

825 utc: bool = ..., 

826 format: str | None = ..., 

827 exact: bool = ..., 

828 unit: str | None = ..., 

829 origin=..., 

830 cache: bool = ..., 

831) -> DatetimeIndex: ... 

832 

833 

834@set_module("pandas") 

835def to_datetime( 

836 arg: DatetimeScalarOrArrayConvertible | DictConvertible, 

837 errors: DateTimeErrorChoices = "raise", 

838 dayfirst: bool = False, 

839 yearfirst: bool = False, 

840 utc: bool = False, 

841 format: str | None = None, 

842 exact: bool | lib.NoDefault = lib.no_default, 

843 unit: str | None = None, 

844 origin: str = "unix", 

845 cache: bool = True, 

846) -> DatetimeIndex | Series | DatetimeScalar | NaTType: 

847 """ 

848 Convert argument to datetime. 

849 

850 This function converts a scalar, array-like, :class:`Series` or 

851 :class:`DataFrame`/dict-like to a pandas datetime object. 

852 

853 Parameters 

854 ---------- 

855 arg : int, float, str, datetime, list, tuple, 1-d array, Series, DataFrame/dict-like 

856 The object to convert to a datetime. If a :class:`DataFrame` is provided, the 

857 method expects minimally the following columns: :const:`"year"`, 

858 :const:`"month"`, :const:`"day"`. The column "year" 

859 must be specified in 4-digit format. 

860 errors : {'raise', 'coerce'}, default 'raise' 

861 - If :const:`'raise'`, then invalid parsing will raise an exception. 

862 - If :const:`'coerce'`, then invalid parsing will be set as :const:`NaT`. 

863 dayfirst : bool, default False 

864 Specify a date parse order if `arg` is str or is list-like. 

865 If :const:`True`, parses dates with the day first, e.g. :const:`"10/11/12"` 

866 is parsed as :const:`2012-11-10`. 

867 

868 .. warning:: 

869 

870 ``dayfirst=True`` is not strict, but will prefer to parse 

871 with day first. 

872 

873 yearfirst : bool, default False 

874 Specify a date parse order if `arg` is str or is list-like. 

875 

876 - If :const:`True` parses dates with the year first, e.g. 

877 :const:`"10/11/12"` is parsed as :const:`2010-11-12`. 

878 - If both `dayfirst` and `yearfirst` are :const:`True`, `yearfirst` is 

879 preceded (same as ``dateutil``). 

880 

881 .. warning:: 

882 

883 ``yearfirst=True`` is not strict, but will prefer to parse 

884 with year first. 

885 

886 utc : bool, default False 

887 Control timezone-related parsing, localization and conversion. 

888 

889 - If :const:`True`, the function *always* returns a timezone-aware 

890 UTC-localized :class:`Timestamp`, :class:`Series` or 

891 :class:`DatetimeIndex`. To do this, timezone-naive inputs are 

892 *localized* as UTC, while timezone-aware inputs are *converted* to UTC. 

893 

894 - If :const:`False` (default), inputs will not be coerced to UTC. 

895 Timezone-naive inputs will remain naive, while timezone-aware ones 

896 will keep their time offsets. Limitations exist for mixed 

897 offsets (typically, daylight savings), see :ref:`Examples 

898 <to_datetime_tz_examples>` section for details. 

899 

900 See also: pandas general documentation about `timezone conversion and 

901 localization 

902 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html 

903 #time-zone-handling>`_. 

904 

905 format : str, default None 

906 The strftime to parse time, e.g. :const:`"%d/%m/%Y"`. See 

907 `strftime documentation 

908 <https://docs.python.org/3/library/datetime.html 

909 #strftime-and-strptime-behavior>`_ for more information on choices, though 

910 note that :const:`"%f"` will parse all the way up to nanoseconds. 

911 You can also pass: 

912 

913 - "ISO8601", to parse any `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ 

914 time string (not necessarily in exactly the same format); 

915 - "mixed", to infer the format for each element individually. This is risky, 

916 and you should probably use it along with `dayfirst`. 

917 

918 .. deprecated:: 3.1.0 

919 

920 Passing integer or float values together with ``format`` is 

921 deprecated; cast them to strings first. In a future version numeric 

922 values will be interpreted as epochs via ``unit`` instead, and 

923 ``format`` will only apply to string input. 

924 

925 .. note:: 

926 

927 If a :class:`DataFrame` is passed, then `format` has no effect. 

928 

929 .. note:: 

930 

931 When using ``format``, any datetime components not present in the 

932 format string default to ``1900-01-01 00:00:00``, consistent with 

933 Python's :meth:`datetime.datetime.strptime` behavior. For example, 

934 ``to_datetime(["1", "2"], format="%d")`` returns dates in January 1900. 

935 The ``origin`` parameter does not affect string parsing via ``format``; 

936 it only applies to numeric input interpreted through ``unit``. 

937 

938 exact : bool, default True 

939 Control how `format` is used: 

940 

941 - If :const:`True`, require an exact `format` match. 

942 - If :const:`False`, allow the `format` to match anywhere in the target 

943 string. 

944 

945 Cannot be used alongside ``format='ISO8601'`` or ``format='mixed'``. 

946 unit : str, default 'ns' 

947 The unit of the numeric arg (Y, M, W, D, h, m, s, ms, us, ns, ps, 

948 fs, as). Specifies the unit of the input values when `arg` is numeric 

949 (int or float), interpreted relative to ``origin``. 

950 For example, with ``unit='ms'`` and ``origin='unix'``, the input values 

951 are treated as millisecond offsets from the Unix epoch (1970-01-01). 

952 

953 This does not truncate or round datetime-like inputs to the given unit. 

954 To change the resolution of the result, use :meth:`Series.dt.as_unit`. 

955 To truncate datetime values, use :meth:`Series.dt.floor` or 

956 :meth:`Series.dt.normalize`. 

957 

958 Only applicable to numeric input; has no effect on datetime-like input 

959 or when ``format`` is specified. 

960 origin : scalar, default 'unix' 

961 Define the reference date. The numeric values would be parsed as number 

962 of units (defined by ``unit``) since this reference date. 

963 

964 - If :const:`'unix'` (or POSIX) time; origin is set to 1970-01-01. 

965 - If :const:`'julian'`, unit must be :const:`'D'`, and origin is set to 

966 beginning of Julian Calendar. Julian day number :const:`0` is assigned 

967 to the day starting at noon on January 1, 4713 BC. 

968 - If Timestamp convertible (Timestamp, dt.datetime, np.datetime64 or date 

969 string), origin is set to Timestamp identified by origin. 

970 - If a float or integer, origin is the difference 

971 (in units determined by the ``unit`` argument) relative to 1970-01-01. 

972 

973 .. note:: 

974 

975 This parameter only affects numeric input used with ``unit``. 

976 It does not affect string parsing via ``format``. See the ``format`` 

977 parameter for how defaults are handled during string parsing. 

978 cache : bool, default True 

979 If :const:`True`, use a cache of unique, converted dates to apply the 

980 datetime conversion. May produce significant speed-up when parsing 

981 duplicate date strings, especially ones with timezone offsets. The cache 

982 is only used when there are at least 50 values. The presence of 

983 out-of-bounds values will render the cache unusable and may slow down 

984 parsing. 

985 

986 Returns 

987 ------- 

988 datetime 

989 If parsing succeeded. 

990 Return type depends on input (types in parenthesis correspond to 

991 fallback in case of unsuccessful timezone or out-of-range timestamp 

992 parsing): 

993 

994 - scalar: :class:`Timestamp` (or :class:`datetime.datetime`) 

995 - array-like: :class:`DatetimeIndex` (or :class:`Series` with 

996 :class:`object` dtype containing :class:`datetime.datetime`) 

997 - Series: :class:`Series` of :class:`datetime64` dtype (or 

998 :class:`Series` of :class:`object` dtype containing 

999 :class:`datetime.datetime`) 

1000 - DataFrame: :class:`Series` of :class:`datetime64` dtype (or 

1001 :class:`Series` of :class:`object` dtype containing 

1002 :class:`datetime.datetime`) 

1003 

1004 Raises 

1005 ------ 

1006 ParserError 

1007 When parsing a date from string fails. 

1008 ValueError 

1009 When another datetime conversion error happens. For example when one 

1010 of 'year', 'month', day' columns is missing in a :class:`DataFrame`, or 

1011 when a Timezone-aware :class:`datetime.datetime` is found in an array-like 

1012 of mixed time offsets, and ``utc=False``, or when parsing datetimes 

1013 with mixed time zones unless ``utc=True``. If parsing datetimes with mixed 

1014 time zones, please specify ``utc=True``. 

1015 

1016 See Also 

1017 -------- 

1018 DataFrame.astype : Cast argument to a specified dtype. 

1019 to_timedelta : Convert argument to timedelta. 

1020 convert_dtypes : Convert dtypes. 

1021 

1022 Notes 

1023 ----- 

1024 

1025 Many input types are supported, and lead to different output types: 

1026 

1027 - **scalars** can be int, float, str, datetime object (from stdlib :mod:`datetime` 

1028 module or :mod:`numpy`). They are converted to :class:`Timestamp` when 

1029 possible, otherwise they are converted to :class:`datetime.datetime`. 

1030 None/NaN/null scalars are converted to :const:`NaT`. 

1031 

1032 - **array-like** can contain int, float, str, datetime objects. They are 

1033 converted to :class:`DatetimeIndex` when possible, otherwise they are 

1034 converted to :class:`Index` with :class:`object` dtype, containing 

1035 :class:`datetime.datetime`. None/NaN/null entries are converted to 

1036 :const:`NaT` in both cases. 

1037 

1038 - **Series** are converted to :class:`Series` with :class:`datetime64` 

1039 dtype when possible, otherwise they are converted to :class:`Series` with 

1040 :class:`object` dtype, containing :class:`datetime.datetime`. None/NaN/null 

1041 entries are converted to :const:`NaT` in both cases. 

1042 

1043 - **DataFrame/dict-like** are converted to :class:`Series` with 

1044 :class:`datetime64` dtype. For each row a datetime is created from assembling 

1045 the various dataframe columns. Column keys can be common abbreviations 

1046 like ['year', 'month', 'day', 'minute', 'second', 'ms', 'us', 'ns']) or 

1047 plurals of the same. 

1048 

1049 The following causes are responsible for :class:`datetime.datetime` objects 

1050 being returned (possibly inside an :class:`Index` or a :class:`Series` with 

1051 :class:`object` dtype) instead of a proper pandas designated type 

1052 (:class:`Timestamp`, :class:`DatetimeIndex` or :class:`Series` 

1053 with :class:`datetime64` dtype): 

1054 

1055 - when any input element is before :const:`Timestamp.min` or after 

1056 :const:`Timestamp.max`, see `timestamp limitations 

1057 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html 

1058 #timeseries-timestamp-limits>`_. 

1059 

1060 - when ``utc=False`` (default) and the input is an array-like or 

1061 :class:`Series` containing mixed naive/aware datetime, or aware with mixed 

1062 time offsets. Note that this happens in the (quite frequent) situation when 

1063 the timezone has a daylight savings policy. In that case you may wish to 

1064 use ``utc=True``. 

1065 

1066 Examples 

1067 -------- 

1068 

1069 **Handling various input formats** 

1070 

1071 Assembling a datetime from multiple columns of a :class:`DataFrame`. The keys 

1072 can be common abbreviations like ['year', 'month', 'day', 'minute', 'second', 

1073 'ms', 'us', 'ns']) or plurals of the same 

1074 

1075 >>> df = pd.DataFrame({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]}) 

1076 >>> pd.to_datetime(df) 

1077 0 2015-02-04 

1078 1 2016-03-05 

1079 dtype: datetime64[us] 

1080 

1081 Using a unix epoch time 

1082 

1083 >>> pd.to_datetime(1490195805, unit="s") 

1084 Timestamp('2017-03-22 15:16:45') 

1085 >>> pd.to_datetime(1490195805433502912, unit="ns") 

1086 Timestamp('2017-03-22 15:16:45.433502912') 

1087 

1088 .. warning:: For float arg, precision rounding might happen. To prevent 

1089 unexpected behavior use a fixed-width exact type. 

1090 

1091 Using a non-unix epoch origin 

1092 

1093 >>> pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01")) 

1094 DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], 

1095 dtype='datetime64[us]', freq=None) 

1096 

1097 **Differences with strptime behavior** 

1098 

1099 :const:`"%f"` will parse all the way up to nanoseconds. 

1100 

1101 >>> pd.to_datetime("2018-10-26 12:00:00.0000000011", format="%Y-%m-%d %H:%M:%S.%f") 

1102 Timestamp('2018-10-26 12:00:00.000000001') 

1103 

1104 **Non-convertible date/times** 

1105 

1106 Passing ``errors='coerce'`` will force an out-of-bounds date to :const:`NaT`, 

1107 in addition to forcing non-dates (or non-parseable dates) to :const:`NaT`. 

1108 

1109 >>> pd.to_datetime("invalid for Ymd", format="%Y%m%d", errors="coerce") 

1110 NaT 

1111 

1112 .. _to_datetime_tz_examples: 

1113 

1114 **Timezones and time offsets** 

1115 

1116 The default behaviour (``utc=False``) is as follows: 

1117 

1118 - Timezone-naive inputs are converted to timezone-naive :class:`DatetimeIndex`: 

1119 

1120 >>> pd.to_datetime(["2018-10-26 12:00:00", "2018-10-26 13:00:15"]) 

1121 DatetimeIndex(['2018-10-26 12:00:00', '2018-10-26 13:00:15'], 

1122 dtype='datetime64[us]', freq=None) 

1123 

1124 - Timezone-aware inputs *with constant time offset* are converted to 

1125 timezone-aware :class:`DatetimeIndex`: 

1126 

1127 >>> pd.to_datetime(["2018-10-26 12:00 -0500", "2018-10-26 13:00 -0500"]) 

1128 DatetimeIndex(['2018-10-26 12:00:00-05:00', '2018-10-26 13:00:00-05:00'], 

1129 dtype='datetime64[us, UTC-05:00]', freq=None) 

1130 

1131 - However, timezone-aware inputs *with mixed time offsets* (for example 

1132 issued from a timezone with daylight savings, such as Europe/Paris) 

1133 are **not successfully converted** to a :class:`DatetimeIndex`. 

1134 Parsing datetimes with mixed time zones will raise a ValueError unless 

1135 ``utc=True``: 

1136 

1137 >>> pd.to_datetime( 

1138 ... ["2020-10-25 02:00 +0200", "2020-10-25 04:00 +0100"] 

1139 ... ) # doctest: +SKIP 

1140 ValueError: Mixed timezones detected. Pass utc=True in to_datetime 

1141 or tz='UTC' in DatetimeIndex to convert to a common timezone. 

1142 

1143 - To create a :class:`Series` with mixed offsets and ``object`` dtype, please use 

1144 :meth:`Series.apply` and :func:`datetime.datetime.strptime`: 

1145 

1146 >>> import datetime as dt 

1147 >>> ser = pd.Series(["2020-10-25 02:00 +0200", "2020-10-25 04:00 +0100"]) 

1148 >>> ser.apply(lambda x: dt.datetime.strptime(x, "%Y-%m-%d %H:%M %z")) 

1149 0 2020-10-25 02:00:00+02:00 

1150 1 2020-10-25 04:00:00+01:00 

1151 dtype: object 

1152 

1153 - A mix of timezone-aware and timezone-naive inputs will also raise a ValueError 

1154 unless ``utc=True``: 

1155 

1156 >>> from datetime import datetime 

1157 >>> pd.to_datetime( 

1158 ... ["2020-01-01 01:00:00-01:00", datetime(2020, 1, 1, 3, 0)] 

1159 ... ) # doctest: +SKIP 

1160 ValueError: Mixed timezones detected. Pass utc=True in to_datetime 

1161 or tz='UTC' in DatetimeIndex to convert to a common timezone. 

1162 

1163 | 

1164 

1165 Setting ``utc=True`` solves most of the above issues: 

1166 

1167 - Timezone-naive inputs are *localized* as UTC 

1168 

1169 >>> pd.to_datetime(["2018-10-26 12:00", "2018-10-26 13:00"], utc=True) 

1170 DatetimeIndex(['2018-10-26 12:00:00+00:00', '2018-10-26 13:00:00+00:00'], 

1171 dtype='datetime64[us, UTC]', freq=None) 

1172 

1173 - Timezone-aware inputs are *converted* to UTC (the output represents the 

1174 exact same datetime, but viewed from the UTC time offset `+00:00`). 

1175 

1176 >>> pd.to_datetime(["2018-10-26 12:00 -0530", "2018-10-26 12:00 -0500"], utc=True) 

1177 DatetimeIndex(['2018-10-26 17:30:00+00:00', '2018-10-26 17:00:00+00:00'], 

1178 dtype='datetime64[us, UTC]', freq=None) 

1179 

1180 - Inputs can contain both string or datetime, the above 

1181 rules still apply 

1182 

1183 >>> pd.to_datetime(["2018-10-26 12:00", datetime(2020, 1, 1, 18)], utc=True) 

1184 DatetimeIndex(['2018-10-26 12:00:00+00:00', '2020-01-01 18:00:00+00:00'], 

1185 dtype='datetime64[us, UTC]', freq=None) 

1186 """ 

1187 if errors not in ("raise", "coerce"): 

1188 raise ValueError("errors must be one of 'raise', or 'coerce'.") 

1189 if exact is not lib.no_default and format in {"mixed", "ISO8601"}: 

1190 raise ValueError("Cannot use 'exact' when 'format' is 'mixed' or 'ISO8601'") 

1191 if arg is None: 

1192 return NaT 

1193 

1194 if origin != "unix": 

1195 # Capture name/index before _adjust_to_origin replaces arg with a 

1196 # DatetimeArray (or Timestamp scalar) for non-julian origins. 

1197 arg_name = getattr(arg, "name", None) 

1198 arg_index = arg.index if isinstance(arg, ABCSeries) else None 

1199 is_series = isinstance(arg, ABCSeries) 

1200 arg = _adjust_to_origin(arg, origin, unit, errors=errors) 

1201 if origin != "julian": 

1202 # GH#63419 _adjust_to_origin already produced the final datetime 

1203 # result; localize and re-wrap into the input's container type. 

1204 if utc: 

1205 arg = arg.tz_localize("utc") # type: ignore[union-attr] 

1206 if is_series: 

1207 from pandas import Series 

1208 

1209 return Series(arg, index=arg_index, name=arg_name) 

1210 if is_list_like(arg): 

1211 return DatetimeIndex(arg, name=arg_name) 

1212 else: 

1213 return arg # type: ignore[return-value] 

1214 

1215 convert_listlike = partial( 

1216 _convert_listlike_datetimes, 

1217 utc=utc, 

1218 unit=unit, 

1219 dayfirst=dayfirst, 

1220 yearfirst=yearfirst, 

1221 errors=errors, 

1222 exact=exact, # type: ignore[arg-type] 

1223 ) 

1224 result: Timestamp | NaTType | Series | Index 

1225 

1226 if isinstance(arg, Timestamp): 

1227 result = arg 

1228 if utc: 

1229 if arg.tz is not None: 

1230 result = arg.tz_convert("utc") 

1231 else: 

1232 result = arg.tz_localize("utc") 

1233 elif isinstance(arg, ABCSeries): 

1234 cache_array = _maybe_cache(arg, format, cache, convert_listlike, unit) 

1235 if not cache_array.empty: 

1236 # see _convert_and_box_cache re: astype 

1237 result = arg.map(cache_array).astype(cache_array.dtype) 

1238 else: 

1239 values = convert_listlike(arg._values, format) 

1240 result = arg._constructor(values, index=arg.index, name=arg.name) 

1241 elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)): 

1242 result = _assemble_from_unit_mappings(arg, errors, utc) 

1243 elif isinstance(arg, Index): 

1244 cache_array = _maybe_cache(arg, format, cache, convert_listlike, unit) 

1245 if not cache_array.empty: 

1246 result = _convert_and_box_cache(arg, cache_array, name=arg.name) 

1247 else: 

1248 result = convert_listlike(arg, format, name=arg.name) 

1249 elif is_list_like(arg): 

1250 try: 

1251 # error: Argument 1 to "_maybe_cache" has incompatible type 

1252 # "Union[float, str, datetime, List[Any], Tuple[Any, ...], ExtensionArray, 

1253 # ndarray[Any, Any], Series]"; expected "Union[List[Any], Tuple[Any, ...], 

1254 # Union[Union[ExtensionArray, ndarray[Any, Any]], Index, Series], Series]" 

1255 argc = cast( 

1256 "list | tuple | ExtensionArray | np.ndarray | Series | Index", arg 

1257 ) 

1258 cache_array = _maybe_cache(argc, format, cache, convert_listlike, unit) 

1259 except OutOfBoundsDatetime: 

1260 # caching attempts to create a DatetimeIndex, which may raise 

1261 # an OOB. If that's the desired behavior, then just reraise... 

1262 if errors == "raise": 

1263 raise 

1264 # ... otherwise, continue without the cache. 

1265 from pandas import Series 

1266 

1267 cache_array = Series([], dtype=object) # just an empty array 

1268 if not cache_array.empty: 

1269 result = _convert_and_box_cache(argc, cache_array) 

1270 else: 

1271 result = convert_listlike(argc, format) 

1272 else: 

1273 result = convert_listlike(np.array([arg]), format)[0] 

1274 if isinstance(arg, bool) and isinstance(result, np.bool_): 

1275 result = bool(result) # TODO: avoid this kludge. 

1276 

1277 # error: Incompatible return value type (got "Union[Timestamp, NaTType, 

1278 # Series, Index]", expected "Union[DatetimeIndex, Series, float, str, 

1279 # NaTType, None]") 

1280 return result # type: ignore[return-value] 

1281 

1282 

1283# mappings for assembling units 

1284_unit_map = { 

1285 "year": "year", 

1286 "years": "year", 

1287 "month": "month", 

1288 "months": "month", 

1289 "day": "day", 

1290 "days": "day", 

1291 "hour": "h", 

1292 "hours": "h", 

1293 "minute": "m", 

1294 "minutes": "m", 

1295 "second": "s", 

1296 "seconds": "s", 

1297 "ms": "ms", 

1298 "millisecond": "ms", 

1299 "milliseconds": "ms", 

1300 "us": "us", 

1301 "microsecond": "us", 

1302 "microseconds": "us", 

1303 "ns": "ns", 

1304 "nanosecond": "ns", 

1305 "nanoseconds": "ns", 

1306} 

1307 

1308 

1309def stringify_numeric_column(values: Series) -> Series: 

1310 """ 

1311 Cast a numeric column to object-dtype strings so that to_datetime with a 

1312 ``format`` doesn't have to infer how to interpret numeric input. GH#55663 

1313 

1314 Floats that can't be represented as int64 (e.g. +/-inf) get their plain 

1315 ``str`` form, which fails to parse downstream: raise for errors="raise", 

1316 NaT for errors="coerce". 

1317 """ 

1318 notna_mask = values.notna().to_numpy() 

1319 int_mask = notna_mask 

1320 if is_float_dtype(values.dtype): 

1321 iinfo = np.iinfo(np.int64) 

1322 # float64(iinfo.max) rounds up to 2**63, just out of range, so 

1323 # exclude the right endpoint 

1324 in_range = values.between(iinfo.min, iinfo.max, inclusive="left") 

1325 int_mask = notna_mask & in_range.to_numpy(dtype=bool, na_value=False) 

1326 

1327 result = np.full(len(values), np.nan, dtype=object) 

1328 oob_mask = notna_mask & ~int_mask 

1329 if oob_mask.any(): 

1330 result[oob_mask] = np.asarray(values[oob_mask].astype(str), dtype=object) 

1331 if int_mask.any(): 

1332 result[int_mask] = np.asarray( 

1333 values[int_mask].astype("int64").astype(str), dtype=object 

1334 ) 

1335 return values._constructor( 

1336 result, index=values.index, name=values.name, dtype=object 

1337 ) 

1338 

1339 

1340def _assemble_from_unit_mappings( 

1341 arg, errors: DateTimeErrorChoices, utc: bool 

1342) -> Series: 

1343 """ 

1344 assemble the unit specified fields from the arg (DataFrame) 

1345 Return a Series for actual parsing 

1346 

1347 Parameters 

1348 ---------- 

1349 arg : DataFrame 

1350 errors : {'raise', 'coerce'}, default 'raise' 

1351 

1352 - If :const:`'raise'`, then invalid parsing will raise an exception 

1353 - If :const:`'coerce'`, then invalid parsing will be set as :const:`NaT` 

1354 utc : bool 

1355 Whether to convert/localize timestamps to UTC. 

1356 

1357 Returns 

1358 ------- 

1359 Series 

1360 """ 

1361 from pandas import ( 

1362 DataFrame, 

1363 Series, 

1364 to_numeric, 

1365 to_timedelta, 

1366 ) 

1367 

1368 arg = DataFrame(arg) 

1369 if not arg.columns.is_unique: 

1370 raise ValueError("cannot assemble with duplicate keys") 

1371 

1372 # replace passed unit with _unit_map 

1373 def f(value): 

1374 if value in _unit_map: 

1375 return _unit_map[value] 

1376 

1377 # m is case significant 

1378 if value.lower() in _unit_map: 

1379 return _unit_map[value.lower()] 

1380 

1381 return value 

1382 

1383 unit = {k: f(k) for k in arg.keys()} 

1384 unit_rev = {v: k for k, v in unit.items()} 

1385 

1386 # we require at least Ymd 

1387 required = ["year", "month", "day"] 

1388 req = set(required) - set(unit_rev.keys()) 

1389 if len(req): 

1390 _required = ",".join(sorted(req)) 

1391 raise ValueError( 

1392 "to assemble mappings requires at least that " 

1393 f"[year, month, day] be specified: [{_required}] is missing" 

1394 ) 

1395 

1396 # keys we don't recognize 

1397 excess = set(unit_rev.keys()) - set(_unit_map.values()) 

1398 if len(excess): 

1399 _excess = ",".join(sorted(excess)) 

1400 raise ValueError( 

1401 f"extra keys have been passed to the datetime assemblage: [{_excess}]" 

1402 ) 

1403 

1404 def coerce(values): 

1405 # we allow coercion to if errors allows 

1406 values = to_numeric(values, errors=errors) 

1407 

1408 # prevent precision issues in case of float32 # GH#60506 

1409 if is_float_dtype(values.dtype): 

1410 values = values.astype("float64") 

1411 

1412 # prevent overflow in case of int8 or int16 

1413 if is_integer_dtype(values.dtype): 

1414 values = values.astype("int64") 

1415 return values 

1416 

1417 # Convert field values to int64 arrays, tracking rows where a 

1418 # year/month/day column has NaN (e.g. from errors="coerce" parsing) 

1419 nan_mask = np.zeros(len(arg), dtype=bool) 

1420 

1421 field_spec = [ 

1422 ("year", 2000), 

1423 ("month", 1), 

1424 ("day", 1), 

1425 ("h", 0), 

1426 ("m", 0), 

1427 ("s", 0), 

1428 ] 

1429 i32info = np.iinfo(np.int32) 

1430 field_arrs = [] 

1431 # hour/minute/second columns that cannot go through datetime_from_fields 

1432 # (fractional e.g. hour=1.5, NaN, bool, or out-of-int32 values); added 

1433 # via to_timedelta below, keeping the column-wise semantics of the 

1434 # non-vectorized implementation 

1435 td_units: list[tuple[UnitChoices, AnyArrayLike]] = [] 

1436 for field, default in field_spec: 

1437 col_name = unit_rev.get(field) 

1438 if col_name is None: 

1439 field_arrs.append(np.zeros(len(arg), dtype=np.int64)) 

1440 continue 

1441 vals = coerce(arg[col_name]) 

1442 arr = np.asarray(vals) 

1443 

1444 if field in ("h", "m", "s"): 

1445 # the npy_datetimestruct time fields are int32 

1446 if is_integer_dtype(arr.dtype): 

1447 fits_struct = len(arr) == 0 or ( 

1448 arr.min() >= i32info.min and arr.max() <= i32info.max 

1449 ) 

1450 elif is_float_dtype(arr.dtype): 

1451 fits_struct = bool( 

1452 ( 

1453 (arr == np.floor(arr)) 

1454 & (arr >= i32info.min) 

1455 & (arr <= i32info.max) 

1456 ).all() 

1457 ) 

1458 else: 

1459 fits_struct = False 

1460 if fits_struct: 

1461 field_arrs.append(arr.astype(np.int64, copy=False)) 

1462 else: 

1463 td_units.append((cast("UnitChoices", field), vals)) 

1464 field_arrs.append(np.zeros(len(arg), dtype=np.int64)) 

1465 continue 

1466 

1467 # year/month/day 

1468 if is_bool_dtype(vals.dtype): 

1469 if errors == "raise": 

1470 raise ValueError( 

1471 f"cannot assemble the datetimes: column {col_name!r} has dtype bool" 

1472 ) 

1473 nan_mask[:] = True 

1474 field_arrs.append(np.full(len(arg), default, dtype=np.int64)) 

1475 continue 

1476 if not is_float_dtype(arr.dtype): 

1477 field_arrs.append(arr.astype(np.int64, copy=False)) 

1478 continue 

1479 isnan = np.isnan(arr) 

1480 fractional = (~isnan) & (arr != np.floor(arr)) 

1481 if fractional.any() and errors == "raise": 

1482 raise ValueError( 

1483 f"cannot assemble the datetimes: column {col_name!r} " 

1484 f"contains fractional values" 

1485 ) 

1486 # +/-inf and values beyond int64 range cannot be cast meaningfully 

1487 out_of_range = (arr >= 2**63) | (arr < -(2**63)) 

1488 if out_of_range.any() and errors == "raise": 

1489 raise ValueError( 

1490 f"cannot assemble the datetimes: column {col_name!r} " 

1491 f"contains out-of-bounds values" 

1492 ) 

1493 bad = isnan | fractional | out_of_range 

1494 if bad.any(): 

1495 nan_mask[bad] = True 

1496 arr = np.where(bad, default, arr) 

1497 field_arrs.append(arr.astype(np.int64)) 

1498 

1499 # Construct datetime64[us] directly from fields, avoiding the 

1500 # object-dtype round-trip through format="%Y%m%d" string parsing. 

1501 # Rows with NaN in a year/month/day column get valid placeholders in 

1502 # every field and NaT at the end; the Cython function writes iNaT for 

1503 # invalid or out-of-bounds dates. 

1504 if nan_mask.any(): 

1505 for idx, (_, default) in enumerate(field_spec): 

1506 field_arrs[idx] = np.where(nan_mask, default, field_arrs[idx]) 

1507 

1508 year_arr, month_arr, day_arr, hour_arr, minute_arr, second_arr = field_arrs 

1509 

1510 usecs, first_invalid = datetime_from_fields( 

1511 year_arr, 

1512 month_arr, 

1513 day_arr, 

1514 hour_arr, 

1515 minute_arr, 

1516 second_arr, 

1517 ) 

1518 if first_invalid >= 0 and errors == "raise": 

1519 bad_val = ( 

1520 f"{year_arr[first_invalid]}-{month_arr[first_invalid]:02d}" 

1521 f"-{day_arr[first_invalid]:02d}" 

1522 ) 

1523 if ( 

1524 hour_arr[first_invalid] 

1525 or minute_arr[first_invalid] 

1526 or second_arr[first_invalid] 

1527 ): 

1528 bad_val += ( 

1529 f" {hour_arr[first_invalid]:02d}:{minute_arr[first_invalid]:02d}" 

1530 f":{second_arr[first_invalid]:02d}" 

1531 ) 

1532 raise ValueError( 

1533 f'cannot assemble the datetimes: invalid or out-of-bounds date "{bad_val}"' 

1534 ) 

1535 # errors="coerce": invalid entries already have iNaT from Cython 

1536 if nan_mask.any(): 

1537 usecs[nan_mask] = iNaT 

1538 

1539 dt64_values = usecs.view("M8[us]") 

1540 

1541 if utc: 

1542 dta = DatetimeArray._simple_new( 

1543 dt64_values, dtype=DatetimeTZDtype(tz="UTC", unit="us") 

1544 ) 

1545 values = Series(dta, index=arg.index, copy=False) 

1546 else: 

1547 values = Series(dt64_values, index=arg.index, copy=False) 

1548 

1549 # Add hour/minute/second columns that couldn't go through the 

1550 # vectorized path 

1551 for u, vals in td_units: 

1552 try: 

1553 values += to_timedelta(vals, unit=u, errors=errors) 

1554 except (TypeError, ValueError) as err: 

1555 raise ValueError( 

1556 f"cannot assemble the datetimes [{unit_rev[u]}]: {err}" 

1557 ) from err 

1558 

1559 # Add sub-second components as timedeltas 

1560 units: list[UnitChoices] = ["ms", "us", "ns"] 

1561 for u in units: 

1562 value = unit_rev.get(u) 

1563 if value is not None and value in arg: 

1564 try: 

1565 values += to_timedelta(coerce(arg[value]), unit=u, errors=errors) 

1566 except (TypeError, ValueError) as err: 

1567 raise ValueError( 

1568 f"cannot assemble the datetimes [{value}]: {err}" 

1569 ) from err 

1570 return values 

1571 

1572 

1573__all__ = [ 

1574 "DateParseError", 

1575 "should_cache", 

1576 "to_datetime", 

1577]