1"""
2datetimelike delegation
3"""
4
5from __future__ import annotations
6
7from typing import (
8 TYPE_CHECKING,
9 NoReturn,
10 cast,
11)
12import warnings
13
14import numpy as np
15
16from pandas._libs import lib
17from pandas.errors import Pandas4Warning
18from pandas.util._exceptions import find_stack_level
19
20from pandas.core.dtypes.common import (
21 is_integer_dtype,
22 is_list_like,
23)
24from pandas.core.dtypes.dtypes import (
25 ArrowDtype,
26 CategoricalDtype,
27 DatetimeTZDtype,
28 PeriodDtype,
29)
30from pandas.core.dtypes.generic import ABCSeries
31
32from pandas.core.accessor import (
33 PandasDelegate,
34 delegate_names,
35)
36from pandas.core.arrays import (
37 DatetimeArray,
38 PeriodArray,
39 TimedeltaArray,
40)
41from pandas.core.arrays.arrow.array import ArrowExtensionArray
42from pandas.core.base import (
43 NoNewAttributesMixin,
44 PandasObject,
45)
46from pandas.core.indexes.datetimes import DatetimeIndex
47from pandas.core.indexes.timedeltas import TimedeltaIndex
48
49from pandas.tseries import frequencies
50
51if TYPE_CHECKING:
52 from pandas import (
53 DataFrame,
54 Series,
55 )
56
57
58class Properties(PandasDelegate, PandasObject, NoNewAttributesMixin):
59 _hidden_attrs = PandasObject._hidden_attrs | {
60 "orig",
61 "name",
62 }
63
64 def __init__(self, data: Series, orig) -> None:
65 if not isinstance(data, ABCSeries):
66 raise TypeError(
67 f"cannot convert an object of type {type(data)} to a datetimelike index"
68 )
69
70 self._parent = data
71 self.orig = orig
72 self.name = getattr(data, "name", None)
73 self._freeze()
74
75 def _get_values(self):
76 data = self._parent
77 if lib.is_np_dtype(data.dtype, "M"):
78 return DatetimeIndex(data, copy=False, name=self.name)
79
80 elif isinstance(data.dtype, DatetimeTZDtype):
81 return DatetimeIndex(data, copy=False, name=self.name)
82
83 elif lib.is_np_dtype(data.dtype, "m"):
84 return TimedeltaIndex(data, copy=False, name=self.name)
85
86 elif isinstance(data.dtype, PeriodDtype):
87 return PeriodArray(data, copy=False)
88
89 raise TypeError(
90 f"cannot convert an object of type {type(data)} to a datetimelike index"
91 )
92
93 def _delegate_property_get(self, name: str):
94 from pandas import Series
95
96 values = self._get_values()
97
98 result = getattr(values, name)
99
100 # maybe need to upcast (ints)
101 if isinstance(result, np.ndarray):
102 if is_integer_dtype(result):
103 result = result.astype("int64")
104 elif not is_list_like(result):
105 return result
106
107 result = np.asarray(result)
108
109 if self.orig is not None:
110 index = self.orig.index
111 else:
112 index = self._parent.index
113 # return the result as a Series
114 return Series(result, index=index, name=self.name).__finalize__(self._parent)
115
116 def _delegate_property_set(self, name: str, value, *args, **kwargs) -> NoReturn:
117 raise ValueError(
118 "modifications to a property of a datetimelike object are not supported. "
119 "Change values on the original."
120 )
121
122 def _delegate_method(self, name: str, *args, **kwargs):
123 from pandas import Series
124
125 values = self._get_values()
126
127 method = getattr(values, name)
128 result = method(*args, **kwargs)
129
130 if not is_list_like(result):
131 return result
132
133 return Series(result, index=self._parent.index, name=self.name).__finalize__(
134 self._parent
135 )
136
137
138@delegate_names(
139 delegate=ArrowExtensionArray,
140 accessors=TimedeltaArray._datetimelike_ops,
141 typ="property",
142 accessor_mapping=lambda x: f"_dt_{x}",
143 raise_on_missing=False,
144)
145@delegate_names(
146 delegate=ArrowExtensionArray,
147 accessors=TimedeltaArray._datetimelike_methods,
148 typ="method",
149 accessor_mapping=lambda x: f"_dt_{x}",
150 raise_on_missing=False,
151)
152@delegate_names(
153 delegate=ArrowExtensionArray,
154 accessors=[
155 *DatetimeArray._datetimelike_ops,
156 # deprecated, but should still work
157 "dayofweek",
158 "dayofyear",
159 "daysinmonth",
160 "weekday",
161 ],
162 typ="property",
163 accessor_mapping=lambda x: f"_dt_{x}",
164 raise_on_missing=False,
165)
166@delegate_names(
167 delegate=ArrowExtensionArray,
168 accessors=DatetimeArray._datetimelike_methods,
169 typ="method",
170 accessor_mapping=lambda x: f"_dt_{x}",
171 raise_on_missing=False,
172)
173class ArrowTemporalProperties(PandasDelegate, PandasObject, NoNewAttributesMixin):
174 def __init__(self, data: Series, orig) -> None:
175 if not isinstance(data, ABCSeries):
176 raise TypeError(
177 f"cannot convert an object of type {type(data)} to a datetimelike index"
178 )
179
180 self._parent = data
181 self._orig = orig
182 self._freeze()
183
184 def _delegate_property_get(self, name: str):
185 if not hasattr(self._parent.array, f"_dt_{name}"):
186 raise NotImplementedError(
187 f"dt.{name} is not supported for {self._parent.dtype}"
188 )
189 result = getattr(self._parent.array, f"_dt_{name}")
190
191 if not is_list_like(result):
192 return result
193
194 if self._orig is not None:
195 index = self._orig.index
196 else:
197 index = self._parent.index
198 # return the result as a Series, which is by definition a copy
199 result = type(self._parent)(
200 result, index=index, name=self._parent.name
201 ).__finalize__(self._parent)
202
203 return result
204
205 def _delegate_method(self, name: str, *args, **kwargs):
206 if not hasattr(self._parent.array, f"_dt_{name}"):
207 raise NotImplementedError(
208 f"dt.{name} is not supported for {self._parent.dtype}"
209 )
210
211 result = getattr(self._parent.array, f"_dt_{name}")(*args, **kwargs)
212
213 if self._orig is not None:
214 index = self._orig.index
215 else:
216 index = self._parent.index
217 # return the result as a Series, which is by definition a copy
218 result = type(self._parent)(
219 result, index=index, name=self._parent.name
220 ).__finalize__(self._parent)
221
222 return result
223
224 def to_pytimedelta(self):
225 # GH 57463
226 warnings.warn(
227 f"The behavior of {type(self).__name__}.to_pytimedelta is deprecated, "
228 "in a future version this will return a Series containing python "
229 "datetime.timedelta objects instead of an ndarray. To retain the "
230 "old behavior, call `np.array` on the result",
231 Pandas4Warning,
232 stacklevel=find_stack_level(),
233 )
234 return cast("ArrowExtensionArray", self._parent.array)._dt_to_pytimedelta()
235
236 def to_pydatetime(self) -> Series:
237 # GH#20306
238 result = cast("ArrowExtensionArray", self._parent.array)._dt_to_pydatetime()
239 result.index = self._parent.index
240 result.name = self._parent.name
241 return result
242
243 def isocalendar(self) -> DataFrame:
244 from pandas import DataFrame
245
246 result = (
247 cast("ArrowExtensionArray", self._parent.array)
248 ._dt_isocalendar()
249 ._pa_array.combine_chunks()
250 )
251 iso_calendar_df = DataFrame(
252 {
253 col: type(self._parent.array)(result.field(i)) # type: ignore[call-arg]
254 for i, col in enumerate(["year", "week", "day"])
255 },
256 index=self._parent.index,
257 )
258 return iso_calendar_df
259
260 @property
261 def components(self) -> DataFrame:
262 from pandas import DataFrame
263
264 return (
265 DataFrame(cast("ArrowExtensionArray", self._parent.array)._dt_components)
266 .set_index(self._parent.index)
267 .__finalize__(self._parent)
268 )
269
270
271@delegate_names(
272 delegate=DatetimeArray,
273 accessors=[*DatetimeArray._datetimelike_ops, "unit"],
274 typ="property",
275)
276@delegate_names(
277 delegate=DatetimeArray,
278 accessors=[*DatetimeArray._datetimelike_methods, "as_unit"],
279 typ="method",
280)
281class DatetimeProperties(Properties):
282 """
283 Accessor object for datetimelike properties of the Series values.
284
285 Examples
286 --------
287 >>> seconds_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="s"))
288 >>> seconds_series
289 0 2000-01-01 00:00:00
290 1 2000-01-01 00:00:01
291 2 2000-01-01 00:00:02
292 dtype: datetime64[us]
293 >>> seconds_series.dt.second
294 0 0
295 1 1
296 2 2
297 dtype: int32
298
299 >>> hours_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="h"))
300 >>> hours_series
301 0 2000-01-01 00:00:00
302 1 2000-01-01 01:00:00
303 2 2000-01-01 02:00:00
304 dtype: datetime64[us]
305 >>> hours_series.dt.hour
306 0 0
307 1 1
308 2 2
309 dtype: int32
310
311 >>> quarters_series = pd.Series(pd.date_range("2000-01-01", periods=3, freq="QE"))
312 >>> quarters_series
313 0 2000-03-31
314 1 2000-06-30
315 2 2000-09-30
316 dtype: datetime64[us]
317 >>> quarters_series.dt.quarter
318 0 1
319 1 2
320 2 3
321 dtype: int32
322
323 Returns a Series indexed like the original Series.
324 Raises TypeError if the Series does not contain datetimelike values.
325 """
326
327 def to_pydatetime(self) -> Series:
328 """
329 Return the data as a Series of :class:`datetime.datetime` objects.
330
331 Timezone information is retained if present.
332
333 .. warning::
334
335 Python's datetime uses microsecond resolution, which is lower than
336 pandas (nanosecond). The values are truncated.
337
338 Returns
339 -------
340 Series
341 Series containing native Python datetime objects.
342
343 See Also
344 --------
345 datetime.datetime : Standard library value for a datetime.
346
347 Examples
348 --------
349 >>> s = pd.Series(pd.date_range("20180310", periods=2))
350 >>> s
351 0 2018-03-10
352 1 2018-03-11
353 dtype: datetime64[us]
354
355 >>> s.dt.to_pydatetime()
356 0 2018-03-10 00:00:00
357 1 2018-03-11 00:00:00
358 dtype: object
359
360 pandas' nanosecond precision is truncated to microseconds.
361
362 >>> s = pd.Series(pd.date_range("20180310", periods=2, freq="ns"))
363 >>> s
364 0 2018-03-10 00:00:00.000000000
365 1 2018-03-10 00:00:00.000000001
366 dtype: datetime64[ns]
367
368 >>> s.dt.to_pydatetime()
369 0 2018-03-10 00:00:00
370 1 2018-03-10 00:00:00
371 dtype: object
372 """
373 # GH#20306
374 from pandas import Series
375
376 return Series(
377 self._get_values().to_pydatetime(),
378 index=self._parent.index,
379 name=self._parent.name,
380 dtype=object,
381 )
382
383 @property
384 def freq(self):
385 """
386 Return a string representing a frequency generated by infer_freq.
387
388 .. deprecated:: 3.1.0
389 A future version of pandas will return a :class:`BaseOffset`
390 object (or ``None``) instead of a string. Use
391 ``pd.set_option('future.infer_freq_returns_offset', True)`` to
392 opt in to the future behavior.
393
394 Returns None if it can't autodetect the frequency.
395
396 See Also
397 --------
398 Series.dt.to_period : Cast to PeriodArray/PeriodIndex at a particular
399 frequency.
400
401 Examples
402 --------
403 >>> ser = pd.Series(["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"])
404 >>> ser = pd.to_datetime(ser)
405 >>> ser.dt.freq
406 'D'
407
408 >>> ser = pd.Series(["2022-01-01", "2024-01-01", "2026-01-01", "2028-01-01"])
409 >>> ser = pd.to_datetime(ser)
410 >>> ser.dt.freq
411 '2YS-JAN'
412 """
413 return frequencies.maybe_convert_inferred_freq(
414 self._get_values()._inferred_freq_str, "Series.dt.freq"
415 )
416
417 def isocalendar(self) -> DataFrame:
418 """
419 Calculate year, week, and day according to the ISO 8601 standard.
420
421 The ISO 8601 standard defines the first week of the year as the week
422 containing the first Thursday. This method returns a DataFrame with
423 columns for the ISO year, ISO week number, and ISO day of week.
424
425 Returns
426 -------
427 DataFrame
428 With columns year, week and day.
429
430 See Also
431 --------
432 Timestamp.isocalendar : Function return a 3-tuple containing ISO year,
433 week number, and weekday for the given Timestamp object.
434 datetime.date.isocalendar : Return a named tuple object with
435 three components: year, week and weekday.
436
437 Examples
438 --------
439 >>> ser = pd.to_datetime(pd.Series(["2010-01-01", pd.NaT]))
440 >>> ser.dt.isocalendar()
441 year week day
442 0 2009 53 5
443 1 <NA> <NA> <NA>
444 >>> ser.dt.isocalendar().week
445 0 53
446 1 <NA>
447 Name: week, dtype: UInt32
448 """
449 return self._get_values().isocalendar().set_index(self._parent.index)
450
451 @property
452 def weekday(self) -> Series:
453 """
454 The day of the week with Monday=0, Sunday=6.
455
456 .. deprecated:: 3.1.0
457 Use :attr:`Series.dt.day_of_week` instead.
458 """
459 warnings.warn(
460 "Series.dt.weekday is deprecated and will be removed "
461 "in a future version. Use Series.dt.day_of_week instead.",
462 Pandas4Warning,
463 stacklevel=find_stack_level(),
464 )
465 return self.day_of_week # type: ignore[attr-defined]
466
467 @property
468 def dayofweek(self) -> Series:
469 """
470 The day of the week with Monday=0, Sunday=6.
471
472 .. deprecated:: 3.1.0
473 Use :attr:`Series.dt.day_of_week` instead.
474 """
475 warnings.warn(
476 "Series.dt.dayofweek is deprecated and will be removed in a "
477 "future version. Use Series.dt.day_of_week instead.",
478 Pandas4Warning,
479 stacklevel=find_stack_level(),
480 )
481 return self.day_of_week # type: ignore[attr-defined]
482
483 @property
484 def dayofyear(self) -> Series:
485 """
486 The ordinal day of the year.
487
488 .. deprecated:: 3.1.0
489 Use :attr:`Series.dt.day_of_year` instead.
490 """
491 warnings.warn(
492 "Series.dt.dayofyear is deprecated and will be removed in a "
493 "future version. Use Series.dt.day_of_year instead.",
494 Pandas4Warning,
495 stacklevel=find_stack_level(),
496 )
497 return self.day_of_year # type: ignore[attr-defined]
498
499 @property
500 def daysinmonth(self) -> Series:
501 """
502 The number of days in the month.
503
504 .. deprecated:: 3.1.0
505 Use :attr:`Series.dt.days_in_month` instead.
506 """
507 warnings.warn(
508 "Series.dt.daysinmonth is deprecated and will be removed in a "
509 "future version. Use Series.dt.days_in_month instead.",
510 Pandas4Warning,
511 stacklevel=find_stack_level(),
512 )
513 return self.days_in_month # type: ignore[attr-defined]
514
515
516@delegate_names(
517 delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property"
518)
519@delegate_names(
520 delegate=TimedeltaArray,
521 accessors=TimedeltaArray._datetimelike_methods,
522 typ="method",
523)
524class TimedeltaProperties(Properties):
525 """
526 Accessor object for datetimelike properties of the Series values.
527
528 Returns a Series indexed like the original Series.
529 Raises TypeError if the Series does not contain datetimelike values.
530
531 Examples
532 --------
533 >>> seconds_series = pd.Series(
534 ... pd.timedelta_range(start="1 second", periods=3, freq="s")
535 ... )
536 >>> seconds_series
537 0 0 days 00:00:01
538 1 0 days 00:00:02
539 2 0 days 00:00:03
540 dtype: timedelta64[us]
541 >>> seconds_series.dt.seconds
542 0 1
543 1 2
544 2 3
545 dtype: int32
546 """
547
548 def to_pytimedelta(self) -> np.ndarray:
549 """
550 Return an array of native :class:`datetime.timedelta` objects.
551
552 Python's standard `datetime` library uses a different representation
553 timedelta's. This method converts a Series of pandas Timedeltas
554 to `datetime.timedelta` format with the same length as the original
555 Series.
556
557 Returns
558 -------
559 numpy.ndarray
560 Array of 1D containing data with `datetime.timedelta` type.
561
562 See Also
563 --------
564 datetime.timedelta : A duration expressing the difference
565 between two date, time, or datetime.
566
567 Examples
568 --------
569 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="D"))
570 >>> s
571 0 0 days
572 1 1 days
573 2 2 days
574 3 3 days
575 4 4 days
576 dtype: timedelta64[us]
577
578 >>> s.dt.to_pytimedelta()
579 array([datetime.timedelta(0), datetime.timedelta(days=1),
580 datetime.timedelta(days=2), datetime.timedelta(days=3),
581 datetime.timedelta(days=4)], dtype=object)
582 """
583 # GH 57463
584 warnings.warn(
585 f"The behavior of {type(self).__name__}.to_pytimedelta is deprecated, "
586 "in a future version this will return a Series containing python "
587 "datetime.timedelta objects instead of an ndarray. To retain the "
588 "old behavior, call `np.array` on the result",
589 Pandas4Warning,
590 stacklevel=find_stack_level(),
591 )
592 return self._get_values().to_pytimedelta()
593
594 @property
595 def components(self) -> DataFrame:
596 """
597 Return a Dataframe of the components of the Timedeltas.
598
599 Each row of the DataFrame corresponds to a Timedelta in the original
600 Series and contains the individual components (days, hours, minutes,
601 seconds, milliseconds, microseconds, nanoseconds) of the Timedelta.
602
603 Returns
604 -------
605 DataFrame
606
607 See Also
608 --------
609 TimedeltaIndex.components : Return a DataFrame of the individual resolution
610 components of the Timedeltas.
611 Series.dt.total_seconds : Return the total number of seconds in the duration.
612
613 Examples
614 --------
615 >>> s = pd.Series(pd.to_timedelta(np.arange(5), unit="s"))
616 >>> s
617 0 0 days 00:00:00
618 1 0 days 00:00:01
619 2 0 days 00:00:02
620 3 0 days 00:00:03
621 4 0 days 00:00:04
622 dtype: timedelta64[us]
623 >>> s.dt.components
624 days hours minutes seconds milliseconds microseconds nanoseconds
625 0 0 0 0 0 0 0 0
626 1 0 0 0 1 0 0 0
627 2 0 0 0 2 0 0 0
628 3 0 0 0 3 0 0 0
629 4 0 0 0 4 0 0 0
630 """
631 return (
632 self._get_values()
633 .components.set_index(self._parent.index)
634 .__finalize__(self._parent)
635 )
636
637 @property
638 def freq(self):
639 """
640 Return a string representing a frequency generated by infer_freq.
641
642 .. deprecated:: 3.1.0
643 A future version of pandas will return a :class:`BaseOffset`
644 object (or ``None``) instead of a string. Use
645 ``pd.set_option('future.infer_freq_returns_offset', True)`` to
646 opt in to the future behavior.
647
648 Returns None if it can't autodetect the frequency.
649
650 See Also
651 --------
652 Series.dt.components : Return a DataFrame of the components of the
653 Timedeltas.
654
655 Examples
656 --------
657 >>> ser = pd.Series(pd.timedelta_range("1 day", periods=4))
658 >>> ser.dt.freq
659 'D'
660
661 >>> ser = pd.Series(pd.to_timedelta(["1 day", "3 days", "10 days"]))
662 >>> ser.dt.freq is None
663 True
664 """
665 return frequencies.maybe_convert_inferred_freq(
666 self._get_values()._inferred_freq_str, "Series.dt.freq"
667 )
668
669
670@delegate_names(
671 delegate=PeriodArray, accessors=PeriodArray._datetimelike_ops, typ="property"
672)
673@delegate_names(
674 delegate=PeriodArray, accessors=PeriodArray._datetimelike_methods, typ="method"
675)
676class PeriodProperties(Properties):
677 """
678 Accessor object for datetimelike properties of the Series values.
679
680 Returns a Series indexed like the original Series.
681 Raises TypeError if the Series does not contain datetimelike values.
682
683 Examples
684 --------
685 >>> seconds_series = pd.Series(
686 ... pd.period_range(
687 ... start="2000-01-01 00:00:00", end="2000-01-01 00:00:03", freq="s"
688 ... )
689 ... )
690 >>> seconds_series
691 0 2000-01-01 00:00:00
692 1 2000-01-01 00:00:01
693 2 2000-01-01 00:00:02
694 3 2000-01-01 00:00:03
695 dtype: period[s]
696 >>> seconds_series.dt.second
697 0 0
698 1 1
699 2 2
700 3 3
701 dtype: int64
702
703 >>> hours_series = pd.Series(
704 ... pd.period_range(start="2000-01-01 00:00", end="2000-01-01 03:00", freq="h")
705 ... )
706 >>> hours_series
707 0 2000-01-01 00:00
708 1 2000-01-01 01:00
709 2 2000-01-01 02:00
710 3 2000-01-01 03:00
711 dtype: period[h]
712 >>> hours_series.dt.hour
713 0 0
714 1 1
715 2 2
716 3 3
717 dtype: int64
718
719 >>> quarters_series = pd.Series(
720 ... pd.period_range(start="2000-01-01", end="2000-12-31", freq="Q-DEC")
721 ... )
722 >>> quarters_series
723 0 2000Q1
724 1 2000Q2
725 2 2000Q3
726 3 2000Q4
727 dtype: period[Q-DEC]
728 >>> quarters_series.dt.quarter
729 0 1
730 1 2
731 2 3
732 3 4
733 dtype: int64
734 """
735
736 @property
737 def weekday(self) -> Series:
738 """
739 The day of the week with Monday=0, Sunday=6.
740
741 .. deprecated:: 3.1.0
742 Use :attr:`Series.dt.day_of_week` instead.
743 """
744 warnings.warn(
745 "Series.dt.weekday is deprecated and will be removed "
746 "in a future version. Use Series.dt.day_of_week instead.",
747 Pandas4Warning,
748 stacklevel=find_stack_level(),
749 )
750 return self.day_of_week # type: ignore[attr-defined]
751
752 @property
753 def dayofweek(self):
754 """
755 The day of the week with Monday=0, Sunday=6.
756
757 .. deprecated:: 3.1.0
758 Use :attr:`Series.dt.day_of_week` instead.
759 """
760 warnings.warn(
761 "Series.dt.dayofweek is deprecated and will be removed in a "
762 "future version. Use Series.dt.day_of_week instead.",
763 Pandas4Warning,
764 stacklevel=find_stack_level(),
765 )
766 return self.day_of_week # type: ignore[attr-defined]
767
768 @property
769 def dayofyear(self):
770 """
771 The ordinal day of the year.
772
773 .. deprecated:: 3.1.0
774 Use :attr:`Series.dt.day_of_year` instead.
775 """
776 warnings.warn(
777 "Series.dt.dayofyear is deprecated and will be removed in a "
778 "future version. Use Series.dt.day_of_year instead.",
779 Pandas4Warning,
780 stacklevel=find_stack_level(),
781 )
782 return self.day_of_year # type: ignore[attr-defined]
783
784 @property
785 def daysinmonth(self):
786 """
787 The number of days in the month.
788
789 .. deprecated:: 3.1.0
790 Use :attr:`Series.dt.days_in_month` instead.
791 """
792 warnings.warn(
793 "Series.dt.daysinmonth is deprecated and will be removed in a "
794 "future version. Use Series.dt.days_in_month instead.",
795 Pandas4Warning,
796 stacklevel=find_stack_level(),
797 )
798 return self.days_in_month # type: ignore[attr-defined]
799
800
801class CombinedDatetimelikeProperties(
802 DatetimeProperties, TimedeltaProperties, PeriodProperties
803):
804 """
805 Accessor object for Series values' datetime-like, timedelta and period properties.
806
807 This accessor provides access to properties and methods for datetime-like,
808 timedelta, and period data types. It can be used with Series containing
809 datetime64, timedelta64, or period data.
810
811 Parameters
812 ----------
813 data : Series
814 Series with datetime-like, timedelta, or period dtype.
815
816 See Also
817 --------
818 DatetimeIndex : Index of datetime64 data.
819
820 Examples
821 --------
822 >>> dates = pd.Series(
823 ... ["2024-01-01", "2024-01-15", "2024-02-5"], dtype="datetime64[ns]"
824 ... )
825 >>> dates.dt.day
826 0 1
827 1 15
828 2 5
829 dtype: int32
830 >>> dates.dt.month
831 0 1
832 1 1
833 2 2
834 dtype: int32
835
836 >>> dates = pd.Series(
837 ... ["2024-01-01", "2024-01-15", "2024-02-5"], dtype="datetime64[ns, UTC]"
838 ... )
839 >>> dates.dt.day
840 0 1
841 1 15
842 2 5
843 dtype: int32
844 >>> dates.dt.month
845 0 1
846 1 1
847 2 2
848 dtype: int32
849 """
850
851 def __new__(cls, data: Series): # pyright: ignore[reportInconsistentConstructor]
852 # CombinedDatetimelikeProperties isn't really instantiated. Instead
853 # we need to choose which parent (datetime or timedelta) is
854 # appropriate. Since we're checking the dtypes anyway, we'll just
855 # do all the validation here.
856
857 if not isinstance(data, ABCSeries):
858 raise TypeError(
859 f"cannot convert an object of type {type(data)} to a datetimelike index"
860 )
861
862 orig = data if isinstance(data.dtype, CategoricalDtype) else None
863 if orig is not None:
864 data = data._constructor(
865 orig.array,
866 name=orig.name,
867 copy=False,
868 dtype=orig._values.categories.dtype,
869 index=orig.index,
870 )
871
872 if isinstance(data.dtype, ArrowDtype) and data.dtype.kind in "Mm":
873 return ArrowTemporalProperties(data, orig)
874 if lib.is_np_dtype(data.dtype, "M"):
875 return DatetimeProperties(data, orig)
876 elif isinstance(data.dtype, DatetimeTZDtype):
877 return DatetimeProperties(data, orig)
878 elif lib.is_np_dtype(data.dtype, "m"):
879 return TimedeltaProperties(data, orig)
880 elif isinstance(data.dtype, PeriodDtype):
881 return PeriodProperties(data, orig)
882
883 raise AttributeError("Can only use .dt accessor with datetimelike values")