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

1from __future__ import annotations 

2 

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 

17 

18import numpy as np 

19 

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 

48 

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 

56 

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 

70 

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 ) 

78 

79 

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) 

109 

110 return result 

111 

112 

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. 

142 

143 Represented internally as int64, and which can be boxed to Timestamp objects 

144 that are subclasses of datetime and carry metadata. 

145 

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. 

167 

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. 

185 

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 

218 

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 

238 

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. 

246 

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 """ 

252 

253 _typ = "datetimeindex" 

254 

255 _data_cls = DatetimeArray 

256 _supports_partial_string_indexing = True 

257 

258 @property 

259 def _engine_type(self) -> type[libindex.DatetimeEngine]: 

260 return libindex.DatetimeEngine 

261 

262 _data: DatetimeArray 

263 inferred_freq: str | None 

264 tz: tzinfo | None 

265 

266 # -------------------------------------------------------------------- 

267 # methods that dispatch to DatetimeArray and wrap result 

268 

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) 

273 

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) 

278 

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) 

283 

284 @doc(DatetimeArray.to_period) 

285 def to_period(self, freq=None) -> PeriodIndex: 

286 from pandas.core.indexes.api import PeriodIndex 

287 

288 arr = self._data.to_period(freq) 

289 return PeriodIndex._simple_new(arr, name=self.name) 

290 

291 @doc(DatetimeArray.to_perioddelta) 

292 def to_perioddelta(self, freq) -> TimedeltaIndex: 

293 from pandas.core.indexes.api import TimedeltaIndex 

294 

295 arr = self._data.to_perioddelta(freq) 

296 return TimedeltaIndex._simple_new(arr, name=self.name) 

297 

298 @doc(DatetimeArray.to_julian_date) 

299 def to_julian_date(self) -> Float64Index: 

300 from pandas.core.indexes.api import Float64Index 

301 

302 arr = self._data.to_julian_date() 

303 return Float64Index._simple_new(arr, name=self.name) 

304 

305 @doc(DatetimeArray.isocalendar) 

306 def isocalendar(self) -> DataFrame: 

307 df = self._data.isocalendar() 

308 return df.set_index(self) 

309 

310 # -------------------------------------------------------------------- 

311 # Constructors 

312 

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: 

327 

328 if is_scalar(data): 

329 raise cls._scalar_data_error(data) 

330 

331 # - Cases checked above all return/raise before reaching here - # 

332 

333 name = maybe_extract_name(name, data, cls) 

334 

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) 

358 

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 ) 

369 

370 subarr = cls._simple_new(dtarr, name=name) 

371 return subarr 

372 

373 # -------------------------------------------------------------------- 

374 

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) 

379 

380 Returns 

381 ------- 

382 bool 

383 """ 

384 from pandas.io.formats.format import is_dates_only 

385 

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] 

390 

391 def __reduce__(self): 

392 d = {"data": self._data, "name": self.name} 

393 return _new_DatetimeIndex, (type(self), d), None 

394 

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) 

404 

405 # -------------------------------------------------------------------- 

406 # Rendering Methods 

407 

408 @property 

409 def _formatter_func(self): 

410 from pandas.io.formats.format import get_format_datetime64 

411 

412 formatter = get_format_datetime64(is_dates_only=self._is_dates_only) 

413 return lambda x: f"'{formatter(x)}'" 

414 

415 # -------------------------------------------------------------------- 

416 # Set Operation Methods 

417 

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) 

434 

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 ) 

445 

446 this = self 

447 

448 for other in others: 

449 if not isinstance(this, DatetimeIndex): 

450 this = Index.union(this, other) 

451 continue 

452 

453 if not isinstance(other, DatetimeIndex): 

454 try: 

455 other = DatetimeIndex(other) 

456 except TypeError: 

457 pass 

458 

459 this, other = this._maybe_utc_convert(other) 

460 

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) 

466 

467 res_name = get_unanimous_names(self, *others)[0] 

468 if this.name != res_name: 

469 return this.rename(res_name) 

470 return this 

471 

472 def _maybe_utc_convert(self, other: Index) -> tuple[DatetimeIndex, Index]: 

473 this = self 

474 

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") 

478 

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 

483 

484 # -------------------------------------------------------------------- 

485 

486 def _get_time_micros(self) -> npt.NDArray[np.int64]: 

487 """ 

488 Return the number of microseconds since midnight. 

489 

490 Returns 

491 ------- 

492 ndarray[int64_t] 

493 """ 

494 values = self._data._local_timestamps() 

495 

496 reso = self._data._reso 

497 ppd = periods_per_day(reso) 

498 

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) 

510 

511 micros[self._isnan] = -1 

512 return micros 

513 

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. 

517 

518 Useful with map for returning an indexer based on an index. 

519 

520 Parameters 

521 ---------- 

522 keep_tz : optional, defaults True 

523 Return the data keeping the timezone. 

524 

525 If keep_tz is True: 

526 

527 If the timezone is not set, the resulting 

528 Series will have a datetime64[ns] dtype. 

529 

530 Otherwise the Series will have an datetime64[ns, tz] dtype; the 

531 tz will be preserved. 

532 

533 If keep_tz is False: 

534 

535 Series will have a datetime64[ns] dtype. TZ aware 

536 objects will have the tz removed. 

537 

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. 

542 

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. 

548 

549 Returns 

550 ------- 

551 Series 

552 """ 

553 from pandas import Series 

554 

555 if index is None: 

556 index = self._view() 

557 if name is None: 

558 name = self.name 

559 

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 

581 

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] 

589 

590 return Series(values, index=index, name=name) 

591 

592 def snap(self, freq="S") -> DatetimeIndex: 

593 """ 

594 Snap time stamps to nearest occurring frequency. 

595 

596 Returns 

597 ------- 

598 DatetimeIndex 

599 """ 

600 # Superdumb, punting on any optimizing 

601 freq = to_offset(freq) 

602 

603 dta = self._data.copy() 

604 

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 

615 

616 return DatetimeIndex._simple_new(dta, name=self.name) 

617 

618 # -------------------------------------------------------------------- 

619 # Indexing Methods 

620 

621 def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime): 

622 """ 

623 Calculate datetime bounds for parsed time string and its resolution. 

624 

625 Parameters 

626 ---------- 

627 reso : str 

628 Resolution provided by parsed string. 

629 parsed : datetime 

630 Datetime from parsed string. 

631 

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 

638 

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) 

645 

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 

655 

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()) 

681 

682 def get_loc(self, key, method=None, tolerance=None): 

683 """ 

684 Get integer location for requested label 

685 

686 Returns 

687 ------- 

688 loc : int 

689 """ 

690 self._check_indexing_error(key) 

691 

692 orig_key = key 

693 if is_valid_na_for_dtype(key, self.dtype): 

694 key = NaT 

695 

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) 

700 

701 elif isinstance(key, str): 

702 

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) 

708 

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 

715 

716 key = self._maybe_cast_for_get_loc(key) 

717 

718 elif isinstance(key, timedelta): 

719 # GH#20464 

720 raise TypeError( 

721 f"Cannot index {type(self).__name__} with {type(key).__name__}" 

722 ) 

723 

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) 

730 

731 else: 

732 # unrecognized type 

733 raise KeyError(key) 

734 

735 try: 

736 return Index.get_loc(self, key, method, tolerance) 

737 except KeyError as err: 

738 raise KeyError(orig_key) from err 

739 

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 

751 

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 

757 

758 @doc(DatetimeTimedeltaMixin._maybe_cast_slice_bound) 

759 def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): 

760 

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() 

766 

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) 

770 

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. 

775 

776 In addition to functionality provided by Index.slice_indexer, does the 

777 following: 

778 

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. 

783 

784 """ 

785 self._deprecated_arg(kind, "kind", "slice_indexer") 

786 

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) 

794 

795 if isinstance(start, time) or isinstance(end, time): 

796 raise KeyError("Cannot mix time and non-time slice keys") 

797 

798 def check_str_or_none(point): 

799 return point is not None and not isinstance(point, str) 

800 

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) 

810 

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 

817 

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 

822 

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 

836 

837 # -------------------------------------------------------------------- 

838 

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" 

844 

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. 

848 

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"). 

855 

856 Returns 

857 ------- 

858 np.ndarray[np.intp] 

859 

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") 

868 

869 if isinstance(time, str): 

870 from dateutil.parser import parse 

871 

872 time = parse(time).time() 

873 

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] 

882 

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. 

888 

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 

897 

898 Returns 

899 ------- 

900 np.ndarray[np.intp] 

901 

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) 

912 

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 

923 

924 if start_time <= end_time: 

925 join_op = operator.and_ 

926 else: 

927 join_op = operator.or_ 

928 

929 mask = join_op(lop(start_micros, time_micros), rop(time_micros, end_micros)) 

930 

931 return mask.nonzero()[0] 

932 

933 

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. 

948 

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.) 

957 

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). 

981 

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. 

987 

988 .. versionadded:: 1.4.0 

989 **kwargs 

990 For compatibility. Has no effect on the result. 

991 

992 Returns 

993 ------- 

994 rng : DatetimeIndex 

995 

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. 

1002 

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). 

1009 

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>`__. 

1012 

1013 Examples 

1014 -------- 

1015 **Specifying the values** 

1016 

1017 The next four examples generate the same `DatetimeIndex`, but vary 

1018 the combination of `start`, `end` and `periods`. 

1019 

1020 Specify `start` and `end`, with the default daily frequency. 

1021 

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') 

1026 

1027 Specify `start` and `periods`, the number of periods (days). 

1028 

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') 

1033 

1034 Specify `end` and `periods`, the number of periods (days). 

1035 

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') 

1040 

1041 Specify `start`, `end`, and `periods`; the frequency is generated 

1042 automatically (linearly spaced). 

1043 

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) 

1048 

1049 **Other Parameters** 

1050 

1051 Changed the `freq` (frequency) to ``'M'`` (month end frequency). 

1052 

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') 

1057 

1058 Multiples are allowed 

1059 

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') 

1064 

1065 `freq` can also be specified as an Offset object. 

1066 

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') 

1071 

1072 Specify `tz` to set the timezone. 

1073 

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') 

1079 

1080 `inclusive` controls whether to include `start` and `end` that are on the 

1081 boundary. The default, "both", includes boundary points on either end. 

1082 

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') 

1086 

1087 Use ``inclusive='left'`` to exclude `end` if it falls on the boundary. 

1088 

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') 

1092 

1093 Use ``inclusive='right'`` to exclude `start` if it falls on the boundary, and 

1094 similarly ``inclusive='neither'`` will exclude both `start` and `end`. 

1095 

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" 

1121 

1122 if freq is None and com.any_none(periods, start, end): 

1123 freq = "D" 

1124 

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) 

1136 

1137 

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. 

1154 

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). 

1183 

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. 

1189 

1190 .. versionadded:: 1.4.0 

1191 **kwargs 

1192 For compatibility. Has no effect on the result. 

1193 

1194 Returns 

1195 ------- 

1196 DatetimeIndex 

1197 

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. 

1204 

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>`__. 

1207 

1208 Examples 

1209 -------- 

1210 Note how the two weekend days are skipped in the result. 

1211 

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) 

1220 

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) 

1234 

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 ) 

1247 

1248 

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