Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pandas/core/indexes/datetimes.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

285 statements  

1from __future__ import annotations 

2 

3import datetime as dt 

4import operator 

5from typing import ( 

6 TYPE_CHECKING, 

7 Hashable, 

8) 

9import warnings 

10 

11import numpy as np 

12import pytz 

13 

14from pandas._libs import ( 

15 NaT, 

16 Period, 

17 Timestamp, 

18 index as libindex, 

19 lib, 

20) 

21from pandas._libs.tslibs import ( 

22 Resolution, 

23 periods_per_day, 

24 timezones, 

25 to_offset, 

26) 

27from pandas._libs.tslibs.offsets import prefix_mapping 

28from pandas._typing import ( 

29 Dtype, 

30 DtypeObj, 

31 Frequency, 

32 IntervalClosedType, 

33 TimeAmbiguous, 

34 TimeNonexistent, 

35 npt, 

36) 

37from pandas.util._decorators import ( 

38 cache_readonly, 

39 doc, 

40) 

41 

42from pandas.core.dtypes.common import ( 

43 is_datetime64_dtype, 

44 is_datetime64tz_dtype, 

45 is_scalar, 

46) 

47from pandas.core.dtypes.generic import ABCSeries 

48from pandas.core.dtypes.missing import is_valid_na_for_dtype 

49 

50from pandas.core.arrays.datetimes import ( 

51 DatetimeArray, 

52 tz_to_dtype, 

53) 

54import pandas.core.common as com 

55from pandas.core.indexes.base import ( 

56 Index, 

57 maybe_extract_name, 

58) 

59from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin 

60from pandas.core.indexes.extension import inherit_names 

61from pandas.core.tools.times import to_time 

62 

63if TYPE_CHECKING: 

64 from pandas.core.api import ( 

65 DataFrame, 

66 PeriodIndex, 

67 ) 

68 

69 

70def _new_DatetimeIndex(cls, d): 

71 """ 

72 This is called upon unpickling, rather than the default which doesn't 

73 have arguments and breaks __new__ 

74 """ 

75 if "data" in d and not isinstance(d["data"], DatetimeIndex): 

76 # Avoid need to verify integrity by calling simple_new directly 

77 data = d.pop("data") 

78 if not isinstance(data, DatetimeArray): 

79 # For backward compat with older pickles, we may need to construct 

80 # a DatetimeArray to adapt to the newer _simple_new signature 

81 tz = d.pop("tz") 

82 freq = d.pop("freq") 

83 dta = DatetimeArray._simple_new(data, dtype=tz_to_dtype(tz), freq=freq) 

84 else: 

85 dta = data 

86 for key in ["tz", "freq"]: 

87 # These are already stored in our DatetimeArray; if they are 

88 # also in the pickle and don't match, we have a problem. 

89 if key in d: 

90 assert d[key] == getattr(dta, key) 

91 d.pop(key) 

92 result = cls._simple_new(dta, **d) 

93 else: 

94 with warnings.catch_warnings(): 

95 # TODO: If we knew what was going in to **d, we might be able to 

96 # go through _simple_new instead 

97 warnings.simplefilter("ignore") 

98 result = cls.__new__(cls, **d) 

99 

100 return result 

101 

102 

103@inherit_names( 

104 DatetimeArray._field_ops 

105 + [ 

106 method 

107 for method in DatetimeArray._datetimelike_methods 

108 if method not in ("tz_localize", "tz_convert", "strftime") 

109 ], 

110 DatetimeArray, 

111 wrap=True, 

112) 

113@inherit_names(["is_normalized"], DatetimeArray, cache=True) 

114@inherit_names( 

115 [ 

116 "tz", 

117 "tzinfo", 

118 "dtype", 

119 "to_pydatetime", 

120 "_format_native_types", 

121 "date", 

122 "time", 

123 "timetz", 

124 "std", 

125 ] 

126 + DatetimeArray._bool_ops, 

127 DatetimeArray, 

128) 

129class DatetimeIndex(DatetimeTimedeltaMixin): 

130 """ 

131 Immutable ndarray-like of datetime64 data. 

132 

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

134 that are subclasses of datetime and carry metadata. 

135 

136 .. versionchanged:: 2.0.0 

137 The various numeric date/time attributes (:attr:`~DatetimeIndex.day`, 

138 :attr:`~DatetimeIndex.month`, :attr:`~DatetimeIndex.year` etc.) now have dtype 

139 ``int32``. Previously they had dtype ``int64``. 

140 

141 Parameters 

142 ---------- 

143 data : array-like (1-dimensional) 

144 Datetime-like data to construct index with. 

145 freq : str or pandas offset object, optional 

146 One of pandas date offset strings or corresponding objects. The string 

147 'infer' can be passed in order to set the frequency of the index as the 

148 inferred frequency upon creation. 

149 tz : pytz.timezone or dateutil.tz.tzfile or datetime.tzinfo or str 

150 Set the Timezone of the data. 

151 normalize : bool, default False 

152 Normalize start/end dates to midnight before generating date range. 

153 closed : {'left', 'right'}, optional 

154 Set whether to include `start` and `end` that are on the 

155 boundary. The default includes boundary points on either end. 

156 ambiguous : 'infer', bool-ndarray, 'NaT', default 'raise' 

157 When clocks moved backward due to DST, ambiguous times may arise. 

158 For example in Central European Time (UTC+01), when going from 03:00 

159 DST to 02:00 non-DST, 02:30:00 local time occurs both at 00:30:00 UTC 

160 and at 01:30:00 UTC. In such a situation, the `ambiguous` parameter 

161 dictates how ambiguous times should be handled. 

162 

163 - 'infer' will attempt to infer fall dst-transition hours based on 

164 order 

165 - bool-ndarray where True signifies a DST time, False signifies a 

166 non-DST time (note that this flag is only applicable for ambiguous 

167 times) 

168 - 'NaT' will return NaT where there are ambiguous times 

169 - 'raise' will raise an AmbiguousTimeError if there are ambiguous times. 

170 dayfirst : bool, default False 

171 If True, parse dates in `data` with the day first order. 

172 yearfirst : bool, default False 

173 If True parse dates in `data` with the year first order. 

174 dtype : numpy.dtype or DatetimeTZDtype or str, default None 

175 Note that the only NumPy dtype allowed is ‘datetime64[ns]’. 

176 copy : bool, default False 

177 Make a copy of input ndarray. 

178 name : label, default None 

179 Name to be stored in the index. 

180 

181 Attributes 

182 ---------- 

183 year 

184 month 

185 day 

186 hour 

187 minute 

188 second 

189 microsecond 

190 nanosecond 

191 date 

192 time 

193 timetz 

194 dayofyear 

195 day_of_year 

196 weekofyear 

197 week 

198 dayofweek 

199 day_of_week 

200 weekday 

201 quarter 

202 tz 

203 freq 

204 freqstr 

205 is_month_start 

206 is_month_end 

207 is_quarter_start 

208 is_quarter_end 

209 is_year_start 

210 is_year_end 

211 is_leap_year 

212 inferred_freq 

213 

214 Methods 

215 ------- 

216 normalize 

217 strftime 

218 snap 

219 tz_convert 

220 tz_localize 

221 round 

222 floor 

223 ceil 

224 to_period 

225 to_pydatetime 

226 to_series 

227 to_frame 

228 month_name 

229 day_name 

230 mean 

231 std 

232 

233 See Also 

234 -------- 

235 Index : The base pandas Index type. 

236 TimedeltaIndex : Index of timedelta64 data. 

237 PeriodIndex : Index of Period data. 

238 to_datetime : Convert argument to datetime. 

239 date_range : Create a fixed-frequency DatetimeIndex. 

240 

241 Notes 

242 ----- 

243 To learn more about the frequency strings, please see `this link 

244 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__. 

245 """ 

246 

247 _typ = "datetimeindex" 

248 

249 _data_cls = DatetimeArray 

250 _supports_partial_string_indexing = True 

251 

252 @property 

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

254 return libindex.DatetimeEngine 

255 

256 _data: DatetimeArray 

257 tz: dt.tzinfo | None 

258 

259 # -------------------------------------------------------------------- 

260 # methods that dispatch to DatetimeArray and wrap result 

261 

262 @doc(DatetimeArray.strftime) 

263 def strftime(self, date_format) -> Index: 

264 arr = self._data.strftime(date_format) 

265 return Index(arr, name=self.name, dtype=object) 

266 

267 @doc(DatetimeArray.tz_convert) 

268 def tz_convert(self, tz) -> DatetimeIndex: 

269 arr = self._data.tz_convert(tz) 

270 return type(self)._simple_new(arr, name=self.name, refs=self._references) 

271 

272 @doc(DatetimeArray.tz_localize) 

273 def tz_localize( 

274 self, 

275 tz, 

276 ambiguous: TimeAmbiguous = "raise", 

277 nonexistent: TimeNonexistent = "raise", 

278 ) -> DatetimeIndex: 

279 arr = self._data.tz_localize(tz, ambiguous, nonexistent) 

280 return type(self)._simple_new(arr, name=self.name) 

281 

282 @doc(DatetimeArray.to_period) 

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

284 from pandas.core.indexes.api import PeriodIndex 

285 

286 arr = self._data.to_period(freq) 

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

288 

289 @doc(DatetimeArray.to_julian_date) 

290 def to_julian_date(self) -> Index: 

291 arr = self._data.to_julian_date() 

292 return Index._simple_new(arr, name=self.name) 

293 

294 @doc(DatetimeArray.isocalendar) 

295 def isocalendar(self) -> DataFrame: 

296 df = self._data.isocalendar() 

297 return df.set_index(self) 

298 

299 @cache_readonly 

300 def _resolution_obj(self) -> Resolution: 

301 return self._data._resolution_obj 

302 

303 # -------------------------------------------------------------------- 

304 # Constructors 

305 

306 def __new__( 

307 cls, 

308 data=None, 

309 freq: Frequency | lib.NoDefault = lib.no_default, 

310 tz=lib.no_default, 

311 normalize: bool = False, 

312 closed=None, 

313 ambiguous: TimeAmbiguous = "raise", 

314 dayfirst: bool = False, 

315 yearfirst: bool = False, 

316 dtype: Dtype | None = None, 

317 copy: bool = False, 

318 name: Hashable = None, 

319 ) -> DatetimeIndex: 

320 if is_scalar(data): 

321 cls._raise_scalar_data_error(data) 

322 

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

324 

325 name = maybe_extract_name(name, data, cls) 

326 

327 if ( 

328 isinstance(data, DatetimeArray) 

329 and freq is lib.no_default 

330 and tz is lib.no_default 

331 and dtype is None 

332 ): 

333 # fastpath, similar logic in TimedeltaIndex.__new__; 

334 # Note in this particular case we retain non-nano. 

335 if copy: 

336 data = data.copy() 

337 return cls._simple_new(data, name=name) 

338 

339 dtarr = DatetimeArray._from_sequence_not_strict( 

340 data, 

341 dtype=dtype, 

342 copy=copy, 

343 tz=tz, 

344 freq=freq, 

345 dayfirst=dayfirst, 

346 yearfirst=yearfirst, 

347 ambiguous=ambiguous, 

348 ) 

349 refs = None 

350 if not copy and isinstance(data, (Index, ABCSeries)): 

351 refs = data._references 

352 

353 subarr = cls._simple_new(dtarr, name=name, refs=refs) 

354 return subarr 

355 

356 # -------------------------------------------------------------------- 

357 

358 @cache_readonly 

359 def _is_dates_only(self) -> bool: 

360 """ 

361 Return a boolean if we are only dates (and don't have a timezone) 

362 

363 Returns 

364 ------- 

365 bool 

366 """ 

367 from pandas.io.formats.format import is_dates_only 

368 

369 # error: Argument 1 to "is_dates_only" has incompatible type 

370 # "Union[ExtensionArray, ndarray]"; expected "Union[ndarray, 

371 # DatetimeArray, Index, DatetimeIndex]" 

372 return self.tz is None and is_dates_only(self._values) # type: ignore[arg-type] 

373 

374 def __reduce__(self): 

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

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

377 

378 def _is_comparable_dtype(self, dtype: DtypeObj) -> bool: 

379 """ 

380 Can we compare values of the given dtype to our own? 

381 """ 

382 if self.tz is not None: 

383 # If we have tz, we can compare to tzaware 

384 return is_datetime64tz_dtype(dtype) 

385 # if we dont have tz, we can only compare to tznaive 

386 return is_datetime64_dtype(dtype) 

387 

388 # -------------------------------------------------------------------- 

389 # Rendering Methods 

390 

391 @property 

392 def _formatter_func(self): 

393 from pandas.io.formats.format import get_format_datetime64 

394 

395 formatter = get_format_datetime64(is_dates_only_=self._is_dates_only) 

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

397 

398 # -------------------------------------------------------------------- 

399 # Set Operation Methods 

400 

401 def _can_range_setop(self, other) -> bool: 

402 # GH 46702: If self or other have non-UTC tzs, DST transitions prevent 

403 # range representation due to no singular step 

404 if ( 

405 self.tz is not None 

406 and not timezones.is_utc(self.tz) 

407 and not timezones.is_fixed_offset(self.tz) 

408 ): 

409 return False 

410 if ( 

411 other.tz is not None 

412 and not timezones.is_utc(other.tz) 

413 and not timezones.is_fixed_offset(other.tz) 

414 ): 

415 return False 

416 return super()._can_range_setop(other) 

417 

418 # -------------------------------------------------------------------- 

419 

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

421 """ 

422 Return the number of microseconds since midnight. 

423 

424 Returns 

425 ------- 

426 ndarray[int64_t] 

427 """ 

428 values = self._data._local_timestamps() 

429 

430 ppd = periods_per_day(self._data._creso) 

431 

432 frac = values % ppd 

433 if self.unit == "ns": 

434 micros = frac // 1000 

435 elif self.unit == "us": 

436 micros = frac 

437 elif self.unit == "ms": 

438 micros = frac * 1000 

439 elif self.unit == "s": 

440 micros = frac * 1_000_000 

441 else: # pragma: no cover 

442 raise NotImplementedError(self.unit) 

443 

444 micros[self._isnan] = -1 

445 return micros 

446 

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

448 """ 

449 Snap time stamps to nearest occurring frequency. 

450 

451 Returns 

452 ------- 

453 DatetimeIndex 

454 """ 

455 # Superdumb, punting on any optimizing 

456 freq = to_offset(freq) 

457 

458 dta = self._data.copy() 

459 

460 for i, v in enumerate(self): 

461 s = v 

462 if not freq.is_on_offset(s): 

463 t0 = freq.rollback(s) 

464 t1 = freq.rollforward(s) 

465 if abs(s - t0) < abs(t1 - s): 

466 s = t0 

467 else: 

468 s = t1 

469 dta[i] = s 

470 

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

472 

473 # -------------------------------------------------------------------- 

474 # Indexing Methods 

475 

476 def _parsed_string_to_bounds(self, reso: Resolution, parsed: dt.datetime): 

477 """ 

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

479 

480 Parameters 

481 ---------- 

482 reso : Resolution 

483 Resolution provided by parsed string. 

484 parsed : datetime 

485 Datetime from parsed string. 

486 

487 Returns 

488 ------- 

489 lower, upper: pd.Timestamp 

490 """ 

491 per = Period(parsed, freq=reso.attr_abbrev) 

492 start, end = per.start_time, per.end_time 

493 

494 # GH 24076 

495 # If an incoming date string contained a UTC offset, need to localize 

496 # the parsed date to this offset first before aligning with the index's 

497 # timezone 

498 start = start.tz_localize(parsed.tzinfo) 

499 end = end.tz_localize(parsed.tzinfo) 

500 

501 if parsed.tzinfo is not None: 

502 if self.tz is None: 

503 raise ValueError( 

504 "The index must be timezone aware when indexing " 

505 "with a date string with a UTC offset" 

506 ) 

507 # The flipped case with parsed.tz is None and self.tz is not None 

508 # is ruled out bc parsed and reso are produced by _parse_with_reso, 

509 # which localizes parsed. 

510 return start, end 

511 

512 def _parse_with_reso(self, label: str): 

513 parsed, reso = super()._parse_with_reso(label) 

514 

515 parsed = Timestamp(parsed) 

516 

517 if self.tz is not None and parsed.tzinfo is None: 

518 # we special-case timezone-naive strings and timezone-aware 

519 # DatetimeIndex 

520 # https://github.com/pandas-dev/pandas/pull/36148#issuecomment-687883081 

521 parsed = parsed.tz_localize(self.tz) 

522 

523 return parsed, reso 

524 

525 def _disallow_mismatched_indexing(self, key) -> None: 

526 """ 

527 Check for mismatched-tzawareness indexing and re-raise as KeyError. 

528 """ 

529 # we get here with isinstance(key, self._data._recognized_scalars) 

530 try: 

531 # GH#36148 

532 self._data._assert_tzawareness_compat(key) 

533 except TypeError as err: 

534 raise KeyError(key) from err 

535 

536 def get_loc(self, key): 

537 """ 

538 Get integer location for requested label 

539 

540 Returns 

541 ------- 

542 loc : int 

543 """ 

544 self._check_indexing_error(key) 

545 

546 orig_key = key 

547 if is_valid_na_for_dtype(key, self.dtype): 

548 key = NaT 

549 

550 if isinstance(key, self._data._recognized_scalars): 

551 # needed to localize naive datetimes 

552 self._disallow_mismatched_indexing(key) 

553 key = Timestamp(key) 

554 

555 elif isinstance(key, str): 

556 try: 

557 parsed, reso = self._parse_with_reso(key) 

558 except (ValueError, pytz.NonExistentTimeError) as err: 

559 raise KeyError(key) from err 

560 self._disallow_mismatched_indexing(parsed) 

561 

562 if self._can_partial_date_slice(reso): 

563 try: 

564 return self._partial_date_slice(reso, parsed) 

565 except KeyError as err: 

566 raise KeyError(key) from err 

567 

568 key = parsed 

569 

570 elif isinstance(key, dt.timedelta): 

571 # GH#20464 

572 raise TypeError( 

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

574 ) 

575 

576 elif isinstance(key, dt.time): 

577 return self.indexer_at_time(key) 

578 

579 else: 

580 # unrecognized type 

581 raise KeyError(key) 

582 

583 try: 

584 return Index.get_loc(self, key) 

585 except KeyError as err: 

586 raise KeyError(orig_key) from err 

587 

588 @doc(DatetimeTimedeltaMixin._maybe_cast_slice_bound) 

589 def _maybe_cast_slice_bound(self, label, side: str): 

590 # GH#42855 handle date here instead of get_slice_bound 

591 if isinstance(label, dt.date) and not isinstance(label, dt.datetime): 

592 # Pandas supports slicing with dates, treated as datetimes at midnight. 

593 # https://github.com/pandas-dev/pandas/issues/31501 

594 label = Timestamp(label).to_pydatetime() 

595 

596 label = super()._maybe_cast_slice_bound(label, side) 

597 self._data._assert_tzawareness_compat(label) 

598 return Timestamp(label) 

599 

600 def slice_indexer(self, start=None, end=None, step=None): 

601 """ 

602 Return indexer for specified label slice. 

603 Index.slice_indexer, customized to handle time slicing. 

604 

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

606 following: 

607 

608 - if both `start` and `end` are instances of `datetime.time`, it 

609 invokes `indexer_between_time` 

610 - if `start` and `end` are both either string or None perform 

611 value-based selection in non-monotonic cases. 

612 

613 """ 

614 # For historical reasons DatetimeIndex supports slices between two 

615 # instances of datetime.time as if it were applying a slice mask to 

616 # an array of (self.hour, self.minute, self.seconds, self.microsecond). 

617 if isinstance(start, dt.time) and isinstance(end, dt.time): 

618 if step is not None and step != 1: 

619 raise ValueError("Must have step size of 1 with time slices") 

620 return self.indexer_between_time(start, end) 

621 

622 if isinstance(start, dt.time) or isinstance(end, dt.time): 

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

624 

625 def check_str_or_none(point) -> bool: 

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

627 

628 # GH#33146 if start and end are combinations of str and None and Index is not 

629 # monotonic, we can not use Index.slice_indexer because it does not honor the 

630 # actual elements, is only searching for start and end 

631 if ( 

632 check_str_or_none(start) 

633 or check_str_or_none(end) 

634 or self.is_monotonic_increasing 

635 ): 

636 return Index.slice_indexer(self, start, end, step) 

637 

638 mask = np.array(True) 

639 raise_mask = np.array(True) 

640 if start is not None: 

641 start_casted = self._maybe_cast_slice_bound(start, "left") 

642 mask = start_casted <= self 

643 raise_mask = start_casted == self 

644 

645 if end is not None: 

646 end_casted = self._maybe_cast_slice_bound(end, "right") 

647 mask = (self <= end_casted) & mask 

648 raise_mask = (end_casted == self) | raise_mask 

649 

650 if not raise_mask.any(): 

651 raise KeyError( 

652 "Value based partial slicing on non-monotonic DatetimeIndexes " 

653 "with non-existing keys is not allowed.", 

654 ) 

655 indexer = mask.nonzero()[0][::step] 

656 if len(indexer) == len(self): 

657 return slice(None) 

658 else: 

659 return indexer 

660 

661 # -------------------------------------------------------------------- 

662 

663 @property 

664 def inferred_type(self) -> str: 

665 # b/c datetime is represented as microseconds since the epoch, make 

666 # sure we can't have ambiguous indexing 

667 return "datetime64" 

668 

669 def indexer_at_time(self, time, asof: bool = False) -> npt.NDArray[np.intp]: 

670 """ 

671 Return index locations of values at particular time of day. 

672 

673 Parameters 

674 ---------- 

675 time : datetime.time or str 

676 Time passed in either as object (datetime.time) or as string in 

677 appropriate format ("%H:%M", "%H%M", "%I:%M%p", "%I%M%p", 

678 "%H:%M:%S", "%H%M%S", "%I:%M:%S%p", "%I%M%S%p"). 

679 

680 Returns 

681 ------- 

682 np.ndarray[np.intp] 

683 

684 See Also 

685 -------- 

686 indexer_between_time : Get index locations of values between particular 

687 times of day. 

688 DataFrame.at_time : Select values at particular time of day. 

689 """ 

690 if asof: 

691 raise NotImplementedError("'asof' argument is not supported") 

692 

693 if isinstance(time, str): 

694 from dateutil.parser import parse 

695 

696 time = parse(time).time() 

697 

698 if time.tzinfo: 

699 if self.tz is None: 

700 raise ValueError("Index must be timezone aware.") 

701 time_micros = self.tz_convert(time.tzinfo)._get_time_micros() 

702 else: 

703 time_micros = self._get_time_micros() 

704 micros = _time_to_micros(time) 

705 return (time_micros == micros).nonzero()[0] 

706 

707 def indexer_between_time( 

708 self, start_time, end_time, include_start: bool = True, include_end: bool = True 

709 ) -> npt.NDArray[np.intp]: 

710 """ 

711 Return index locations of values between particular times of day. 

712 

713 Parameters 

714 ---------- 

715 start_time, end_time : datetime.time, str 

716 Time passed either as object (datetime.time) or as string in 

717 appropriate format ("%H:%M", "%H%M", "%I:%M%p", "%I%M%p", 

718 "%H:%M:%S", "%H%M%S", "%I:%M:%S%p","%I%M%S%p"). 

719 include_start : bool, default True 

720 include_end : bool, default True 

721 

722 Returns 

723 ------- 

724 np.ndarray[np.intp] 

725 

726 See Also 

727 -------- 

728 indexer_at_time : Get index locations of values at particular time of day. 

729 DataFrame.between_time : Select values between particular times of day. 

730 """ 

731 start_time = to_time(start_time) 

732 end_time = to_time(end_time) 

733 time_micros = self._get_time_micros() 

734 start_micros = _time_to_micros(start_time) 

735 end_micros = _time_to_micros(end_time) 

736 

737 if include_start and include_end: 

738 lop = rop = operator.le 

739 elif include_start: 

740 lop = operator.le 

741 rop = operator.lt 

742 elif include_end: 

743 lop = operator.lt 

744 rop = operator.le 

745 else: 

746 lop = rop = operator.lt 

747 

748 if start_time <= end_time: 

749 join_op = operator.and_ 

750 else: 

751 join_op = operator.or_ 

752 

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

754 

755 return mask.nonzero()[0] 

756 

757 

758def date_range( 

759 start=None, 

760 end=None, 

761 periods=None, 

762 freq=None, 

763 tz=None, 

764 normalize: bool = False, 

765 name: Hashable = None, 

766 inclusive: IntervalClosedType = "both", 

767 *, 

768 unit: str | None = None, 

769 **kwargs, 

770) -> DatetimeIndex: 

771 """ 

772 Return a fixed frequency DatetimeIndex. 

773 

774 Returns the range of equally spaced time points (where the difference between any 

775 two adjacent points is specified by the given frequency) such that they all 

776 satisfy `start <[=] x <[=] end`, where the first one and the last one are, resp., 

777 the first and last time points in that range that fall on the boundary of ``freq`` 

778 (if given as a frequency string) or that are valid for ``freq`` (if given as a 

779 :class:`pandas.tseries.offsets.DateOffset`). (If exactly one of ``start``, 

780 ``end``, or ``freq`` is *not* specified, this missing parameter can be computed 

781 given ``periods``, the number of timesteps in the range. See the note below.) 

782 

783 Parameters 

784 ---------- 

785 start : str or datetime-like, optional 

786 Left bound for generating dates. 

787 end : str or datetime-like, optional 

788 Right bound for generating dates. 

789 periods : int, optional 

790 Number of periods to generate. 

791 freq : str, datetime.timedelta, or DateOffset, default 'D' 

792 Frequency strings can have multiples, e.g. '5H'. See 

793 :ref:`here <timeseries.offset_aliases>` for a list of 

794 frequency aliases. 

795 tz : str or tzinfo, optional 

796 Time zone name for returning localized DatetimeIndex, for example 

797 'Asia/Hong_Kong'. By default, the resulting DatetimeIndex is 

798 timezone-naive unless timezone-aware datetime-likes are passed. 

799 normalize : bool, default False 

800 Normalize start/end dates to midnight before generating date range. 

801 name : str, default None 

802 Name of the resulting DatetimeIndex. 

803 inclusive : {"both", "neither", "left", "right"}, default "both" 

804 Include boundaries; Whether to set each bound as closed or open. 

805 

806 .. versionadded:: 1.4.0 

807 unit : str, default None 

808 Specify the desired resolution of the result. 

809 

810 .. versionadded:: 2.0.0 

811 **kwargs 

812 For compatibility. Has no effect on the result. 

813 

814 Returns 

815 ------- 

816 DatetimeIndex 

817 

818 See Also 

819 -------- 

820 DatetimeIndex : An immutable container for datetimes. 

821 timedelta_range : Return a fixed frequency TimedeltaIndex. 

822 period_range : Return a fixed frequency PeriodIndex. 

823 interval_range : Return a fixed frequency IntervalIndex. 

824 

825 Notes 

826 ----- 

827 Of the four parameters ``start``, ``end``, ``periods``, and ``freq``, 

828 exactly three must be specified. If ``freq`` is omitted, the resulting 

829 ``DatetimeIndex`` will have ``periods`` linearly spaced elements between 

830 ``start`` and ``end`` (closed on both sides). 

831 

832 To learn more about the frequency strings, please see `this link 

833 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__. 

834 

835 Examples 

836 -------- 

837 **Specifying the values** 

838 

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

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

841 

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

843 

844 >>> pd.date_range(start='1/1/2018', end='1/08/2018') 

845 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', 

846 '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], 

847 dtype='datetime64[ns]', freq='D') 

848 

849 Specify timezone-aware `start` and `end`, with the default daily frequency. 

850 

851 >>> pd.date_range( 

852 ... start=pd.to_datetime("1/1/2018").tz_localize("Europe/Berlin"), 

853 ... end=pd.to_datetime("1/08/2018").tz_localize("Europe/Berlin"), 

854 ... ) 

855 DatetimeIndex(['2018-01-01 00:00:00+01:00', '2018-01-02 00:00:00+01:00', 

856 '2018-01-03 00:00:00+01:00', '2018-01-04 00:00:00+01:00', 

857 '2018-01-05 00:00:00+01:00', '2018-01-06 00:00:00+01:00', 

858 '2018-01-07 00:00:00+01:00', '2018-01-08 00:00:00+01:00'], 

859 dtype='datetime64[ns, Europe/Berlin]', freq='D') 

860 

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

862 

863 >>> pd.date_range(start='1/1/2018', periods=8) 

864 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', 

865 '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], 

866 dtype='datetime64[ns]', freq='D') 

867 

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

869 

870 >>> pd.date_range(end='1/1/2018', periods=8) 

871 DatetimeIndex(['2017-12-25', '2017-12-26', '2017-12-27', '2017-12-28', 

872 '2017-12-29', '2017-12-30', '2017-12-31', '2018-01-01'], 

873 dtype='datetime64[ns]', freq='D') 

874 

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

876 automatically (linearly spaced). 

877 

878 >>> pd.date_range(start='2018-04-24', end='2018-04-27', periods=3) 

879 DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00', 

880 '2018-04-27 00:00:00'], 

881 dtype='datetime64[ns]', freq=None) 

882 

883 **Other Parameters** 

884 

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

886 

887 >>> pd.date_range(start='1/1/2018', periods=5, freq='M') 

888 DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30', 

889 '2018-05-31'], 

890 dtype='datetime64[ns]', freq='M') 

891 

892 Multiples are allowed 

893 

894 >>> pd.date_range(start='1/1/2018', periods=5, freq='3M') 

895 DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', 

896 '2019-01-31'], 

897 dtype='datetime64[ns]', freq='3M') 

898 

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

900 

901 >>> pd.date_range(start='1/1/2018', periods=5, freq=pd.offsets.MonthEnd(3)) 

902 DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', 

903 '2019-01-31'], 

904 dtype='datetime64[ns]', freq='3M') 

905 

906 Specify `tz` to set the timezone. 

907 

908 >>> pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo') 

909 DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00', 

910 '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00', 

911 '2018-01-05 00:00:00+09:00'], 

912 dtype='datetime64[ns, Asia/Tokyo]', freq='D') 

913 

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

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

916 

917 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive="both") 

918 DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04'], 

919 dtype='datetime64[ns]', freq='D') 

920 

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

922 

923 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive='left') 

924 DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03'], 

925 dtype='datetime64[ns]', freq='D') 

926 

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

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

929 

930 >>> pd.date_range(start='2017-01-01', end='2017-01-04', inclusive='right') 

931 DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'], 

932 dtype='datetime64[ns]', freq='D') 

933 

934 **Specify a unit** 

935 

936 >>> pd.date_range(start="2017-01-01", periods=10, freq="100AS", unit="s") 

937 DatetimeIndex(['2017-01-01', '2117-01-01', '2217-01-01', '2317-01-01', 

938 '2417-01-01', '2517-01-01', '2617-01-01', '2717-01-01', 

939 '2817-01-01', '2917-01-01'], 

940 dtype='datetime64[s]', freq='100AS-JAN') 

941 """ 

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

943 freq = "D" 

944 

945 dtarr = DatetimeArray._generate_range( 

946 start=start, 

947 end=end, 

948 periods=periods, 

949 freq=freq, 

950 tz=tz, 

951 normalize=normalize, 

952 inclusive=inclusive, 

953 unit=unit, 

954 **kwargs, 

955 ) 

956 return DatetimeIndex._simple_new(dtarr, name=name) 

957 

958 

959def bdate_range( 

960 start=None, 

961 end=None, 

962 periods: int | None = None, 

963 freq: Frequency = "B", 

964 tz=None, 

965 normalize: bool = True, 

966 name: Hashable = None, 

967 weekmask=None, 

968 holidays=None, 

969 inclusive: IntervalClosedType = "both", 

970 **kwargs, 

971) -> DatetimeIndex: 

972 """ 

973 Return a fixed frequency DatetimeIndex with business day as the default. 

974 

975 Parameters 

976 ---------- 

977 start : str or datetime-like, default None 

978 Left bound for generating dates. 

979 end : str or datetime-like, default None 

980 Right bound for generating dates. 

981 periods : int, default None 

982 Number of periods to generate. 

983 freq : str, Timedelta, datetime.timedelta, or DateOffset, default 'B' 

984 Frequency strings can have multiples, e.g. '5H'. The default is 

985 business daily ('B'). 

986 tz : str or None 

987 Time zone name for returning localized DatetimeIndex, for example 

988 Asia/Beijing. 

989 normalize : bool, default False 

990 Normalize start/end dates to midnight before generating date range. 

991 name : str, default None 

992 Name of the resulting DatetimeIndex. 

993 weekmask : str or None, default None 

994 Weekmask of valid business days, passed to ``numpy.busdaycalendar``, 

995 only used when custom frequency strings are passed. The default 

996 value None is equivalent to 'Mon Tue Wed Thu Fri'. 

997 holidays : list-like or None, default None 

998 Dates to exclude from the set of valid business days, passed to 

999 ``numpy.busdaycalendar``, only used when custom frequency strings 

1000 are passed. 

1001 inclusive : {"both", "neither", "left", "right"}, default "both" 

1002 Include boundaries; Whether to set each bound as closed or open. 

1003 

1004 .. versionadded:: 1.4.0 

1005 **kwargs 

1006 For compatibility. Has no effect on the result. 

1007 

1008 Returns 

1009 ------- 

1010 DatetimeIndex 

1011 

1012 Notes 

1013 ----- 

1014 Of the four parameters: ``start``, ``end``, ``periods``, and ``freq``, 

1015 exactly three must be specified. Specifying ``freq`` is a requirement 

1016 for ``bdate_range``. Use ``date_range`` if specifying ``freq`` is not 

1017 desired. 

1018 

1019 To learn more about the frequency strings, please see `this link 

1020 <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`__. 

1021 

1022 Examples 

1023 -------- 

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

1025 

1026 >>> pd.bdate_range(start='1/1/2018', end='1/08/2018') 

1027 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', 

1028 '2018-01-05', '2018-01-08'], 

1029 dtype='datetime64[ns]', freq='B') 

1030 """ 

1031 if freq is None: 

1032 msg = "freq must be specified for bdate_range; use date_range instead" 

1033 raise TypeError(msg) 

1034 

1035 if isinstance(freq, str) and freq.startswith("C"): 

1036 try: 

1037 weekmask = weekmask or "Mon Tue Wed Thu Fri" 

1038 freq = prefix_mapping[freq](holidays=holidays, weekmask=weekmask) 

1039 except (KeyError, TypeError) as err: 

1040 msg = f"invalid custom frequency string: {freq}" 

1041 raise ValueError(msg) from err 

1042 elif holidays or weekmask: 

1043 msg = ( 

1044 "a custom frequency string is required when holidays or " 

1045 f"weekmask are passed, got frequency {freq}" 

1046 ) 

1047 raise ValueError(msg) 

1048 

1049 return date_range( 

1050 start=start, 

1051 end=end, 

1052 periods=periods, 

1053 freq=freq, 

1054 tz=tz, 

1055 normalize=normalize, 

1056 name=name, 

1057 inclusive=inclusive, 

1058 **kwargs, 

1059 ) 

1060 

1061 

1062def _time_to_micros(time_obj: dt.time) -> int: 

1063 seconds = time_obj.hour * 60 * 60 + 60 * time_obj.minute + time_obj.second 

1064 return 1_000_000 * seconds + time_obj.microsecond