Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/core/indexes/datetimes.py: 16%
363 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from __future__ import annotations
3from datetime import (
4 date,
5 datetime,
6 time,
7 timedelta,
8 tzinfo,
9)
10import operator
11from typing import (
12 TYPE_CHECKING,
13 Hashable,
14 Literal,
15)
16import warnings
18import numpy as np
20from pandas._libs import (
21 NaT,
22 Period,
23 Timestamp,
24 index as libindex,
25 lib,
26)
27from pandas._libs.tslibs import (
28 BaseOffset,
29 Resolution,
30 periods_per_day,
31 timezones,
32 to_offset,
33)
34from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
35from pandas._libs.tslibs.offsets import prefix_mapping
36from pandas._typing import (
37 Dtype,
38 DtypeObj,
39 IntervalClosedType,
40 IntervalLeftRight,
41 npt,
42)
43from pandas.util._decorators import (
44 cache_readonly,
45 doc,
46)
47from pandas.util._exceptions import find_stack_level
49from pandas.core.dtypes.common import (
50 is_datetime64_dtype,
51 is_datetime64tz_dtype,
52 is_dtype_equal,
53 is_scalar,
54)
55from pandas.core.dtypes.missing import is_valid_na_for_dtype
57from pandas.core.arrays.datetimes import (
58 DatetimeArray,
59 tz_to_dtype,
60)
61import pandas.core.common as com
62from pandas.core.indexes.base import (
63 Index,
64 get_unanimous_names,
65 maybe_extract_name,
66)
67from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin
68from pandas.core.indexes.extension import inherit_names
69from pandas.core.tools.times import to_time
71if TYPE_CHECKING: 71 ↛ 72line 71 didn't jump to line 72, because the condition on line 71 was never true
72 from pandas import (
73 DataFrame,
74 Float64Index,
75 PeriodIndex,
76 TimedeltaIndex,
77 )
80def _new_DatetimeIndex(cls, d):
81 """
82 This is called upon unpickling, rather than the default which doesn't
83 have arguments and breaks __new__
84 """
85 if "data" in d and not isinstance(d["data"], DatetimeIndex):
86 # Avoid need to verify integrity by calling simple_new directly
87 data = d.pop("data")
88 if not isinstance(data, DatetimeArray):
89 # For backward compat with older pickles, we may need to construct
90 # a DatetimeArray to adapt to the newer _simple_new signature
91 tz = d.pop("tz")
92 freq = d.pop("freq")
93 dta = DatetimeArray._simple_new(data, dtype=tz_to_dtype(tz), freq=freq)
94 else:
95 dta = data
96 for key in ["tz", "freq"]:
97 # These are already stored in our DatetimeArray; if they are
98 # also in the pickle and don't match, we have a problem.
99 if key in d:
100 assert d[key] == getattr(dta, key)
101 d.pop(key)
102 result = cls._simple_new(dta, **d)
103 else:
104 with warnings.catch_warnings():
105 # TODO: If we knew what was going in to **d, we might be able to
106 # go through _simple_new instead
107 warnings.simplefilter("ignore")
108 result = cls.__new__(cls, **d)
110 return result
113@inherit_names(
114 DatetimeArray._field_ops
115 + [
116 method
117 for method in DatetimeArray._datetimelike_methods
118 if method not in ("tz_localize", "tz_convert", "strftime")
119 ],
120 DatetimeArray,
121 wrap=True,
122)
123@inherit_names(["is_normalized", "_resolution_obj"], DatetimeArray, cache=True)
124@inherit_names(
125 [
126 "tz",
127 "tzinfo",
128 "dtype",
129 "to_pydatetime",
130 "_format_native_types",
131 "date",
132 "time",
133 "timetz",
134 "std",
135 ]
136 + DatetimeArray._bool_ops,
137 DatetimeArray,
138)
139class DatetimeIndex(DatetimeTimedeltaMixin):
140 """
141 Immutable ndarray-like of datetime64 data.
143 Represented internally as int64, and which can be boxed to Timestamp objects
144 that are subclasses of datetime and carry metadata.
146 Parameters
147 ----------
148 data : array-like (1-dimensional)
149 Datetime-like data to construct index with.
150 freq : str or pandas offset object, optional
151 One of pandas date offset strings or corresponding objects. The string
152 'infer' can be passed in order to set the frequency of the index as the
153 inferred frequency upon creation.
154 tz : pytz.timezone or dateutil.tz.tzfile or datetime.tzinfo or str
155 Set the Timezone of the data.
156 normalize : bool, default False
157 Normalize start/end dates to midnight before generating date range.
158 closed : {'left', 'right'}, optional
159 Set whether to include `start` and `end` that are on the
160 boundary. The default includes boundary points on either end.
161 ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise'
162 When clocks moved backward due to DST, ambiguous times may arise.
163 For example in Central European Time (UTC+01), when going from 03:00
164 DST to 02:00 non-DST, 02:30:00 local time occurs both at 00:30:00 UTC
165 and at 01:30:00 UTC. In such a situation, the `ambiguous` parameter
166 dictates how ambiguous times should be handled.
168 - 'infer' will attempt to infer fall dst-transition hours based on
169 order
170 - bool-ndarray where True signifies a DST time, False signifies a
171 non-DST time (note that this flag is only applicable for ambiguous
172 times)
173 - 'NaT' will return NaT where there are ambiguous times
174 - 'raise' will raise an AmbiguousTimeError if there are ambiguous times.
175 dayfirst : bool, default False
176 If True, parse dates in `data` with the day first order.
177 yearfirst : bool, default False
178 If True parse dates in `data` with the year first order.
179 dtype : numpy.dtype or DatetimeTZDtype or str, default None
180 Note that the only NumPy dtype allowed is ‘datetime64[ns]’.
181 copy : bool, default False
182 Make a copy of input ndarray.
183 name : label, default None
184 Name to be stored in the index.
186 Attributes
187 ----------
188 year
189 month
190 day
191 hour
192 minute
193 second
194 microsecond
195 nanosecond
196 date
197 time
198 timetz
199 dayofyear
200 day_of_year
201 weekofyear
202 week
203 dayofweek
204 day_of_week
205 weekday
206 quarter
207 tz
208 freq
209 freqstr
210 is_month_start
211 is_month_end
212 is_quarter_start
213 is_quarter_end
214 is_year_start
215 is_year_end
216 is_leap_year
217 inferred_freq
219 Methods
220 -------
221 normalize
222 strftime
223 snap
224 tz_convert
225 tz_localize
226 round
227 floor
228 ceil
229 to_period
230 to_perioddelta
231 to_pydatetime
232 to_series
233 to_frame
234 month_name
235 day_name
236 mean
237 std
239 See Also
240 --------
241 Index : The base pandas Index type.
242 TimedeltaIndex : Index of timedelta64 data.
243 PeriodIndex : Index of Period data.
244 to_datetime : Convert argument to datetime.
245 date_range : Create a fixed-frequency DatetimeIndex.
247 Notes
248 -----
249 To learn more about the frequency strings, please see `this link
250 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.
251 """
253 _typ = "datetimeindex"
255 _data_cls = DatetimeArray
256 _supports_partial_string_indexing = True
258 @property
259 def _engine_type(self) -> type[libindex.DatetimeEngine]:
260 return libindex.DatetimeEngine
262 _data: DatetimeArray
263 inferred_freq: str | None
264 tz: tzinfo | None
266 # --------------------------------------------------------------------
267 # methods that dispatch to DatetimeArray and wrap result
269 @doc(DatetimeArray.strftime)
270 def strftime(self, date_format) -> Index:
271 arr = self._data.strftime(date_format)
272 return Index(arr, name=self.name, dtype=object)
274 @doc(DatetimeArray.tz_convert)
275 def tz_convert(self, tz) -> DatetimeIndex:
276 arr = self._data.tz_convert(tz)
277 return type(self)._simple_new(arr, name=self.name)
279 @doc(DatetimeArray.tz_localize)
280 def tz_localize(self, tz, ambiguous="raise", nonexistent="raise") -> DatetimeIndex:
281 arr = self._data.tz_localize(tz, ambiguous, nonexistent)
282 return type(self)._simple_new(arr, name=self.name)
284 @doc(DatetimeArray.to_period)
285 def to_period(self, freq=None) -> PeriodIndex:
286 from pandas.core.indexes.api import PeriodIndex
288 arr = self._data.to_period(freq)
289 return PeriodIndex._simple_new(arr, name=self.name)
291 @doc(DatetimeArray.to_perioddelta)
292 def to_perioddelta(self, freq) -> TimedeltaIndex:
293 from pandas.core.indexes.api import TimedeltaIndex
295 arr = self._data.to_perioddelta(freq)
296 return TimedeltaIndex._simple_new(arr, name=self.name)
298 @doc(DatetimeArray.to_julian_date)
299 def to_julian_date(self) -> Float64Index:
300 from pandas.core.indexes.api import Float64Index
302 arr = self._data.to_julian_date()
303 return Float64Index._simple_new(arr, name=self.name)
305 @doc(DatetimeArray.isocalendar)
306 def isocalendar(self) -> DataFrame:
307 df = self._data.isocalendar()
308 return df.set_index(self)
310 # --------------------------------------------------------------------
311 # Constructors
313 def __new__(
314 cls,
315 data=None,
316 freq: str | BaseOffset | lib.NoDefault = lib.no_default,
317 tz=None,
318 normalize: bool = False,
319 closed=None,
320 ambiguous="raise",
321 dayfirst: bool = False,
322 yearfirst: bool = False,
323 dtype: Dtype | None = None,
324 copy: bool = False,
325 name: Hashable = None,
326 ) -> DatetimeIndex:
328 if is_scalar(data):
329 raise cls._scalar_data_error(data)
331 # - Cases checked above all return/raise before reaching here - #
333 name = maybe_extract_name(name, data, cls)
335 if (
336 isinstance(data, DatetimeArray)
337 and freq is lib.no_default
338 and tz is None
339 and dtype is None
340 ):
341 # fastpath, similar logic in TimedeltaIndex.__new__;
342 # Note in this particular case we retain non-nano.
343 if copy:
344 data = data.copy()
345 return cls._simple_new(data, name=name)
346 elif (
347 isinstance(data, DatetimeArray)
348 and freq is lib.no_default
349 and tz is None
350 and is_dtype_equal(data.dtype, dtype)
351 ):
352 # Reached via Index.__new__ when we call .astype
353 # TODO(2.0): special casing can be removed once _from_sequence_not_strict
354 # no longer chokes on non-nano
355 if copy:
356 data = data.copy()
357 return cls._simple_new(data, name=name)
359 dtarr = DatetimeArray._from_sequence_not_strict(
360 data,
361 dtype=dtype,
362 copy=copy,
363 tz=tz,
364 freq=freq,
365 dayfirst=dayfirst,
366 yearfirst=yearfirst,
367 ambiguous=ambiguous,
368 )
370 subarr = cls._simple_new(dtarr, name=name)
371 return subarr
373 # --------------------------------------------------------------------
375 @cache_readonly
376 def _is_dates_only(self) -> bool:
377 """
378 Return a boolean if we are only dates (and don't have a timezone)
380 Returns
381 -------
382 bool
383 """
384 from pandas.io.formats.format import is_dates_only
386 # error: Argument 1 to "is_dates_only" has incompatible type
387 # "Union[ExtensionArray, ndarray]"; expected "Union[ndarray,
388 # DatetimeArray, Index, DatetimeIndex]"
389 return self.tz is None and is_dates_only(self._values) # type: ignore[arg-type]
391 def __reduce__(self):
392 d = {"data": self._data, "name": self.name}
393 return _new_DatetimeIndex, (type(self), d), None
395 def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
396 """
397 Can we compare values of the given dtype to our own?
398 """
399 if self.tz is not None:
400 # If we have tz, we can compare to tzaware
401 return is_datetime64tz_dtype(dtype)
402 # if we dont have tz, we can only compare to tznaive
403 return is_datetime64_dtype(dtype)
405 # --------------------------------------------------------------------
406 # Rendering Methods
408 @property
409 def _formatter_func(self):
410 from pandas.io.formats.format import get_format_datetime64
412 formatter = get_format_datetime64(is_dates_only=self._is_dates_only)
413 return lambda x: f"'{formatter(x)}'"
415 # --------------------------------------------------------------------
416 # Set Operation Methods
418 def _can_range_setop(self, other) -> bool:
419 # GH 46702: If self or other have non-UTC tzs, DST transitions prevent
420 # range representation due to no singular step
421 if (
422 self.tz is not None
423 and not timezones.is_utc(self.tz)
424 and not timezones.is_fixed_offset(self.tz)
425 ):
426 return False
427 if (
428 other.tz is not None
429 and not timezones.is_utc(other.tz)
430 and not timezones.is_fixed_offset(other.tz)
431 ):
432 return False
433 return super()._can_range_setop(other)
435 def union_many(self, others):
436 """
437 A bit of a hack to accelerate unioning a collection of indexes.
438 """
439 warnings.warn(
440 "DatetimeIndex.union_many is deprecated and will be removed in "
441 "a future version. Use obj.union instead.",
442 FutureWarning,
443 stacklevel=find_stack_level(),
444 )
446 this = self
448 for other in others:
449 if not isinstance(this, DatetimeIndex):
450 this = Index.union(this, other)
451 continue
453 if not isinstance(other, DatetimeIndex):
454 try:
455 other = DatetimeIndex(other)
456 except TypeError:
457 pass
459 this, other = this._maybe_utc_convert(other)
461 if len(self) and len(other) and this._can_fast_union(other):
462 # union already has fastpath handling for empty cases
463 this = this._fast_union(other)
464 else:
465 this = Index.union(this, other)
467 res_name = get_unanimous_names(self, *others)[0]
468 if this.name != res_name:
469 return this.rename(res_name)
470 return this
472 def _maybe_utc_convert(self, other: Index) -> tuple[DatetimeIndex, Index]:
473 this = self
475 if isinstance(other, DatetimeIndex):
476 if (self.tz is None) ^ (other.tz is None):
477 raise TypeError("Cannot join tz-naive with tz-aware DatetimeIndex")
479 if not timezones.tz_compare(self.tz, other.tz):
480 this = self.tz_convert("UTC")
481 other = other.tz_convert("UTC")
482 return this, other
484 # --------------------------------------------------------------------
486 def _get_time_micros(self) -> npt.NDArray[np.int64]:
487 """
488 Return the number of microseconds since midnight.
490 Returns
491 -------
492 ndarray[int64_t]
493 """
494 values = self._data._local_timestamps()
496 reso = self._data._reso
497 ppd = periods_per_day(reso)
499 frac = values % ppd
500 if reso == NpyDatetimeUnit.NPY_FR_ns.value:
501 micros = frac // 1000
502 elif reso == NpyDatetimeUnit.NPY_FR_us.value:
503 micros = frac
504 elif reso == NpyDatetimeUnit.NPY_FR_ms.value:
505 micros = frac * 1000
506 elif reso == NpyDatetimeUnit.NPY_FR_s.value:
507 micros = frac * 1_000_000
508 else: # pragma: no cover
509 raise NotImplementedError(reso)
511 micros[self._isnan] = -1
512 return micros
514 def to_series(self, keep_tz=lib.no_default, index=None, name=None):
515 """
516 Create a Series with both index and values equal to the index keys.
518 Useful with map for returning an indexer based on an index.
520 Parameters
521 ----------
522 keep_tz : optional, defaults True
523 Return the data keeping the timezone.
525 If keep_tz is True:
527 If the timezone is not set, the resulting
528 Series will have a datetime64[ns] dtype.
530 Otherwise the Series will have an datetime64[ns, tz] dtype; the
531 tz will be preserved.
533 If keep_tz is False:
535 Series will have a datetime64[ns] dtype. TZ aware
536 objects will have the tz removed.
538 .. versionchanged:: 1.0.0
539 The default value is now True. In a future version,
540 this keyword will be removed entirely. Stop passing the
541 argument to obtain the future behavior and silence the warning.
543 index : Index, optional
544 Index of resulting Series. If None, defaults to original index.
545 name : str, optional
546 Name of resulting Series. If None, defaults to name of original
547 index.
549 Returns
550 -------
551 Series
552 """
553 from pandas import Series
555 if index is None:
556 index = self._view()
557 if name is None:
558 name = self.name
560 if keep_tz is not lib.no_default:
561 if keep_tz:
562 warnings.warn(
563 "The 'keep_tz' keyword in DatetimeIndex.to_series "
564 "is deprecated and will be removed in a future version. "
565 "You can stop passing 'keep_tz' to silence this warning.",
566 FutureWarning,
567 stacklevel=find_stack_level(),
568 )
569 else:
570 warnings.warn(
571 "Specifying 'keep_tz=False' is deprecated and this "
572 "option will be removed in a future release. If "
573 "you want to remove the timezone information, you "
574 "can do 'idx.tz_convert(None)' before calling "
575 "'to_series'.",
576 FutureWarning,
577 stacklevel=find_stack_level(),
578 )
579 else:
580 keep_tz = True
582 if keep_tz and self.tz is not None:
583 # preserve the tz & copy
584 values = self.copy(deep=True)
585 else:
586 # error: Incompatible types in assignment (expression has type
587 # "Union[ExtensionArray, ndarray]", variable has type "DatetimeIndex")
588 values = self._values.view("M8[ns]").copy() # type: ignore[assignment]
590 return Series(values, index=index, name=name)
592 def snap(self, freq="S") -> DatetimeIndex:
593 """
594 Snap time stamps to nearest occurring frequency.
596 Returns
597 -------
598 DatetimeIndex
599 """
600 # Superdumb, punting on any optimizing
601 freq = to_offset(freq)
603 dta = self._data.copy()
605 for i, v in enumerate(self):
606 s = v
607 if not freq.is_on_offset(s):
608 t0 = freq.rollback(s)
609 t1 = freq.rollforward(s)
610 if abs(s - t0) < abs(t1 - s):
611 s = t0
612 else:
613 s = t1
614 dta[i] = s
616 return DatetimeIndex._simple_new(dta, name=self.name)
618 # --------------------------------------------------------------------
619 # Indexing Methods
621 def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
622 """
623 Calculate datetime bounds for parsed time string and its resolution.
625 Parameters
626 ----------
627 reso : str
628 Resolution provided by parsed string.
629 parsed : datetime
630 Datetime from parsed string.
632 Returns
633 -------
634 lower, upper: pd.Timestamp
635 """
636 per = Period(parsed, freq=reso.attr_abbrev)
637 start, end = per.start_time, per.end_time
639 # GH 24076
640 # If an incoming date string contained a UTC offset, need to localize
641 # the parsed date to this offset first before aligning with the index's
642 # timezone
643 start = start.tz_localize(parsed.tzinfo)
644 end = end.tz_localize(parsed.tzinfo)
646 if parsed.tzinfo is not None:
647 if self.tz is None:
648 raise ValueError(
649 "The index must be timezone aware when indexing "
650 "with a date string with a UTC offset"
651 )
652 start = self._maybe_cast_for_get_loc(start)
653 end = self._maybe_cast_for_get_loc(end)
654 return start, end
656 def _deprecate_mismatched_indexing(self, key, one_way: bool = False) -> None:
657 # GH#36148
658 # we get here with isinstance(key, self._data._recognized_scalars)
659 try:
660 self._data._assert_tzawareness_compat(key)
661 except TypeError:
662 if self.tz is None:
663 msg = (
664 "Indexing a timezone-naive DatetimeIndex with a "
665 "timezone-aware datetime is deprecated and will "
666 "raise KeyError in a future version. "
667 "Use a timezone-naive object instead."
668 )
669 elif one_way:
670 # we special-case timezone-naive strings and timezone-aware
671 # DatetimeIndex
672 return
673 else:
674 msg = (
675 "Indexing a timezone-aware DatetimeIndex with a "
676 "timezone-naive datetime is deprecated and will "
677 "raise KeyError in a future version. "
678 "Use a timezone-aware object instead."
679 )
680 warnings.warn(msg, FutureWarning, stacklevel=find_stack_level())
682 def get_loc(self, key, method=None, tolerance=None):
683 """
684 Get integer location for requested label
686 Returns
687 -------
688 loc : int
689 """
690 self._check_indexing_error(key)
692 orig_key = key
693 if is_valid_na_for_dtype(key, self.dtype):
694 key = NaT
696 if isinstance(key, self._data._recognized_scalars):
697 # needed to localize naive datetimes
698 self._deprecate_mismatched_indexing(key)
699 key = self._maybe_cast_for_get_loc(key)
701 elif isinstance(key, str):
703 try:
704 parsed, reso = self._parse_with_reso(key)
705 except ValueError as err:
706 raise KeyError(key) from err
707 self._deprecate_mismatched_indexing(parsed, one_way=True)
709 if self._can_partial_date_slice(reso):
710 try:
711 return self._partial_date_slice(reso, parsed)
712 except KeyError as err:
713 if method is None:
714 raise KeyError(key) from err
716 key = self._maybe_cast_for_get_loc(key)
718 elif isinstance(key, timedelta):
719 # GH#20464
720 raise TypeError(
721 f"Cannot index {type(self).__name__} with {type(key).__name__}"
722 )
724 elif isinstance(key, time):
725 if method is not None:
726 raise NotImplementedError(
727 "cannot yet lookup inexact labels when key is a time object"
728 )
729 return self.indexer_at_time(key)
731 else:
732 # unrecognized type
733 raise KeyError(key)
735 try:
736 return Index.get_loc(self, key, method, tolerance)
737 except KeyError as err:
738 raise KeyError(orig_key) from err
740 def _maybe_cast_for_get_loc(self, key) -> Timestamp:
741 # needed to localize naive datetimes or dates (GH 35690)
742 try:
743 key = Timestamp(key)
744 except ValueError as err:
745 # FIXME(dateutil#1180): we get here because parse_with_reso
746 # doesn't raise on "t2m"
747 if not isinstance(key, str):
748 # Not expected to be reached, but check to be sure
749 raise # pragma: no cover
750 raise KeyError(key) from err
752 if key.tzinfo is None:
753 key = key.tz_localize(self.tz)
754 else:
755 key = key.tz_convert(self.tz)
756 return key
758 @doc(DatetimeTimedeltaMixin._maybe_cast_slice_bound)
759 def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
761 # GH#42855 handle date here instead of get_slice_bound
762 if isinstance(label, date) and not isinstance(label, datetime):
763 # Pandas supports slicing with dates, treated as datetimes at midnight.
764 # https://github.com/pandas-dev/pandas/issues/31501
765 label = Timestamp(label).to_pydatetime()
767 label = super()._maybe_cast_slice_bound(label, side, kind=kind)
768 self._deprecate_mismatched_indexing(label)
769 return self._maybe_cast_for_get_loc(label)
771 def slice_indexer(self, start=None, end=None, step=None, kind=lib.no_default):
772 """
773 Return indexer for specified label slice.
774 Index.slice_indexer, customized to handle time slicing.
776 In addition to functionality provided by Index.slice_indexer, does the
777 following:
779 - if both `start` and `end` are instances of `datetime.time`, it
780 invokes `indexer_between_time`
781 - if `start` and `end` are both either string or None perform
782 value-based selection in non-monotonic cases.
784 """
785 self._deprecated_arg(kind, "kind", "slice_indexer")
787 # For historical reasons DatetimeIndex supports slices between two
788 # instances of datetime.time as if it were applying a slice mask to
789 # an array of (self.hour, self.minute, self.seconds, self.microsecond).
790 if isinstance(start, time) and isinstance(end, time):
791 if step is not None and step != 1:
792 raise ValueError("Must have step size of 1 with time slices")
793 return self.indexer_between_time(start, end)
795 if isinstance(start, time) or isinstance(end, time):
796 raise KeyError("Cannot mix time and non-time slice keys")
798 def check_str_or_none(point):
799 return point is not None and not isinstance(point, str)
801 # GH#33146 if start and end are combinations of str and None and Index is not
802 # monotonic, we can not use Index.slice_indexer because it does not honor the
803 # actual elements, is only searching for start and end
804 if (
805 check_str_or_none(start)
806 or check_str_or_none(end)
807 or self.is_monotonic_increasing
808 ):
809 return Index.slice_indexer(self, start, end, step, kind=kind)
811 mask = np.array(True)
812 deprecation_mask = np.array(True)
813 if start is not None:
814 start_casted = self._maybe_cast_slice_bound(start, "left")
815 mask = start_casted <= self
816 deprecation_mask = start_casted == self
818 if end is not None:
819 end_casted = self._maybe_cast_slice_bound(end, "right")
820 mask = (self <= end_casted) & mask
821 deprecation_mask = (end_casted == self) | deprecation_mask
823 if not deprecation_mask.any():
824 warnings.warn(
825 "Value based partial slicing on non-monotonic DatetimeIndexes "
826 "with non-existing keys is deprecated and will raise a "
827 "KeyError in a future Version.",
828 FutureWarning,
829 stacklevel=find_stack_level(),
830 )
831 indexer = mask.nonzero()[0][::step]
832 if len(indexer) == len(self):
833 return slice(None)
834 else:
835 return indexer
837 # --------------------------------------------------------------------
839 @property
840 def inferred_type(self) -> str:
841 # b/c datetime is represented as microseconds since the epoch, make
842 # sure we can't have ambiguous indexing
843 return "datetime64"
845 def indexer_at_time(self, time, asof: bool = False) -> npt.NDArray[np.intp]:
846 """
847 Return index locations of values at particular time of day.
849 Parameters
850 ----------
851 time : datetime.time or str
852 Time passed in either as object (datetime.time) or as string in
853 appropriate format ("%H:%M", "%H%M", "%I:%M%p", "%I%M%p",
854 "%H:%M:%S", "%H%M%S", "%I:%M:%S%p", "%I%M%S%p").
856 Returns
857 -------
858 np.ndarray[np.intp]
860 See Also
861 --------
862 indexer_between_time : Get index locations of values between particular
863 times of day.
864 DataFrame.at_time : Select values at particular time of day.
865 """
866 if asof:
867 raise NotImplementedError("'asof' argument is not supported")
869 if isinstance(time, str):
870 from dateutil.parser import parse
872 time = parse(time).time()
874 if time.tzinfo:
875 if self.tz is None:
876 raise ValueError("Index must be timezone aware.")
877 time_micros = self.tz_convert(time.tzinfo)._get_time_micros()
878 else:
879 time_micros = self._get_time_micros()
880 micros = _time_to_micros(time)
881 return (time_micros == micros).nonzero()[0]
883 def indexer_between_time(
884 self, start_time, end_time, include_start: bool = True, include_end: bool = True
885 ) -> npt.NDArray[np.intp]:
886 """
887 Return index locations of values between particular times of day.
889 Parameters
890 ----------
891 start_time, end_time : datetime.time, str
892 Time passed either as object (datetime.time) or as string in
893 appropriate format ("%H:%M", "%H%M", "%I:%M%p", "%I%M%p",
894 "%H:%M:%S", "%H%M%S", "%I:%M:%S%p","%I%M%S%p").
895 include_start : bool, default True
896 include_end : bool, default True
898 Returns
899 -------
900 np.ndarray[np.intp]
902 See Also
903 --------
904 indexer_at_time : Get index locations of values at particular time of day.
905 DataFrame.between_time : Select values between particular times of day.
906 """
907 start_time = to_time(start_time)
908 end_time = to_time(end_time)
909 time_micros = self._get_time_micros()
910 start_micros = _time_to_micros(start_time)
911 end_micros = _time_to_micros(end_time)
913 if include_start and include_end:
914 lop = rop = operator.le
915 elif include_start:
916 lop = operator.le
917 rop = operator.lt
918 elif include_end:
919 lop = operator.lt
920 rop = operator.le
921 else:
922 lop = rop = operator.lt
924 if start_time <= end_time:
925 join_op = operator.and_
926 else:
927 join_op = operator.or_
929 mask = join_op(lop(start_micros, time_micros), rop(time_micros, end_micros))
931 return mask.nonzero()[0]
934def date_range(
935 start=None,
936 end=None,
937 periods=None,
938 freq=None,
939 tz=None,
940 normalize: bool = False,
941 name: Hashable = None,
942 closed: Literal["left", "right"] | None | lib.NoDefault = lib.no_default,
943 inclusive: IntervalClosedType | None = None,
944 **kwargs,
945) -> DatetimeIndex:
946 """
947 Return a fixed frequency DatetimeIndex.
949 Returns the range of equally spaced time points (where the difference between any
950 two adjacent points is specified by the given frequency) such that they all
951 satisfy `start <[=] x <[=] end`, where the first one and the last one are, resp.,
952 the first and last time points in that range that fall on the boundary of ``freq``
953 (if given as a frequency string) or that are valid for ``freq`` (if given as a
954 :class:`pandas.tseries.offsets.DateOffset`). (If exactly one of ``start``,
955 ``end``, or ``freq`` is *not* specified, this missing parameter can be computed
956 given ``periods``, the number of timesteps in the range. See the note below.)
958 Parameters
959 ----------
960 start : str or datetime-like, optional
961 Left bound for generating dates.
962 end : str or datetime-like, optional
963 Right bound for generating dates.
964 periods : int, optional
965 Number of periods to generate.
966 freq : str or DateOffset, default 'D'
967 Frequency strings can have multiples, e.g. '5H'. See
968 :ref:`here <timeseries.offset_aliases>` for a list of
969 frequency aliases.
970 tz : str or tzinfo, optional
971 Time zone name for returning localized DatetimeIndex, for example
972 'Asia/Hong_Kong'. By default, the resulting DatetimeIndex is
973 timezone-naive.
974 normalize : bool, default False
975 Normalize start/end dates to midnight before generating date range.
976 name : str, default None
977 Name of the resulting DatetimeIndex.
978 closed : {None, 'left', 'right'}, optional
979 Make the interval closed with respect to the given frequency to
980 the 'left', 'right', or both sides (None, the default).
982 .. deprecated:: 1.4.0
983 Argument `closed` has been deprecated to standardize boundary inputs.
984 Use `inclusive` instead, to set each bound as closed or open.
985 inclusive : {"both", "neither", "left", "right"}, default "both"
986 Include boundaries; Whether to set each bound as closed or open.
988 .. versionadded:: 1.4.0
989 **kwargs
990 For compatibility. Has no effect on the result.
992 Returns
993 -------
994 rng : DatetimeIndex
996 See Also
997 --------
998 DatetimeIndex : An immutable container for datetimes.
999 timedelta_range : Return a fixed frequency TimedeltaIndex.
1000 period_range : Return a fixed frequency PeriodIndex.
1001 interval_range : Return a fixed frequency IntervalIndex.
1003 Notes
1004 -----
1005 Of the four parameters ``start``, ``end``, ``periods``, and ``freq``,
1006 exactly three must be specified. If ``freq`` is omitted, the resulting
1007 ``DatetimeIndex`` will have ``periods`` linearly spaced elements between
1008 ``start`` and ``end`` (closed on both sides).
1010 To learn more about the frequency strings, please see `this link
1011 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.
1013 Examples
1014 --------
1015 **Specifying the values**
1017 The next four examples generate the same `DatetimeIndex`, but vary
1018 the combination of `start`, `end` and `periods`.
1020 Specify `start` and `end`, with the default daily frequency.
1022 >>> pd.date_range(start='1/1/2018', end='1/08/2018')
1023 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
1024 '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
1025 dtype='datetime64[ns]', freq='D')
1027 Specify `start` and `periods`, the number of periods (days).
1029 >>> pd.date_range(start='1/1/2018', periods=8)
1030 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
1031 '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'],
1032 dtype='datetime64[ns]', freq='D')
1034 Specify `end` and `periods`, the number of periods (days).
1036 >>> pd.date_range(end='1/1/2018', periods=8)
1037 DatetimeIndex(['2017-12-25', '2017-12-26', '2017-12-27', '2017-12-28',
1038 '2017-12-29', '2017-12-30', '2017-12-31', '2018-01-01'],
1039 dtype='datetime64[ns]', freq='D')
1041 Specify `start`, `end`, and `periods`; the frequency is generated
1042 automatically (linearly spaced).
1044 >>> pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)
1045 DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00',
1046 '2018-04-27 00:00:00'],
1047 dtype='datetime64[ns]', freq=None)
1049 **Other Parameters**
1051 Changed the `freq` (frequency) to ``'M'`` (month end frequency).
1053 >>> pd.date_range(start='1/1/2018', periods=5, freq='M')
1054 DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30',
1055 '2018-05-31'],
1056 dtype='datetime64[ns]', freq='M')
1058 Multiples are allowed
1060 >>> pd.date_range(start='1/1/2018', periods=5, freq='3M')
1061 DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
1062 '2019-01-31'],
1063 dtype='datetime64[ns]', freq='3M')
1065 `freq` can also be specified as an Offset object.
1067 >>> pd.date_range(start='1/1/2018', periods=5, freq=pd.offsets.MonthEnd(3))
1068 DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31',
1069 '2019-01-31'],
1070 dtype='datetime64[ns]', freq='3M')
1072 Specify `tz` to set the timezone.
1074 >>> pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo')
1075 DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00',
1076 '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00',
1077 '2018-01-05 00:00:00+09:00'],
1078 dtype='datetime64[ns, Asia/Tokyo]', freq='D')
1080 `inclusive` controls whether to include `start` and `end` that are on the
1081 boundary. The default, "both", includes boundary points on either end.
1083 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive="both")
1084 DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04'],
1085 dtype='datetime64[ns]', freq='D')
1087 Use ``inclusive='left'`` to exclude `end` if it falls on the boundary.
1089 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive='left')
1090 DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03'],
1091 dtype='datetime64[ns]', freq='D')
1093 Use ``inclusive='right'`` to exclude `start` if it falls on the boundary, and
1094 similarly ``inclusive='neither'`` will exclude both `start` and `end`.
1096 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive='right')
1097 DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'],
1098 dtype='datetime64[ns]', freq='D')
1099 """
1100 if inclusive is not None and closed is not lib.no_default:
1101 raise ValueError(
1102 "Deprecated argument `closed` cannot be passed"
1103 "if argument `inclusive` is not None"
1104 )
1105 elif closed is not lib.no_default:
1106 warnings.warn(
1107 "Argument `closed` is deprecated in favor of `inclusive`.",
1108 FutureWarning,
1109 stacklevel=find_stack_level(),
1110 )
1111 if closed is None:
1112 inclusive = "both"
1113 elif closed in ("left", "right"):
1114 inclusive = closed
1115 else:
1116 raise ValueError(
1117 "Argument `closed` has to be either 'left', 'right' or None"
1118 )
1119 elif inclusive is None:
1120 inclusive = "both"
1122 if freq is None and com.any_none(periods, start, end):
1123 freq = "D"
1125 dtarr = DatetimeArray._generate_range(
1126 start=start,
1127 end=end,
1128 periods=periods,
1129 freq=freq,
1130 tz=tz,
1131 normalize=normalize,
1132 inclusive=inclusive,
1133 **kwargs,
1134 )
1135 return DatetimeIndex._simple_new(dtarr, name=name)
1138def bdate_range(
1139 start=None,
1140 end=None,
1141 periods: int | None = None,
1142 freq="B",
1143 tz=None,
1144 normalize: bool = True,
1145 name: Hashable = None,
1146 weekmask=None,
1147 holidays=None,
1148 closed: IntervalLeftRight | lib.NoDefault | None = lib.no_default,
1149 inclusive: IntervalClosedType | None = None,
1150 **kwargs,
1151) -> DatetimeIndex:
1152 """
1153 Return a fixed frequency DatetimeIndex with business day as the default.
1155 Parameters
1156 ----------
1157 start : str or datetime-like, default None
1158 Left bound for generating dates.
1159 end : str or datetime-like, default None
1160 Right bound for generating dates.
1161 periods : int, default None
1162 Number of periods to generate.
1163 freq : str or DateOffset, default 'B' (business daily)
1164 Frequency strings can have multiples, e.g. '5H'.
1165 tz : str or None
1166 Time zone name for returning localized DatetimeIndex, for example
1167 Asia/Beijing.
1168 normalize : bool, default False
1169 Normalize start/end dates to midnight before generating date range.
1170 name : str, default None
1171 Name of the resulting DatetimeIndex.
1172 weekmask : str or None, default None
1173 Weekmask of valid business days, passed to ``numpy.busdaycalendar``,
1174 only used when custom frequency strings are passed. The default
1175 value None is equivalent to 'Mon Tue Wed Thu Fri'.
1176 holidays : list-like or None, default None
1177 Dates to exclude from the set of valid business days, passed to
1178 ``numpy.busdaycalendar``, only used when custom frequency strings
1179 are passed.
1180 closed : str, default None
1181 Make the interval closed with respect to the given frequency to
1182 the 'left', 'right', or both sides (None).
1184 .. deprecated:: 1.4.0
1185 Argument `closed` has been deprecated to standardize boundary inputs.
1186 Use `inclusive` instead, to set each bound as closed or open.
1187 inclusive : {"both", "neither", "left", "right"}, default "both"
1188 Include boundaries; Whether to set each bound as closed or open.
1190 .. versionadded:: 1.4.0
1191 **kwargs
1192 For compatibility. Has no effect on the result.
1194 Returns
1195 -------
1196 DatetimeIndex
1198 Notes
1199 -----
1200 Of the four parameters: ``start``, ``end``, ``periods``, and ``freq``,
1201 exactly three must be specified. Specifying ``freq`` is a requirement
1202 for ``bdate_range``. Use ``date_range`` if specifying ``freq`` is not
1203 desired.
1205 To learn more about the frequency strings, please see `this link
1206 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__.
1208 Examples
1209 --------
1210 Note how the two weekend days are skipped in the result.
1212 >>> pd.bdate_range(start='1/1/2018', end='1/08/2018')
1213 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04',
1214 '2018-01-05', '2018-01-08'],
1215 dtype='datetime64[ns]', freq='B')
1216 """
1217 if freq is None:
1218 msg = "freq must be specified for bdate_range; use date_range instead"
1219 raise TypeError(msg)
1221 if isinstance(freq, str) and freq.startswith("C"):
1222 try:
1223 weekmask = weekmask or "Mon Tue Wed Thu Fri"
1224 freq = prefix_mapping[freq](holidays=holidays, weekmask=weekmask)
1225 except (KeyError, TypeError) as err:
1226 msg = f"invalid custom frequency string: {freq}"
1227 raise ValueError(msg) from err
1228 elif holidays or weekmask:
1229 msg = (
1230 "a custom frequency string is required when holidays or "
1231 f"weekmask are passed, got frequency {freq}"
1232 )
1233 raise ValueError(msg)
1235 return date_range(
1236 start=start,
1237 end=end,
1238 periods=periods,
1239 freq=freq,
1240 tz=tz,
1241 normalize=normalize,
1242 name=name,
1243 closed=closed,
1244 inclusive=inclusive,
1245 **kwargs,
1246 )
1249def _time_to_micros(time_obj: time) -> int:
1250 seconds = time_obj.hour * 60 * 60 + 60 * time_obj.minute + time_obj.second
1251 return 1_000_000 * seconds + time_obj.microsecond