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 Timedelta,
27 Timestamp,
28 astype_overflowsafe,
29 get_supported_dtype,
30 is_supported_dtype,
31 timezones as libtimezones,
32)
33from pandas._libs.tslibs.conversion import cast_from_unit_vectorized
34from pandas._libs.tslibs.parsing import (
35 DateParseError,
36 guess_datetime_format,
37)
38from pandas._libs.tslibs.strptime import array_strptime
39from pandas._typing import (
40 AnyArrayLike,
41 ArrayLike,
42 DateTimeErrorChoices,
43)
44from pandas.util._decorators import set_module
45from pandas.util._exceptions import find_stack_level
46
47from pandas.core.dtypes.common import (
48 ensure_object,
49 is_float,
50 is_float_dtype,
51 is_integer,
52 is_integer_dtype,
53 is_list_like,
54 is_numeric_dtype,
55)
56from pandas.core.dtypes.dtypes import (
57 ArrowDtype,
58 DatetimeTZDtype,
59)
60from pandas.core.dtypes.generic import (
61 ABCDataFrame,
62 ABCSeries,
63)
64
65from pandas.arrays import (
66 DatetimeArray,
67 IntegerArray,
68 NumpyExtensionArray,
69)
70from pandas.core.algorithms import unique
71from pandas.core.arrays import ArrowExtensionArray
72from pandas.core.arrays.base import ExtensionArray
73from pandas.core.arrays.datetimes import (
74 maybe_convert_dtype,
75 objects_to_datetime64,
76 tz_to_dtype,
77)
78from pandas.core.construction import extract_array
79from pandas.core.indexes.base import Index
80from pandas.core.indexes.datetimes import DatetimeIndex
81
82if TYPE_CHECKING:
83 from collections.abc import (
84 Callable,
85 Hashable,
86 )
87
88 from pandas._libs.tslibs.nattype import NaTType
89 from pandas._libs.tslibs.timedeltas import UnitChoices
90 from pandas._typing import TimeUnit
91
92 from pandas import (
93 DataFrame,
94 Series,
95 )
96
97# ---------------------------------------------------------------------
98# types used in annotations
99
100ArrayConvertible: TypeAlias = list | tuple | AnyArrayLike
101Scalar: TypeAlias = float | str
102DatetimeScalar: TypeAlias = Scalar | date | np.datetime64
103
104DatetimeScalarOrArrayConvertible: TypeAlias = DatetimeScalar | ArrayConvertible
105DatetimeDictArg: TypeAlias = list[Scalar] | tuple[Scalar, ...] | AnyArrayLike
106
107
108class YearMonthDayDict(TypedDict, total=True):
109 year: DatetimeDictArg
110 month: DatetimeDictArg
111 day: DatetimeDictArg
112
113
114class FulldatetimeDict(YearMonthDayDict, total=False):
115 hour: DatetimeDictArg
116 hours: DatetimeDictArg
117 minute: DatetimeDictArg
118 minutes: DatetimeDictArg
119 second: DatetimeDictArg
120 seconds: DatetimeDictArg
121 ms: DatetimeDictArg
122 us: DatetimeDictArg
123 ns: DatetimeDictArg
124
125
126DictConvertible = Union[FulldatetimeDict, "DataFrame"]
127start_caching_at = 50
128
129
130# ---------------------------------------------------------------------
131
132
133def _guess_datetime_format_for_array(arr, dayfirst: bool | None = False) -> str | None:
134 # Try to guess the format based on the first non-NaN element, return None if can't
135 if (first_non_null := tslib.first_non_null(arr)) != -1:
136 if type(first_non_nan_element := arr[first_non_null]) is str:
137 # GH#32264 np.str_ object
138 guessed_format = guess_datetime_format(
139 first_non_nan_element, dayfirst=dayfirst
140 )
141 if guessed_format is not None:
142 return guessed_format
143 # If there are multiple non-null elements, warn about
144 # how parsing might not be consistent
145 if tslib.first_non_null(arr[first_non_null + 1 :]) != -1:
146 warnings.warn(
147 "Could not infer format, so each element will be parsed "
148 "individually, falling back to `dateutil`. To ensure parsing is "
149 "consistent and as-expected, please specify a format.",
150 UserWarning,
151 stacklevel=find_stack_level(),
152 )
153 return None
154
155
156def should_cache(
157 arg: ArrayConvertible, unique_share: float = 0.7, check_count: int | None = None
158) -> bool:
159 """
160 Decides whether to do caching.
161
162 If the percent of unique elements among `check_count` elements less
163 than `unique_share * 100` then we can do caching.
164
165 Parameters
166 ----------
167 arg: listlike, tuple, 1-d array, Series
168 unique_share: float, default=0.7, optional
169 0 < unique_share < 1
170 check_count: int, optional
171 0 <= check_count <= len(arg)
172
173 Returns
174 -------
175 do_caching: bool
176
177 Notes
178 -----
179 By default for a sequence of less than 50 items in size, we don't do
180 caching; for the number of elements less than 5000, we take ten percent of
181 all elements to check for a uniqueness share; if the sequence size is more
182 than 5000, then we check only the first 500 elements.
183 All constants were chosen empirically by.
184 """
185 do_caching = True
186
187 # default realization
188 if check_count is None:
189 # in this case, the gain from caching is negligible
190 if len(arg) <= start_caching_at:
191 return False
192
193 if len(arg) <= 5000:
194 check_count = len(arg) // 10
195 else:
196 check_count = 500
197 else:
198 assert 0 <= check_count <= len(arg), (
199 "check_count must be in next bounds: [0; len(arg)]"
200 )
201 if check_count == 0:
202 return False
203
204 assert 0 < unique_share < 1, "unique_share must be in next bounds: (0; 1)"
205
206 try:
207 # We can't cache if the items are not hashable.
208 unique_elements = set(islice(arg, check_count))
209 except TypeError:
210 return False
211 if len(unique_elements) > check_count * unique_share:
212 do_caching = False
213 return do_caching
214
215
216def _maybe_cache(
217 arg: ArrayConvertible,
218 format: str | None,
219 cache: bool,
220 convert_listlike: Callable,
221) -> Series:
222 """
223 Create a cache of unique dates from an array of dates
224
225 Parameters
226 ----------
227 arg : listlike, tuple, 1-d array, Series
228 format : string
229 Strftime format to parse time
230 cache : bool
231 True attempts to create a cache of converted values
232 convert_listlike : function
233 Conversion function to apply on dates
234
235 Returns
236 -------
237 cache_array : Series
238 Cache of converted, unique dates. Can be empty
239 """
240 from pandas import Series
241
242 cache_array = Series(dtype=object)
243
244 if cache:
245 # Perform a quicker unique check
246 if not should_cache(arg):
247 return cache_array
248
249 if not isinstance(arg, (np.ndarray, ExtensionArray, Index, ABCSeries)):
250 arg = np.array(arg)
251
252 unique_dates = unique(arg)
253 if len(unique_dates) < len(arg):
254 cache_dates = convert_listlike(unique_dates, format)
255 # GH#45319
256 try:
257 cache_array = Series(cache_dates, index=unique_dates, copy=False)
258 except OutOfBoundsDatetime:
259 return cache_array
260 # GH#39882 and GH#35888 in case of None and NaT we get duplicates
261 if not cache_array.index.is_unique:
262 cache_array = cache_array[~cache_array.index.duplicated()]
263 return cache_array
264
265
266def _box_as_indexlike(
267 dt_array: ArrayLike, utc: bool = False, name: Hashable | None = None
268) -> Index:
269 """
270 Properly boxes the ndarray of datetimes to DatetimeIndex
271 if it is possible or to generic Index instead
272
273 Parameters
274 ----------
275 dt_array: 1-d array
276 Array of datetimes to be wrapped in an Index.
277 utc : bool
278 Whether to convert/localize timestamps to UTC.
279 name : string, default None
280 Name for a resulting index
281
282 Returns
283 -------
284 result : datetime of converted dates
285 - DatetimeIndex if convertible to sole datetime64 type
286 - general Index otherwise
287 """
288
289 if lib.is_np_dtype(dt_array.dtype, "M"):
290 tz = "utc" if utc else None
291 return DatetimeIndex(dt_array, tz=tz, name=name)
292 return Index(dt_array, name=name, dtype=dt_array.dtype)
293
294
295def _convert_and_box_cache(
296 arg: DatetimeScalarOrArrayConvertible,
297 cache_array: Series,
298 name: Hashable | None = None,
299) -> Index:
300 """
301 Convert array of dates with a cache and wrap the result in an Index.
302
303 Parameters
304 ----------
305 arg : integer, float, string, datetime, list, tuple, 1-d array, Series
306 cache_array : Series
307 Cache of converted, unique dates
308 name : string, default None
309 Name for a DatetimeIndex
310
311 Returns
312 -------
313 result : Index-like of converted dates
314 """
315 from pandas import Series
316
317 result = Series(arg, dtype=cache_array.index.dtype).map(cache_array)
318 return _box_as_indexlike(result._values, utc=False, name=name)
319
320
321def _convert_listlike_datetimes(
322 arg,
323 format: str | None,
324 name: Hashable | None = None,
325 utc: bool = False,
326 unit: str | None = None,
327 errors: DateTimeErrorChoices = "raise",
328 dayfirst: bool | None = None,
329 yearfirst: bool | None = None,
330 exact: bool = True,
331):
332 """
333 Helper function for to_datetime. Performs the conversions of 1D listlike
334 of dates
335
336 Parameters
337 ----------
338 arg : list, tuple, ndarray, Series, Index
339 date to be parsed
340 name : object
341 None or string for the Index name
342 utc : bool
343 Whether to convert/localize timestamps to UTC.
344 unit : str
345 None or string of the frequency of the passed data
346 errors : str
347 error handing behaviors from to_datetime, 'raise', 'coerce'
348 dayfirst : bool
349 dayfirst parsing behavior from to_datetime
350 yearfirst : bool
351 yearfirst parsing behavior from to_datetime
352 exact : bool, default True
353 exact format matching behavior from to_datetime
354
355 Returns
356 -------
357 Index-like of parsed dates
358 """
359 if isinstance(arg, (list, tuple)):
360 arg = np.array(arg, dtype="O")
361 elif isinstance(arg, NumpyExtensionArray):
362 arg = np.array(arg)
363
364 arg_dtype = getattr(arg, "dtype", None)
365 # these are shortcutable
366 tz = "utc" if utc else None
367 if isinstance(arg_dtype, DatetimeTZDtype):
368 if not isinstance(arg, (DatetimeArray, DatetimeIndex)):
369 return DatetimeIndex(arg, tz=tz, name=name)
370 if utc:
371 arg = arg.tz_convert(None).tz_localize("utc")
372 return arg
373
374 elif isinstance(arg_dtype, ArrowDtype) and arg_dtype.type is Timestamp:
375 # TODO: Combine with above if DTI/DTA supports Arrow timestamps
376 if utc:
377 # pyarrow uses UTC, not lowercase utc
378 if isinstance(arg, Index):
379 arg_array = cast(ArrowExtensionArray, arg.array)
380 if arg_dtype.pyarrow_dtype.tz is not None:
381 arg_array = arg_array._dt_tz_convert("UTC")
382 else:
383 arg_array = arg_array._dt_tz_localize("UTC")
384 arg = Index(arg_array, copy=False)
385 # ArrowExtensionArray
386 elif arg_dtype.pyarrow_dtype.tz is not None:
387 arg = arg._dt_tz_convert("UTC")
388 else:
389 arg = arg._dt_tz_localize("UTC")
390 return arg
391
392 elif lib.is_np_dtype(arg_dtype, "M"):
393 if not is_supported_dtype(arg_dtype):
394 # We go to closest supported reso, i.e. "s"
395 arg = astype_overflowsafe(
396 np.asarray(arg),
397 np.dtype("M8[s]"),
398 is_coerce=errors == "coerce",
399 )
400
401 if not isinstance(arg, (DatetimeArray, DatetimeIndex)):
402 return DatetimeIndex(arg, tz=tz, name=name)
403 elif utc:
404 # DatetimeArray, DatetimeIndex
405 return arg.tz_localize("utc")
406
407 return arg
408
409 elif unit is not None:
410 if format is not None:
411 raise ValueError("cannot specify both format and unit")
412 return _to_datetime_with_unit(arg, unit, name, utc, errors)
413 elif getattr(arg, "ndim", 1) > 1:
414 raise TypeError(
415 "arg must be a string, datetime, list, tuple, 1-d array, or Series"
416 )
417
418 # warn if passing timedelta64, raise for PeriodDtype
419 # NB: this must come after unit transformation
420 try:
421 arg, _ = maybe_convert_dtype(arg, copy=False, tz=libtimezones.maybe_get_tz(tz))
422 except TypeError:
423 if errors == "coerce":
424 npvalues = np.full(len(arg), np.datetime64("NaT", "ns"))
425 return DatetimeIndex(npvalues, name=name)
426 raise
427
428 arg = ensure_object(arg)
429
430 if format is None:
431 format = _guess_datetime_format_for_array(arg, dayfirst=dayfirst)
432
433 # `format` could be inferred, or user didn't ask for mixed-format parsing.
434 if format is not None and format != "mixed":
435 return _array_strptime_with_fallback(arg, name, utc, format, exact, errors)
436
437 result, tz_parsed = objects_to_datetime64(
438 arg,
439 dayfirst=dayfirst,
440 yearfirst=yearfirst,
441 utc=utc,
442 errors=errors,
443 allow_object=True,
444 )
445
446 if tz_parsed is not None:
447 # We can take a shortcut since the datetime64 numpy array
448 # is in UTC
449 out_unit = np.datetime_data(result.dtype)[0]
450 out_unit = cast("TimeUnit", out_unit)
451 dtype = tz_to_dtype(tz_parsed, out_unit)
452 dt64_values = result.view(f"M8[{dtype.unit}]")
453 dta = DatetimeArray._simple_new(dt64_values, dtype=dtype)
454 return DatetimeIndex._simple_new(dta, name=name)
455
456 return _box_as_indexlike(result, utc=utc, name=name)
457
458
459def _array_strptime_with_fallback(
460 arg,
461 name,
462 utc: bool,
463 fmt: str,
464 exact: bool,
465 errors: str,
466) -> Index:
467 """
468 Call array_strptime, with fallback behavior depending on 'errors'.
469 """
470 result, tz_out = array_strptime(arg, fmt, exact=exact, errors=errors, utc=utc)
471 if tz_out is not None:
472 unit = np.datetime_data(result.dtype)[0]
473 unit = cast("TimeUnit", unit)
474 dtype = DatetimeTZDtype(tz=tz_out, unit=unit)
475 dta = DatetimeArray._simple_new(result, dtype=dtype)
476 if utc:
477 dta = dta.tz_convert("UTC")
478 return Index(dta, name=name, copy=False)
479 elif result.dtype != object and utc:
480 unit = np.datetime_data(result.dtype)[0]
481 unit = cast("TimeUnit", unit)
482 res = Index(result, dtype=f"M8[{unit}, UTC]", name=name, copy=False)
483 return res
484 return Index(result, dtype=result.dtype, name=name, copy=False)
485
486
487def _to_datetime_with_unit(arg, unit, name, utc: bool, errors: str) -> Index:
488 """
489 to_datetime specalized to the case where a 'unit' is passed.
490 """
491 arg = extract_array(arg, extract_numpy=True)
492
493 # GH#30050 pass an ndarray to tslib.array_to_datetime
494 # because it expects an ndarray argument
495 if isinstance(arg, IntegerArray):
496 arr = arg.astype(f"datetime64[{unit}]")
497 tz_parsed = None
498 else:
499 arg = np.asarray(arg)
500
501 if arg.dtype.kind in "iu":
502 # Note we can't do "f" here because that could induce unwanted
503 # rounding GH#14156, GH#20445
504 arr = arg.astype(f"datetime64[{unit}]", copy=False)
505 dtype = get_supported_dtype(arr.dtype)
506 try:
507 arr = astype_overflowsafe(arr, dtype, copy=False)
508 except OutOfBoundsDatetime:
509 if errors == "raise":
510 raise
511 arg = arg.astype(object)
512 return _to_datetime_with_unit(arg, unit, name, utc, errors)
513 tz_parsed = None
514
515 elif arg.dtype.kind == "f":
516 with np.errstate(invalid="ignore"):
517 int_values = arg.astype(np.int64)
518 mask = np.isnan(arg)
519 if (mask | (arg == int_values)).all():
520 # With all-round-or-NaN entries, we give the requested unit
521 # back like with integers
522 result = _to_datetime_with_unit(
523 int_values, unit=unit, name=name, utc=utc, errors=errors
524 )
525 result._data[mask] = NaT
526 return result
527 with np.errstate(over="raise"):
528 try:
529 arr = cast_from_unit_vectorized(arg, unit=unit)
530 except OutOfBoundsDatetime as err:
531 if errors != "raise":
532 return _to_datetime_with_unit(
533 arg.astype(object), unit, name, utc, errors
534 )
535 raise OutOfBoundsDatetime(
536 f"cannot convert input with unit '{unit}'"
537 ) from err
538
539 arr = arr.view("M8[ns]")
540 tz_parsed = None
541 else:
542 arg = arg.astype(object, copy=False)
543 arr, tz_parsed = tslib.array_to_datetime(
544 arg,
545 utc=utc,
546 errors=errors,
547 unit_for_numerics=unit,
548 )
549
550 result = DatetimeIndex(arr, name=name)
551 if not isinstance(result, DatetimeIndex):
552 return result
553
554 # GH#23758: We may still need to localize the result with tz
555 # GH#25546: Apply tz_parsed first (from arg), then tz (from caller)
556 # result will be naive but in UTC
557 result = result.tz_localize("UTC").tz_convert(tz_parsed)
558
559 if utc:
560 if result.tz is None:
561 result = result.tz_localize("utc")
562 else:
563 result = result.tz_convert("utc")
564 return result
565
566
567def _adjust_to_origin(arg, origin, unit):
568 """
569 Helper function for to_datetime.
570 Adjust input argument to the specified origin
571
572 Parameters
573 ----------
574 arg : list, tuple, ndarray, Series, Index
575 date to be adjusted
576 origin : 'julian' or Timestamp
577 origin offset for the arg
578 unit : str
579 passed unit from to_datetime, must be 'D'
580
581 Returns
582 -------
583 ndarray or scalar of adjusted date(s)
584 """
585 if origin == "julian":
586 original = arg
587 j0 = Timestamp(0).to_julian_date()
588 if unit != "D":
589 raise ValueError("unit must be 'D' for origin='julian'")
590 try:
591 arg = arg - j0
592 except TypeError as err:
593 raise ValueError(
594 "incompatible 'arg' type for given 'origin'='julian'"
595 ) from err
596
597 # preemptively check this for a nice range
598 j_max = Timestamp.max.to_julian_date() - j0
599 j_min = Timestamp.min.to_julian_date() - j0
600 if np.any(arg > j_max) or np.any(arg < j_min):
601 raise OutOfBoundsDatetime(
602 f"{original} is Out of Bounds for origin='julian'"
603 )
604 else:
605 # arg must be numeric
606 if not (
607 (is_integer(arg) or is_float(arg)) or is_numeric_dtype(np.asarray(arg))
608 ):
609 raise ValueError(
610 f"'{arg}' is not compatible with origin='{origin}'; "
611 "it must be numeric with a unit specified"
612 )
613
614 # we are going to offset back to unix / epoch time
615 try:
616 if lib.is_integer(origin) or lib.is_float(origin):
617 offset = Timestamp(origin, unit=unit)
618 else:
619 offset = Timestamp(origin)
620 except OutOfBoundsDatetime as err:
621 raise OutOfBoundsDatetime(f"origin {origin} is Out of Bounds") from err
622 except ValueError as err:
623 raise ValueError(
624 f"origin {origin} cannot be converted to a Timestamp"
625 ) from err
626
627 if offset.tz is not None:
628 raise ValueError(f"origin offset {offset} must be tz-naive")
629 td_offset = offset - Timestamp(0)
630
631 # convert the offset to the unit of the arg
632 # this should be lossless in terms of precision
633 ioffset = td_offset // Timedelta(1, unit=unit)
634
635 # scalars & ndarray-like can handle the addition
636 if is_list_like(arg) and not isinstance(arg, (ABCSeries, Index, np.ndarray)):
637 arg = np.asarray(arg)
638 arg = arg + ioffset
639 return arg
640
641
642@overload
643def to_datetime(
644 arg: DatetimeScalar,
645 errors: DateTimeErrorChoices = ...,
646 dayfirst: bool = ...,
647 yearfirst: bool = ...,
648 utc: bool = ...,
649 format: str | None = ...,
650 exact: bool = ...,
651 unit: str | None = ...,
652 origin=...,
653 cache: bool = ...,
654) -> Timestamp: ...
655
656
657@overload
658def to_datetime(
659 arg: Series | DictConvertible,
660 errors: DateTimeErrorChoices = ...,
661 dayfirst: bool = ...,
662 yearfirst: bool = ...,
663 utc: bool = ...,
664 format: str | None = ...,
665 exact: bool = ...,
666 unit: str | None = ...,
667 origin=...,
668 cache: bool = ...,
669) -> Series: ...
670
671
672@overload
673def to_datetime(
674 arg: list | tuple | Index | ArrayLike,
675 errors: DateTimeErrorChoices = ...,
676 dayfirst: bool = ...,
677 yearfirst: bool = ...,
678 utc: bool = ...,
679 format: str | None = ...,
680 exact: bool = ...,
681 unit: str | None = ...,
682 origin=...,
683 cache: bool = ...,
684) -> DatetimeIndex: ...
685
686
687@set_module("pandas")
688def to_datetime(
689 arg: DatetimeScalarOrArrayConvertible | DictConvertible,
690 errors: DateTimeErrorChoices = "raise",
691 dayfirst: bool = False,
692 yearfirst: bool = False,
693 utc: bool = False,
694 format: str | None = None,
695 exact: bool | lib.NoDefault = lib.no_default,
696 unit: str | None = None,
697 origin: str = "unix",
698 cache: bool = True,
699) -> DatetimeIndex | Series | DatetimeScalar | NaTType:
700 """
701 Convert argument to datetime.
702
703 This function converts a scalar, array-like, :class:`Series` or
704 :class:`DataFrame`/dict-like to a pandas datetime object.
705
706 Parameters
707 ----------
708 arg : int, float, str, datetime, list, tuple, 1-d array, Series, DataFrame/dict-like
709 The object to convert to a datetime. If a :class:`DataFrame` is provided, the
710 method expects minimally the following columns: :const:`"year"`,
711 :const:`"month"`, :const:`"day"`. The column "year"
712 must be specified in 4-digit format.
713 errors : {'raise', 'coerce'}, default 'raise'
714 - If :const:`'raise'`, then invalid parsing will raise an exception.
715 - If :const:`'coerce'`, then invalid parsing will be set as :const:`NaT`.
716 dayfirst : bool, default False
717 Specify a date parse order if `arg` is str or is list-like.
718 If :const:`True`, parses dates with the day first, e.g. :const:`"10/11/12"`
719 is parsed as :const:`2012-11-10`.
720
721 .. warning::
722
723 ``dayfirst=True`` is not strict, but will prefer to parse
724 with day first.
725
726 yearfirst : bool, default False
727 Specify a date parse order if `arg` is str or is list-like.
728
729 - If :const:`True` parses dates with the year first, e.g.
730 :const:`"10/11/12"` is parsed as :const:`2010-11-12`.
731 - If both `dayfirst` and `yearfirst` are :const:`True`, `yearfirst` is
732 preceded (same as :mod:`dateutil`).
733
734 .. warning::
735
736 ``yearfirst=True`` is not strict, but will prefer to parse
737 with year first.
738
739 utc : bool, default False
740 Control timezone-related parsing, localization and conversion.
741
742 - If :const:`True`, the function *always* returns a timezone-aware
743 UTC-localized :class:`Timestamp`, :class:`Series` or
744 :class:`DatetimeIndex`. To do this, timezone-naive inputs are
745 *localized* as UTC, while timezone-aware inputs are *converted* to UTC.
746
747 - If :const:`False` (default), inputs will not be coerced to UTC.
748 Timezone-naive inputs will remain naive, while timezone-aware ones
749 will keep their time offsets. Limitations exist for mixed
750 offsets (typically, daylight savings), see :ref:`Examples
751 <to_datetime_tz_examples>` section for details.
752
753 See also: pandas general documentation about `timezone conversion and
754 localization
755 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
756 #time-zone-handling>`_.
757
758 format : str, default None
759 The strftime to parse time, e.g. :const:`"%d/%m/%Y"`. See
760 `strftime documentation
761 <https://docs.python.org/3/library/datetime.html
762 #strftime-and-strptime-behavior>`_ for more information on choices, though
763 note that :const:`"%f"` will parse all the way up to nanoseconds.
764 You can also pass:
765
766 - "ISO8601", to parse any `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_
767 time string (not necessarily in exactly the same format);
768 - "mixed", to infer the format for each element individually. This is risky,
769 and you should probably use it along with `dayfirst`.
770
771 .. note::
772
773 If a :class:`DataFrame` is passed, then `format` has no effect.
774
775 exact : bool, default True
776 Control how `format` is used:
777
778 - If :const:`True`, require an exact `format` match.
779 - If :const:`False`, allow the `format` to match anywhere in the target
780 string.
781
782 Cannot be used alongside ``format='ISO8601'`` or ``format='mixed'``.
783 unit : str, default 'ns'
784 The unit of the arg (D,s,ms,us,ns) denote the unit, which is an
785 integer or float number. This will be based off the origin.
786 Example, with ``unit='ms'`` and ``origin='unix'``, this would calculate
787 the number of milliseconds to the unix epoch start.
788 origin : scalar, default 'unix'
789 Define the reference date. The numeric values would be parsed as number
790 of units (defined by `unit`) since this reference date.
791
792 - If :const:`'unix'` (or POSIX) time; origin is set to 1970-01-01.
793 - If :const:`'julian'`, unit must be :const:`'D'`, and origin is set to
794 beginning of Julian Calendar. Julian day number :const:`0` is assigned
795 to the day starting at noon on January 1, 4713 BC.
796 - If Timestamp convertible (Timestamp, dt.datetime, np.datetimt64 or date
797 string), origin is set to Timestamp identified by origin.
798 - If a float or integer, origin is the difference
799 (in units determined by the ``unit`` argument) relative to 1970-01-01.
800 cache : bool, default True
801 If :const:`True`, use a cache of unique, converted dates to apply the
802 datetime conversion. May produce significant speed-up when parsing
803 duplicate date strings, especially ones with timezone offsets. The cache
804 is only used when there are at least 50 values. The presence of
805 out-of-bounds values will render the cache unusable and may slow down
806 parsing.
807
808 Returns
809 -------
810 datetime
811 If parsing succeeded.
812 Return type depends on input (types in parenthesis correspond to
813 fallback in case of unsuccessful timezone or out-of-range timestamp
814 parsing):
815
816 - scalar: :class:`Timestamp` (or :class:`datetime.datetime`)
817 - array-like: :class:`DatetimeIndex` (or :class:`Series` with
818 :class:`object` dtype containing :class:`datetime.datetime`)
819 - Series: :class:`Series` of :class:`datetime64` dtype (or
820 :class:`Series` of :class:`object` dtype containing
821 :class:`datetime.datetime`)
822 - DataFrame: :class:`Series` of :class:`datetime64` dtype (or
823 :class:`Series` of :class:`object` dtype containing
824 :class:`datetime.datetime`)
825
826 Raises
827 ------
828 ParserError
829 When parsing a date from string fails.
830 ValueError
831 When another datetime conversion error happens. For example when one
832 of 'year', 'month', day' columns is missing in a :class:`DataFrame`, or
833 when a Timezone-aware :class:`datetime.datetime` is found in an array-like
834 of mixed time offsets, and ``utc=False``, or when parsing datetimes
835 with mixed time zones unless ``utc=True``. If parsing datetimes with mixed
836 time zones, please specify ``utc=True``.
837
838 See Also
839 --------
840 DataFrame.astype : Cast argument to a specified dtype.
841 to_timedelta : Convert argument to timedelta.
842 convert_dtypes : Convert dtypes.
843
844 Notes
845 -----
846
847 Many input types are supported, and lead to different output types:
848
849 - **scalars** can be int, float, str, datetime object (from stdlib :mod:`datetime`
850 module or :mod:`numpy`). They are converted to :class:`Timestamp` when
851 possible, otherwise they are converted to :class:`datetime.datetime`.
852 None/NaN/null scalars are converted to :const:`NaT`.
853
854 - **array-like** can contain int, float, str, datetime objects. They are
855 converted to :class:`DatetimeIndex` when possible, otherwise they are
856 converted to :class:`Index` with :class:`object` dtype, containing
857 :class:`datetime.datetime`. None/NaN/null entries are converted to
858 :const:`NaT` in both cases.
859
860 - **Series** are converted to :class:`Series` with :class:`datetime64`
861 dtype when possible, otherwise they are converted to :class:`Series` with
862 :class:`object` dtype, containing :class:`datetime.datetime`. None/NaN/null
863 entries are converted to :const:`NaT` in both cases.
864
865 - **DataFrame/dict-like** are converted to :class:`Series` with
866 :class:`datetime64` dtype. For each row a datetime is created from assembling
867 the various dataframe columns. Column keys can be common abbreviations
868 like ['year', 'month', 'day', 'minute', 'second', 'ms', 'us', 'ns']) or
869 plurals of the same.
870
871 The following causes are responsible for :class:`datetime.datetime` objects
872 being returned (possibly inside an :class:`Index` or a :class:`Series` with
873 :class:`object` dtype) instead of a proper pandas designated type
874 (:class:`Timestamp`, :class:`DatetimeIndex` or :class:`Series`
875 with :class:`datetime64` dtype):
876
877 - when any input element is before :const:`Timestamp.min` or after
878 :const:`Timestamp.max`, see `timestamp limitations
879 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
880 #timeseries-timestamp-limits>`_.
881
882 - when ``utc=False`` (default) and the input is an array-like or
883 :class:`Series` containing mixed naive/aware datetime, or aware with mixed
884 time offsets. Note that this happens in the (quite frequent) situation when
885 the timezone has a daylight savings policy. In that case you may wish to
886 use ``utc=True``.
887
888 Examples
889 --------
890
891 **Handling various input formats**
892
893 Assembling a datetime from multiple columns of a :class:`DataFrame`. The keys
894 can be common abbreviations like ['year', 'month', 'day', 'minute', 'second',
895 'ms', 'us', 'ns']) or plurals of the same
896
897 >>> df = pd.DataFrame({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]})
898 >>> pd.to_datetime(df)
899 0 2015-02-04
900 1 2016-03-05
901 dtype: datetime64[us]
902
903 Using a unix epoch time
904
905 >>> pd.to_datetime(1490195805, unit="s")
906 Timestamp('2017-03-22 15:16:45')
907 >>> pd.to_datetime(1490195805433502912, unit="ns")
908 Timestamp('2017-03-22 15:16:45.433502912')
909
910 .. warning:: For float arg, precision rounding might happen. To prevent
911 unexpected behavior use a fixed-width exact type.
912
913 Using a non-unix epoch origin
914
915 >>> pd.to_datetime([1, 2, 3], unit="D", origin=pd.Timestamp("1960-01-01"))
916 DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'],
917 dtype='datetime64[s]', freq=None)
918
919 **Differences with strptime behavior**
920
921 :const:`"%f"` will parse all the way up to nanoseconds.
922
923 >>> pd.to_datetime("2018-10-26 12:00:00.0000000011", format="%Y-%m-%d %H:%M:%S.%f")
924 Timestamp('2018-10-26 12:00:00.000000001')
925
926 **Non-convertible date/times**
927
928 Passing ``errors='coerce'`` will force an out-of-bounds date to :const:`NaT`,
929 in addition to forcing non-dates (or non-parseable dates) to :const:`NaT`.
930
931 >>> pd.to_datetime("invalid for Ymd", format="%Y%m%d", errors="coerce")
932 NaT
933
934 .. _to_datetime_tz_examples:
935
936 **Timezones and time offsets**
937
938 The default behaviour (``utc=False``) is as follows:
939
940 - Timezone-naive inputs are converted to timezone-naive :class:`DatetimeIndex`:
941
942 >>> pd.to_datetime(["2018-10-26 12:00:00", "2018-10-26 13:00:15"])
943 DatetimeIndex(['2018-10-26 12:00:00', '2018-10-26 13:00:15'],
944 dtype='datetime64[us]', freq=None)
945
946 - Timezone-aware inputs *with constant time offset* are converted to
947 timezone-aware :class:`DatetimeIndex`:
948
949 >>> pd.to_datetime(["2018-10-26 12:00 -0500", "2018-10-26 13:00 -0500"])
950 DatetimeIndex(['2018-10-26 12:00:00-05:00', '2018-10-26 13:00:00-05:00'],
951 dtype='datetime64[us, UTC-05:00]', freq=None)
952
953 - However, timezone-aware inputs *with mixed time offsets* (for example
954 issued from a timezone with daylight savings, such as Europe/Paris)
955 are **not successfully converted** to a :class:`DatetimeIndex`.
956 Parsing datetimes with mixed time zones will raise a ValueError unless
957 ``utc=True``:
958
959 >>> pd.to_datetime(
960 ... ["2020-10-25 02:00 +0200", "2020-10-25 04:00 +0100"]
961 ... ) # doctest: +SKIP
962 ValueError: Mixed timezones detected. Pass utc=True in to_datetime
963 or tz='UTC' in DatetimeIndex to convert to a common timezone.
964
965 - To create a :class:`Series` with mixed offsets and ``object`` dtype, please use
966 :meth:`Series.apply` and :func:`datetime.datetime.strptime`:
967
968 >>> import datetime as dt
969 >>> ser = pd.Series(["2020-10-25 02:00 +0200", "2020-10-25 04:00 +0100"])
970 >>> ser.apply(lambda x: dt.datetime.strptime(x, "%Y-%m-%d %H:%M %z"))
971 0 2020-10-25 02:00:00+02:00
972 1 2020-10-25 04:00:00+01:00
973 dtype: object
974
975 - A mix of timezone-aware and timezone-naive inputs will also raise a ValueError
976 unless ``utc=True``:
977
978 >>> from datetime import datetime
979 >>> pd.to_datetime(
980 ... ["2020-01-01 01:00:00-01:00", datetime(2020, 1, 1, 3, 0)]
981 ... ) # doctest: +SKIP
982 ValueError: Mixed timezones detected. Pass utc=True in to_datetime
983 or tz='UTC' in DatetimeIndex to convert to a common timezone.
984
985 |
986
987 Setting ``utc=True`` solves most of the above issues:
988
989 - Timezone-naive inputs are *localized* as UTC
990
991 >>> pd.to_datetime(["2018-10-26 12:00", "2018-10-26 13:00"], utc=True)
992 DatetimeIndex(['2018-10-26 12:00:00+00:00', '2018-10-26 13:00:00+00:00'],
993 dtype='datetime64[us, UTC]', freq=None)
994
995 - Timezone-aware inputs are *converted* to UTC (the output represents the
996 exact same datetime, but viewed from the UTC time offset `+00:00`).
997
998 >>> pd.to_datetime(["2018-10-26 12:00 -0530", "2018-10-26 12:00 -0500"], utc=True)
999 DatetimeIndex(['2018-10-26 17:30:00+00:00', '2018-10-26 17:00:00+00:00'],
1000 dtype='datetime64[us, UTC]', freq=None)
1001
1002 - Inputs can contain both string or datetime, the above
1003 rules still apply
1004
1005 >>> pd.to_datetime(["2018-10-26 12:00", datetime(2020, 1, 1, 18)], utc=True)
1006 DatetimeIndex(['2018-10-26 12:00:00+00:00', '2020-01-01 18:00:00+00:00'],
1007 dtype='datetime64[us, UTC]', freq=None)
1008 """
1009 if exact is not lib.no_default and format in {"mixed", "ISO8601"}:
1010 raise ValueError("Cannot use 'exact' when 'format' is 'mixed' or 'ISO8601'")
1011 if arg is None:
1012 return NaT
1013
1014 if origin != "unix":
1015 arg = _adjust_to_origin(arg, origin, unit)
1016
1017 convert_listlike = partial(
1018 _convert_listlike_datetimes,
1019 utc=utc,
1020 unit=unit,
1021 dayfirst=dayfirst,
1022 yearfirst=yearfirst,
1023 errors=errors,
1024 exact=exact, # type: ignore[arg-type]
1025 )
1026 result: Timestamp | NaTType | Series | Index
1027
1028 if isinstance(arg, Timestamp):
1029 result = arg
1030 if utc:
1031 if arg.tz is not None:
1032 result = arg.tz_convert("utc")
1033 else:
1034 result = arg.tz_localize("utc")
1035 elif isinstance(arg, ABCSeries):
1036 cache_array = _maybe_cache(arg, format, cache, convert_listlike)
1037 if not cache_array.empty:
1038 result = arg.map(cache_array)
1039 else:
1040 values = convert_listlike(arg._values, format)
1041 result = arg._constructor(values, index=arg.index, name=arg.name)
1042 elif isinstance(arg, (ABCDataFrame, abc.MutableMapping)):
1043 result = _assemble_from_unit_mappings(arg, errors, utc)
1044 elif isinstance(arg, Index):
1045 cache_array = _maybe_cache(arg, format, cache, convert_listlike)
1046 if not cache_array.empty:
1047 result = _convert_and_box_cache(arg, cache_array, name=arg.name)
1048 else:
1049 result = convert_listlike(arg, format, name=arg.name)
1050 elif is_list_like(arg):
1051 try:
1052 # error: Argument 1 to "_maybe_cache" has incompatible type
1053 # "Union[float, str, datetime, List[Any], Tuple[Any, ...], ExtensionArray,
1054 # ndarray[Any, Any], Series]"; expected "Union[List[Any], Tuple[Any, ...],
1055 # Union[Union[ExtensionArray, ndarray[Any, Any]], Index, Series], Series]"
1056 argc = cast(
1057 Union[list, tuple, ExtensionArray, np.ndarray, "Series", Index], arg
1058 )
1059 cache_array = _maybe_cache(argc, format, cache, convert_listlike)
1060 except OutOfBoundsDatetime:
1061 # caching attempts to create a DatetimeIndex, which may raise
1062 # an OOB. If that's the desired behavior, then just reraise...
1063 if errors == "raise":
1064 raise
1065 # ... otherwise, continue without the cache.
1066 from pandas import Series
1067
1068 cache_array = Series([], dtype=object) # just an empty array
1069 if not cache_array.empty:
1070 result = _convert_and_box_cache(argc, cache_array)
1071 else:
1072 result = convert_listlike(argc, format)
1073 else:
1074 result = convert_listlike(np.array([arg]), format)[0]
1075 if isinstance(arg, bool) and isinstance(result, np.bool_):
1076 result = bool(result) # TODO: avoid this kludge.
1077
1078 # error: Incompatible return value type (got "Union[Timestamp, NaTType,
1079 # Series, Index]", expected "Union[DatetimeIndex, Series, float, str,
1080 # NaTType, None]")
1081 return result # type: ignore[return-value]
1082
1083
1084# mappings for assembling units
1085_unit_map = {
1086 "year": "year",
1087 "years": "year",
1088 "month": "month",
1089 "months": "month",
1090 "day": "day",
1091 "days": "day",
1092 "hour": "h",
1093 "hours": "h",
1094 "minute": "m",
1095 "minutes": "m",
1096 "second": "s",
1097 "seconds": "s",
1098 "ms": "ms",
1099 "millisecond": "ms",
1100 "milliseconds": "ms",
1101 "us": "us",
1102 "microsecond": "us",
1103 "microseconds": "us",
1104 "ns": "ns",
1105 "nanosecond": "ns",
1106 "nanoseconds": "ns",
1107}
1108
1109
1110def _assemble_from_unit_mappings(
1111 arg, errors: DateTimeErrorChoices, utc: bool
1112) -> Series:
1113 """
1114 assemble the unit specified fields from the arg (DataFrame)
1115 Return a Series for actual parsing
1116
1117 Parameters
1118 ----------
1119 arg : DataFrame
1120 errors : {'raise', 'coerce'}, default 'raise'
1121
1122 - If :const:`'raise'`, then invalid parsing will raise an exception
1123 - If :const:`'coerce'`, then invalid parsing will be set as :const:`NaT`
1124 utc : bool
1125 Whether to convert/localize timestamps to UTC.
1126
1127 Returns
1128 -------
1129 Series
1130 """
1131 from pandas import (
1132 DataFrame,
1133 to_numeric,
1134 to_timedelta,
1135 )
1136
1137 arg = DataFrame(arg)
1138 if not arg.columns.is_unique:
1139 raise ValueError("cannot assemble with duplicate keys")
1140
1141 # replace passed unit with _unit_map
1142 def f(value):
1143 if value in _unit_map:
1144 return _unit_map[value]
1145
1146 # m is case significant
1147 if value.lower() in _unit_map:
1148 return _unit_map[value.lower()]
1149
1150 return value
1151
1152 unit = {k: f(k) for k in arg.keys()}
1153 unit_rev = {v: k for k, v in unit.items()}
1154
1155 # we require at least Ymd
1156 required = ["year", "month", "day"]
1157 req = set(required) - set(unit_rev.keys())
1158 if len(req):
1159 _required = ",".join(sorted(req))
1160 raise ValueError(
1161 "to assemble mappings requires at least that "
1162 f"[year, month, day] be specified: [{_required}] is missing"
1163 )
1164
1165 # keys we don't recognize
1166 excess = set(unit_rev.keys()) - set(_unit_map.values())
1167 if len(excess):
1168 _excess = ",".join(sorted(excess))
1169 raise ValueError(
1170 f"extra keys have been passed to the datetime assemblage: [{_excess}]"
1171 )
1172
1173 def coerce(values):
1174 # we allow coercion to if errors allows
1175 values = to_numeric(values, errors=errors)
1176
1177 # prevent prevision issues in case of float32 # GH#60506
1178 if is_float_dtype(values.dtype):
1179 values = values.astype("float64")
1180
1181 # prevent overflow in case of int8 or int16
1182 if is_integer_dtype(values.dtype):
1183 values = values.astype("int64")
1184 return values
1185
1186 values = (
1187 coerce(arg[unit_rev["year"]]) * 10000
1188 + coerce(arg[unit_rev["month"]]) * 100
1189 + coerce(arg[unit_rev["day"]])
1190 )
1191 try:
1192 values = to_datetime(values, format="%Y%m%d", errors=errors, utc=utc)
1193 except (TypeError, ValueError) as err:
1194 raise ValueError(f"cannot assemble the datetimes: {err}") from err
1195
1196 units: list[UnitChoices] = ["h", "m", "s", "ms", "us", "ns"]
1197 for u in units:
1198 value = unit_rev.get(u)
1199 if value is not None and value in arg:
1200 try:
1201 values += to_timedelta(coerce(arg[value]), unit=u, errors=errors)
1202 except (TypeError, ValueError) as err:
1203 raise ValueError(
1204 f"cannot assemble the datetimes [{value}]: {err}"
1205 ) from err
1206 return values
1207
1208
1209__all__ = [
1210 "DateParseError",
1211 "should_cache",
1212 "to_datetime",
1213]