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

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

350 statements  

1from __future__ import annotations 

2 

3import datetime as dt 

4import operator 

5from typing import ( 

6 TYPE_CHECKING, 

7 Self, 

8) 

9import warnings 

10 

11import numpy as np 

12 

13from pandas._libs import ( 

14 NaT, 

15 Period, 

16 Timestamp, 

17 index as libindex, 

18 lib, 

19) 

20from pandas._libs.tslibs import ( 

21 Resolution, 

22 Tick, 

23 Timedelta, 

24 periods_per_day, 

25 timezones, 

26 to_offset, 

27) 

28from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit 

29from pandas._libs.tslibs.offsets import ( 

30 DateOffset, 

31 prefix_mapping, 

32) 

33from pandas.errors import Pandas4Warning 

34from pandas.util._decorators import ( 

35 cache_readonly, 

36 set_module, 

37) 

38from pandas.util._exceptions import find_stack_level 

39 

40from pandas.core.dtypes.common import is_scalar 

41from pandas.core.dtypes.dtypes import ( 

42 ArrowDtype, 

43 DatetimeTZDtype, 

44) 

45from pandas.core.dtypes.generic import ABCSeries 

46from pandas.core.dtypes.missing import is_valid_na_for_dtype 

47 

48from pandas.core.arrays.datetimes import ( 

49 DatetimeArray, 

50 tz_to_dtype, 

51) 

52import pandas.core.common as com 

53from pandas.core.indexes.base import ( 

54 Index, 

55 maybe_extract_name, 

56) 

57from pandas.core.indexes.datetimelike import DatetimeTimedeltaMixin 

58from pandas.core.indexes.extension import inherit_names 

59from pandas.core.tools.times import to_time 

60 

61if TYPE_CHECKING: 

62 from collections.abc import Hashable 

63 

64 from pandas._typing import ( 

65 Dtype, 

66 DtypeObj, 

67 Frequency, 

68 IntervalClosedType, 

69 TimeAmbiguous, 

70 TimeNonexistent, 

71 npt, 

72 TimeUnit, 

73 ) 

74 

75 from pandas.core.api import ( 

76 DataFrame, 

77 PeriodIndex, 

78 ) 

79 

80from pandas._libs.tslibs.dtypes import OFFSET_TO_PERIOD_FREQSTR 

81 

82 

83def _new_DatetimeIndex(cls, d): 

84 """ 

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

86 have arguments and breaks __new__ 

87 """ 

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

89 # Avoid need to verify integrity by calling simple_new directly 

90 data = d.pop("data") 

91 if not isinstance(data, DatetimeArray): 

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

93 # a DatetimeArray to adapt to the newer _simple_new signature 

94 tz = d.pop("tz") 

95 freq = d.pop("freq") 

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

97 else: 

98 dta = data 

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

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

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

102 if key in d: 

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

104 d.pop(key) 

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

106 else: 

107 with warnings.catch_warnings(): 

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

109 # go through _simple_new instead 

110 warnings.simplefilter("ignore") 

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

112 

113 return result 

114 

115 

116@inherit_names( 

117 DatetimeArray._field_ops 

118 + [ 

119 method 

120 for method in DatetimeArray._datetimelike_methods 

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

122 ], 

123 DatetimeArray, 

124 wrap=True, 

125) 

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

127@inherit_names( 

128 [ 

129 "tz", 

130 "tzinfo", 

131 "dtype", 

132 "to_pydatetime", 

133 "date", 

134 "time", 

135 "timetz", 

136 "std", 

137 *DatetimeArray._bool_ops, 

138 ], 

139 DatetimeArray, 

140) 

141@set_module("pandas") 

142class DatetimeIndex(DatetimeTimedeltaMixin): 

143 """ 

144 Immutable ndarray-like of datetime64 data. 

145 

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

147 that are subclasses of datetime and carry metadata. 

148 

149 .. versionchanged:: 2.0.0 

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

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

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

153 

154 Parameters 

155 ---------- 

156 data : array-like (1-dimensional) 

157 Datetime-like data to construct index with. 

158 freq : str or pandas offset object, optional 

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

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

161 inferred frequency upon creation. 

162 tz : zoneinfo.ZoneInfo, pytz.timezone, dateutil.tz.tzfile, datetime.tzinfo or str 

163 Set the Timezone of the data. 

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

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

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

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

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

169 dictates how ambiguous times should be handled. 

170 

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

172 order 

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

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

175 times) 

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

177 - 'raise' will raise a ValueError if there are ambiguous times. 

178 dayfirst : bool, default False 

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

180 yearfirst : bool, default False 

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

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

183 Note that the only NumPy dtype allowed is `datetime64[ns]`. 

184 copy : bool, default None 

185 Whether to copy input data, only relevant for array, Series, and Index 

186 inputs (for other input, e.g. a list, a new array is created anyway). 

187 Defaults to True for array input and False for Index/Series. 

188 Set to False to avoid copying array input at your own risk (if you 

189 know the input data won't be modified elsewhere). 

190 Set to True to force copying Series/Index up front. 

191 name : label, default None 

192 Name to be stored in the index. 

193 

194 Attributes 

195 ---------- 

196 year 

197 month 

198 day 

199 hour 

200 minute 

201 second 

202 microsecond 

203 nanosecond 

204 date 

205 time 

206 timetz 

207 dayofyear 

208 day_of_year 

209 dayofweek 

210 day_of_week 

211 weekday 

212 quarter 

213 tz 

214 freq 

215 freqstr 

216 is_month_start 

217 is_month_end 

218 is_quarter_start 

219 is_quarter_end 

220 is_year_start 

221 is_year_end 

222 is_leap_year 

223 inferred_freq 

224 

225 Methods 

226 ------- 

227 normalize 

228 strftime 

229 snap 

230 tz_convert 

231 tz_localize 

232 round 

233 floor 

234 ceil 

235 to_period 

236 to_pydatetime 

237 to_series 

238 to_frame 

239 to_julian_date 

240 month_name 

241 day_name 

242 mean 

243 std 

244 

245 See Also 

246 -------- 

247 Index : The base pandas Index type. 

248 TimedeltaIndex : Index of timedelta64 data. 

249 PeriodIndex : Index of Period data. 

250 to_datetime : Convert argument to datetime. 

251 date_range : Create a fixed-frequency DatetimeIndex. 

252 

253 Notes 

254 ----- 

255 To learn more about the frequency strings, please see 

256 :ref:`this link<timeseries.offset_aliases>`. 

257 

258 Examples 

259 -------- 

260 >>> idx = pd.DatetimeIndex(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

261 >>> idx 

262 DatetimeIndex(['2020-01-01 10:00:00+00:00', '2020-02-01 11:00:00+00:00'], 

263 dtype='datetime64[us, UTC]', freq=None) 

264 """ 

265 

266 _typ = "datetimeindex" 

267 

268 _data_cls = DatetimeArray 

269 _supports_partial_string_indexing = True 

270 

271 @property 

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

273 return libindex.DatetimeEngine 

274 

275 _data: DatetimeArray 

276 _values: DatetimeArray 

277 tz: dt.tzinfo | None 

278 

279 # -------------------------------------------------------------------- 

280 # methods that dispatch to DatetimeArray and wrap result 

281 

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

283 """ 

284 Convert to Index using specified date_format. 

285 

286 Return an Index of formatted strings specified by date_format, which 

287 supports the same string format as the python standard library. Details 

288 of the string format can be found in `python string format 

289 doc <https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior>`__. 

290 

291 Formats supported by the C `strftime` API but not by the python string format 

292 doc (such as `"%R"`, `"%r"`) are not officially supported and should be 

293 preferably replaced with their supported equivalents (such as `"%H:%M"`, 

294 `"%I:%M:%S %p"`). 

295 Note that `PeriodIndex` support additional directives, detailed in 

296 `Period.strftime`. 

297 

298 Parameters 

299 ---------- 

300 date_format : str 

301 Date format string (e.g. "%Y-%m-%d"). 

302 

303 Returns 

304 ------- 

305 ndarray[object] 

306 NumPy ndarray of formatted strings. 

307 

308 See Also 

309 -------- 

310 to_datetime : Convert the given argument to datetime. 

311 DatetimeIndex.normalize : Return DatetimeIndex with times to midnight. 

312 DatetimeIndex.round : Round the DatetimeIndex to the specified freq. 

313 DatetimeIndex.floor : Floor the DatetimeIndex to the specified freq. 

314 Timestamp.strftime : Format a single Timestamp. 

315 Period.strftime : Format a single Period. 

316 

317 Examples 

318 -------- 

319 >>> rng = pd.date_range(pd.Timestamp("2018-03-10 09:00"), periods=3, freq="s") 

320 >>> rng.strftime("%B %d, %Y, %r") 

321 Index(['March 10, 2018, 09:00:00 AM', 'March 10, 2018, 09:00:01 AM', 

322 'March 10, 2018, 09:00:02 AM'], 

323 dtype='str') 

324 """ 

325 arr = self._data.strftime(date_format) 

326 return Index(arr, name=self.name, dtype=arr.dtype, copy=False) 

327 

328 def tz_convert(self, tz) -> Self: 

329 """ 

330 Convert tz-aware Datetime Array/Index from one time zone to another. 

331 

332 Parameters 

333 ---------- 

334 tz : str, zoneinfo.ZoneInfo, pytz.timezone, dateutil.tz.tzfile, datetime.tzinfo or None 

335 Time zone for time. Corresponding timestamps would be converted 

336 to this time zone of the Datetime Array/Index. A `tz` of None will 

337 convert to UTC and remove the timezone information. 

338 

339 Returns 

340 ------- 

341 Array or Index 

342 Datetme Array/Index with target `tz`. 

343 

344 Raises 

345 ------ 

346 TypeError 

347 If Datetime Array/Index is tz-naive. 

348 

349 See Also 

350 -------- 

351 DatetimeIndex.tz : A timezone that has a variable offset from UTC. 

352 DatetimeIndex.tz_localize : Localize tz-naive DatetimeIndex to a 

353 given time zone, or remove timezone from a tz-aware DatetimeIndex. 

354 

355 Examples 

356 -------- 

357 With the `tz` parameter, we can change the DatetimeIndex 

358 to other time zones: 

359 

360 >>> dti = pd.date_range( 

361 ... start="2014-08-01 09:00", freq="h", periods=3, tz="Europe/Berlin" 

362 ... ) 

363 

364 >>> dti 

365 DatetimeIndex(['2014-08-01 09:00:00+02:00', 

366 '2014-08-01 10:00:00+02:00', 

367 '2014-08-01 11:00:00+02:00'], 

368 dtype='datetime64[us, Europe/Berlin]', freq='h') 

369 

370 >>> dti.tz_convert("US/Central") 

371 DatetimeIndex(['2014-08-01 02:00:00-05:00', 

372 '2014-08-01 03:00:00-05:00', 

373 '2014-08-01 04:00:00-05:00'], 

374 dtype='datetime64[us, US/Central]', freq='h') 

375 

376 With the ``tz=None``, we can remove the timezone (after converting 

377 to UTC if necessary): 

378 

379 >>> dti = pd.date_range( 

380 ... start="2014-08-01 09:00", freq="h", periods=3, tz="Europe/Berlin" 

381 ... ) 

382 

383 >>> dti 

384 DatetimeIndex(['2014-08-01 09:00:00+02:00', 

385 '2014-08-01 10:00:00+02:00', 

386 '2014-08-01 11:00:00+02:00'], 

387 dtype='datetime64[us, Europe/Berlin]', freq='h') 

388 

389 >>> dti.tz_convert(None) 

390 DatetimeIndex(['2014-08-01 07:00:00', 

391 '2014-08-01 08:00:00', 

392 '2014-08-01 09:00:00'], 

393 dtype='datetime64[us]', freq='h') 

394 """ # noqa: E501 

395 arr = self._data.tz_convert(tz) 

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

397 

398 def tz_localize( 

399 self, 

400 tz, 

401 ambiguous: TimeAmbiguous = "raise", 

402 nonexistent: TimeNonexistent = "raise", 

403 ) -> Self: 

404 """ 

405 Localize tz-naive Datetime Array/Index to tz-aware Datetime Array/Index. 

406 

407 This method takes a time zone (tz) naive Datetime Array/Index object 

408 and makes this time zone aware. It does not move the time to another 

409 time zone. 

410 

411 This method can also be used to do the inverse -- to create a time 

412 zone unaware object from an aware object. To that end, pass `tz=None`. 

413 

414 Parameters 

415 ---------- 

416 tz : str, zoneinfo.ZoneInfo,, pytz.timezone, dateutil.tz.tzfile, datetime.tzinfo or None 

417 Time zone to convert timestamps to. Passing ``None`` will 

418 remove the time zone information preserving local time. 

419 ambiguous : 'infer', 'NaT', bool array, default 'raise' 

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

421 For example in Central European Time (UTC+01), when going from 

422 03:00 DST to 02:00 non-DST, 02:30:00 local time occurs both at 

423 00:30:00 UTC and at 01:30:00 UTC. In such a situation, the 

424 `ambiguous` parameter dictates how ambiguous times should be 

425 handled. 

426 

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

428 order 

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

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

431 ambiguous times) 

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

433 - 'raise' will raise a ValueError if there are ambiguous 

434 times. 

435 

436 nonexistent : 'shift_forward', 'shift_backward, 'NaT', timedelta, \ 

437 default 'raise' 

438 A nonexistent time does not exist in a particular timezone 

439 where clocks moved forward due to DST. 

440 

441 - 'shift_forward' will shift the nonexistent time forward to the 

442 closest existing time 

443 - 'shift_backward' will shift the nonexistent time backward to the 

444 closest existing time 

445 - 'NaT' will return NaT where there are nonexistent times 

446 - timedelta objects will shift nonexistent times by the timedelta 

447 - 'raise' will raise a ValueError if there are 

448 nonexistent times. 

449 

450 Returns 

451 ------- 

452 Same type as self 

453 Array/Index converted to the specified time zone. 

454 

455 Raises 

456 ------ 

457 TypeError 

458 If the Datetime Array/Index is tz-aware and tz is not None. 

459 

460 See Also 

461 -------- 

462 DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from 

463 one time zone to another. 

464 

465 Examples 

466 -------- 

467 >>> tz_naive = pd.date_range('2018-03-01 09:00', periods=3) 

468 >>> tz_naive 

469 DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00', 

470 '2018-03-03 09:00:00'], 

471 dtype='datetime64[us]', freq='D') 

472 

473 Localize DatetimeIndex in US/Eastern time zone: 

474 

475 >>> tz_aware = tz_naive.tz_localize(tz='US/Eastern') 

476 >>> tz_aware 

477 DatetimeIndex(['2018-03-01 09:00:00-05:00', 

478 '2018-03-02 09:00:00-05:00', 

479 '2018-03-03 09:00:00-05:00'], 

480 dtype='datetime64[us, US/Eastern]', freq=None) 

481 

482 With the ``tz=None``, we can remove the time zone information 

483 while keeping the local time (not converted to UTC): 

484 

485 >>> tz_aware.tz_localize(None) 

486 DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00', 

487 '2018-03-03 09:00:00'], 

488 dtype='datetime64[us]', freq=None) 

489 

490 Be careful with DST changes. When there is sequential data, pandas can 

491 infer the DST time: 

492 

493 >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:30:00', 

494 ... '2018-10-28 02:00:00', 

495 ... '2018-10-28 02:30:00', 

496 ... '2018-10-28 02:00:00', 

497 ... '2018-10-28 02:30:00', 

498 ... '2018-10-28 03:00:00', 

499 ... '2018-10-28 03:30:00'])) 

500 >>> s.dt.tz_localize('CET', ambiguous='infer') 

501 0 2018-10-28 01:30:00+02:00 

502 1 2018-10-28 02:00:00+02:00 

503 2 2018-10-28 02:30:00+02:00 

504 3 2018-10-28 02:00:00+01:00 

505 4 2018-10-28 02:30:00+01:00 

506 5 2018-10-28 03:00:00+01:00 

507 6 2018-10-28 03:30:00+01:00 

508 dtype: datetime64[us, CET] 

509 

510 In some cases, inferring the DST is impossible. In such cases, you can 

511 pass an ndarray to the ambiguous parameter to set the DST explicitly 

512 

513 >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:20:00', 

514 ... '2018-10-28 02:36:00', 

515 ... '2018-10-28 03:46:00'])) 

516 >>> s.dt.tz_localize('CET', ambiguous=np.array([True, True, False])) 

517 0 2018-10-28 01:20:00+02:00 

518 1 2018-10-28 02:36:00+02:00 

519 2 2018-10-28 03:46:00+01:00 

520 dtype: datetime64[us, CET] 

521 

522 If the DST transition causes nonexistent times, you can shift these 

523 dates forward or backwards with a timedelta object or `'shift_forward'` 

524 or `'shift_backwards'`. 

525 

526 >>> s = pd.to_datetime(pd.Series(['2015-03-29 02:30:00', 

527 ... '2015-03-29 03:30:00'], dtype="M8[ns]")) 

528 >>> s.dt.tz_localize('Europe/Warsaw', nonexistent='shift_forward') 

529 0 2015-03-29 03:00:00+02:00 

530 1 2015-03-29 03:30:00+02:00 

531 dtype: datetime64[ns, Europe/Warsaw] 

532 

533 >>> s.dt.tz_localize('Europe/Warsaw', nonexistent='shift_backward') 

534 0 2015-03-29 01:59:59.999999999+01:00 

535 1 2015-03-29 03:30:00+02:00 

536 dtype: datetime64[ns, Europe/Warsaw] 

537 

538 >>> s.dt.tz_localize('Europe/Warsaw', nonexistent=pd.Timedelta('1h')) 

539 0 2015-03-29 03:30:00+02:00 

540 1 2015-03-29 03:30:00+02:00 

541 dtype: datetime64[ns, Europe/Warsaw] 

542 """ # noqa: E501 

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

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

545 

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

547 """ 

548 Cast to PeriodArray/PeriodIndex at a particular frequency. 

549 

550 Converts DatetimeArray/Index to PeriodArray/PeriodIndex. 

551 

552 Parameters 

553 ---------- 

554 freq : str or Period, optional 

555 One of pandas' :ref:`period aliases <timeseries.period_aliases>` 

556 or a Period object. Will be inferred by default. 

557 

558 Returns 

559 ------- 

560 PeriodArray/PeriodIndex 

561 Immutable ndarray holding ordinal values at a particular frequency. 

562 

563 Raises 

564 ------ 

565 ValueError 

566 When converting a DatetimeArray/Index with non-regular values, 

567 so that a frequency cannot be inferred. 

568 

569 See Also 

570 -------- 

571 PeriodIndex: Immutable ndarray holding ordinal values. 

572 DatetimeIndex.to_pydatetime: Return DatetimeIndex as object. 

573 

574 Examples 

575 -------- 

576 >>> df = pd.DataFrame( 

577 ... {"y": [1, 2, 3]}, 

578 ... index=pd.to_datetime( 

579 ... [ 

580 ... "2000-03-31 00:00:00", 

581 ... "2000-05-31 00:00:00", 

582 ... "2000-08-31 00:00:00", 

583 ... ] 

584 ... ), 

585 ... ) 

586 >>> df.index.to_period("M") 

587 PeriodIndex(['2000-03', '2000-05', '2000-08'], 

588 dtype='period[M]') 

589 

590 Infer the daily frequency 

591 

592 >>> idx = pd.date_range("2017-01-01", periods=2) 

593 >>> idx.to_period() 

594 PeriodIndex(['2017-01-01', '2017-01-02'], 

595 dtype='period[D]') 

596 """ 

597 from pandas.core.indexes.api import PeriodIndex 

598 

599 arr = self._data.to_period(freq) 

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

601 

602 def to_julian_date(self) -> Index: 

603 """ 

604 Convert TimeStamp to a Julian Date. 

605 

606 This method returns the number of days as a float since noon January 1, 4713 BC. 

607 

608 https://en.wikipedia.org/wiki/Julian_day 

609 

610 Returns 

611 ------- 

612 ndarray or Index 

613 Float values that represent each date in Julian Calendar. 

614 

615 See Also 

616 -------- 

617 Timestamp.to_julian_date : Equivalent method on ``Timestamp`` objects. 

618 

619 Examples 

620 -------- 

621 >>> idx = pd.DatetimeIndex(["2028-08-12 00:54", "2028-08-12 02:06"]) 

622 >>> idx.to_julian_date() 

623 Index([2461995.5375, 2461995.5875], dtype='float64') 

624 """ 

625 arr = self._data.to_julian_date() 

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

627 

628 def isocalendar(self) -> DataFrame: 

629 """ 

630 Calculate year, week, and day according to the ISO 8601 standard. 

631 Returns 

632 ------- 

633 DataFrame 

634 With columns year, week and day. 

635 See Also 

636 -------- 

637 Timestamp.isocalendar : Function return a 3-tuple containing ISO year, 

638 week number, and weekday for the given Timestamp object. 

639 datetime.date.isocalendar : Return a named tuple object with 

640 three components: year, week and weekday. 

641 

642 Examples 

643 -------- 

644 >>> idx = pd.date_range(start="2019-12-29", freq="D", periods=4) 

645 >>> idx.isocalendar() 

646 year week day 

647 2019-12-29 2019 52 7 

648 2019-12-30 2020 1 1 

649 2019-12-31 2020 1 2 

650 2020-01-01 2020 1 3 

651 >>> idx.isocalendar().week 

652 2019-12-29 52 

653 2019-12-30 1 

654 2019-12-31 1 

655 2020-01-01 1 

656 Freq: D, Name: week, dtype: UInt32 

657 """ 

658 df = self._data.isocalendar() 

659 return df.set_index(self) 

660 

661 @cache_readonly 

662 def _resolution_obj(self) -> Resolution: 

663 return self._data._resolution_obj 

664 

665 # -------------------------------------------------------------------- 

666 # Constructors 

667 

668 def __new__( 

669 cls, 

670 data=None, 

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

672 tz=lib.no_default, 

673 ambiguous: TimeAmbiguous = "raise", 

674 dayfirst: bool = False, 

675 yearfirst: bool = False, 

676 dtype: Dtype | None = None, 

677 copy: bool | None = None, 

678 name: Hashable | None = None, 

679 ) -> Self: 

680 if is_scalar(data): 

681 cls._raise_scalar_data_error(data) 

682 

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

684 

685 name = maybe_extract_name(name, data, cls) 

686 

687 # GH#63388 

688 data, copy = cls._maybe_copy_array_input(data, copy, dtype) 

689 

690 if ( 

691 isinstance(data, DatetimeArray) 

692 and freq is lib.no_default 

693 and tz is lib.no_default 

694 and dtype is None 

695 ): 

696 # fastpath, similar logic in TimedeltaIndex.__new__; 

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

698 if copy: 

699 data = data.copy() 

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

701 

702 dtarr = DatetimeArray._from_sequence_not_strict( 

703 data, 

704 dtype=dtype, 

705 copy=copy, 

706 tz=tz, 

707 freq=freq, 

708 dayfirst=dayfirst, 

709 yearfirst=yearfirst, 

710 ambiguous=ambiguous, 

711 ) 

712 refs = None 

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

714 refs = data._references 

715 

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

717 return subarr 

718 

719 # -------------------------------------------------------------------- 

720 

721 @cache_readonly 

722 def _is_dates_only(self) -> bool: 

723 """ 

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

725 

726 Returns 

727 ------- 

728 bool 

729 """ 

730 if isinstance(self.freq, Tick): 

731 delta = Timedelta(self.freq) 

732 

733 if delta % dt.timedelta(days=1) != dt.timedelta(days=0): 

734 return False 

735 

736 return self._values._is_dates_only 

737 

738 def __reduce__(self): 

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

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

741 

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

743 """ 

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

745 """ 

746 if isinstance(dtype, ArrowDtype): 

747 # GH#62277 

748 if dtype.kind != "M": 

749 return False 

750 

751 pa_dtype = dtype.pyarrow_dtype 

752 if (pa_dtype.tz is None) ^ (self.tz is None): 

753 return False 

754 return True 

755 

756 if self.tz is not None: 

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

758 return isinstance(dtype, DatetimeTZDtype) 

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

760 return lib.is_np_dtype(dtype, "M") 

761 

762 # -------------------------------------------------------------------- 

763 # Rendering Methods 

764 

765 @cache_readonly 

766 def _formatter_func(self): 

767 # Note this is equivalent to the DatetimeIndexOpsMixin method but 

768 # uses the maybe-cached self._is_dates_only instead of re-computing it. 

769 from pandas.io.formats.format import get_format_datetime64 

770 

771 formatter = get_format_datetime64(is_dates_only=self._is_dates_only) 

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

773 

774 # -------------------------------------------------------------------- 

775 # Set Operation Methods 

776 

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

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

779 # range representation due to no singular step 

780 if ( 

781 self.tz is not None 

782 and not timezones.is_utc(self.tz) 

783 and not timezones.is_fixed_offset(self.tz) 

784 ): 

785 return False 

786 if ( 

787 other.tz is not None 

788 and not timezones.is_utc(other.tz) 

789 and not timezones.is_fixed_offset(other.tz) 

790 ): 

791 return False 

792 return super()._can_range_setop(other) 

793 

794 # -------------------------------------------------------------------- 

795 

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

797 """ 

798 Return the number of microseconds since midnight. 

799 

800 Returns 

801 ------- 

802 ndarray[int64_t] 

803 """ 

804 values = self._data._local_timestamps() 

805 

806 ppd = periods_per_day(self._data._creso) 

807 

808 frac = values % ppd 

809 if self.unit == "ns": 

810 micros = frac // 1000 

811 elif self.unit == "us": 

812 micros = frac 

813 elif self.unit == "ms": 

814 micros = frac * 1000 

815 elif self.unit == "s": 

816 micros = frac * 1_000_000 

817 else: # pragma: no cover 

818 raise NotImplementedError(self.unit) 

819 

820 micros[self._isnan] = -1 

821 return micros 

822 

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

824 """ 

825 Snap time stamps to nearest occurring frequency. 

826 

827 Parameters 

828 ---------- 

829 freq : str, Timedelta, datetime.timedelta, or DateOffset, default 'S' 

830 Frequency strings can have multiples, e.g. '5h'. See 

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

832 frequency aliases. 

833 

834 Returns 

835 ------- 

836 DatetimeIndex 

837 Time stamps to nearest occurring `freq`. 

838 

839 See Also 

840 -------- 

841 DatetimeIndex.round : Perform round operation on the data to the 

842 specified `freq`. 

843 DatetimeIndex.floor : Perform floor operation on the data to the 

844 specified `freq`. 

845 

846 Examples 

847 -------- 

848 >>> idx = pd.DatetimeIndex( 

849 ... ["2023-01-01", "2023-01-02", "2023-02-01", "2023-02-02"], 

850 ... dtype="M8[ns]", 

851 ... ) 

852 >>> idx 

853 DatetimeIndex(['2023-01-01', '2023-01-02', '2023-02-01', '2023-02-02'], 

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

855 >>> idx.snap("MS") 

856 DatetimeIndex(['2023-01-01', '2023-01-01', '2023-02-01', '2023-02-01'], 

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

858 """ 

859 # Superdumb, punting on any optimizing 

860 freq = to_offset(freq) 

861 

862 dta = self._data.copy() 

863 

864 for i, v in enumerate(self): 

865 s = v 

866 if not freq.is_on_offset(s): 

867 t0 = freq.rollback(s) 

868 t1 = freq.rollforward(s) 

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

870 s = t0 

871 else: 

872 s = t1 

873 dta[i] = s 

874 

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

876 

877 # -------------------------------------------------------------------- 

878 # Indexing Methods 

879 

880 def _parsed_string_to_bounds( 

881 self, reso: Resolution, parsed: dt.datetime 

882 ) -> tuple[Timestamp, Timestamp]: 

883 """ 

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

885 

886 Parameters 

887 ---------- 

888 reso : Resolution 

889 Resolution provided by parsed string. 

890 parsed : datetime 

891 Datetime from parsed string. 

892 

893 Returns 

894 ------- 

895 lower, upper: pd.Timestamp 

896 """ 

897 freq = OFFSET_TO_PERIOD_FREQSTR.get(reso.attr_abbrev, reso.attr_abbrev) 

898 per = Period(parsed, freq=freq) 

899 start = per.start_time 

900 # Can't use end_time here bc that will subtract a microsecond 

901 # instead of a nanosecond 

902 end = (per + 1).start_time - np.timedelta64(1, "ns") 

903 start = start.as_unit(self.unit) 

904 end = end.as_unit(self.unit) 

905 

906 # GH 24076 

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

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

909 # timezone 

910 start = start.tz_localize(parsed.tzinfo) 

911 end = end.tz_localize(parsed.tzinfo) 

912 

913 if parsed.tzinfo is not None: 

914 if self.tz is None: 

915 raise ValueError( 

916 "The index must be timezone aware when indexing " 

917 "with a date string with a UTC offset" 

918 ) 

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

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

921 # which localizes parsed. 

922 return start, end 

923 

924 def _parse_with_reso(self, label: str) -> tuple[Timestamp, Resolution]: 

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

926 

927 parsed = Timestamp(parsed) 

928 

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

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

931 # DatetimeIndex 

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

933 parsed = parsed.tz_localize(self.tz) 

934 

935 return parsed, reso 

936 

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

938 """ 

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

940 """ 

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

942 try: 

943 # GH#36148 

944 self._data._assert_tzawareness_compat(key) 

945 except TypeError as err: 

946 raise KeyError(key) from err 

947 

948 def get_loc(self, key): 

949 """ 

950 Get integer location for requested label 

951 

952 Returns 

953 ------- 

954 loc : int 

955 """ 

956 self._check_indexing_error(key) 

957 

958 orig_key = key 

959 if is_valid_na_for_dtype(key, self.dtype): 

960 key = NaT 

961 

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

963 # needed to localize naive datetimes 

964 self._disallow_mismatched_indexing(key) 

965 key = Timestamp(key) 

966 

967 elif isinstance(key, str): 

968 try: 

969 parsed, reso = self._parse_with_reso(key) 

970 except ValueError as err: 

971 raise KeyError(key) from err 

972 self._disallow_mismatched_indexing(parsed) 

973 

974 if self._can_partial_date_slice(reso): 

975 try: 

976 return self._partial_date_slice(reso, parsed) 

977 except KeyError as err: 

978 raise KeyError(key) from err 

979 

980 key = parsed 

981 

982 elif isinstance(key, dt.timedelta): 

983 # GH#20464 

984 raise TypeError( 

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

986 ) 

987 

988 elif isinstance(key, dt.time): 

989 return self.indexer_at_time(key) 

990 

991 else: 

992 # unrecognized type 

993 raise KeyError(key) 

994 

995 try: 

996 return Index.get_loc(self, key) 

997 except KeyError as err: 

998 raise KeyError(orig_key) from err 

999 

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

1001 """ 

1002 This function should be overloaded in subclasses that allow non-trivial 

1003 casting on label-slice bounds, e.g. datetime-like indices allowing 

1004 strings containing formatted datetimes. 

1005 

1006 Parameters 

1007 ---------- 

1008 label : object 

1009 side : {'left', 'right'} 

1010 

1011 Returns 

1012 ------- 

1013 label : object 

1014 

1015 Notes 

1016 ----- 

1017 Value of `side` parameter should be validated in caller. 

1018 """ 

1019 # GH#42855 handle date here instead of get_slice_bound 

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

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

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

1023 label = Timestamp(label).to_pydatetime() 

1024 warnings.warn( 

1025 # GH#35830 deprecate last remaining inconsistent date treatment 

1026 "Slicing with a datetime.date object is deprecated. " 

1027 "Explicitly cast to Timestamp instead.", 

1028 Pandas4Warning, 

1029 stacklevel=find_stack_level(), 

1030 ) 

1031 

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

1033 self._data._assert_tzawareness_compat(label) 

1034 return Timestamp(label) 

1035 

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

1037 """ 

1038 Return indexer for specified label slice. 

1039 Index.slice_indexer, customized to handle time slicing. 

1040 

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

1042 following: 

1043 

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

1045 invokes `indexer_between_time` 

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

1047 value-based selection in non-monotonic cases. 

1048 

1049 """ 

1050 # For historical reasons DatetimeIndex supports slices between two 

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

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

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

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

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

1056 return self.indexer_between_time(start, end) 

1057 

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

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

1060 

1061 def check_str_or_none(point) -> bool: 

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

1063 

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

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

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

1067 if ( 

1068 check_str_or_none(start) 

1069 or check_str_or_none(end) 

1070 or self.is_monotonic_increasing 

1071 ): 

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

1073 

1074 mask = np.array(True) 

1075 in_index = True 

1076 if start is not None: 

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

1078 mask = start_casted <= self 

1079 in_index &= (start_casted == self).any() 

1080 

1081 if end is not None: 

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

1083 mask = (self <= end_casted) & mask 

1084 in_index &= (end_casted == self).any() 

1085 

1086 if not in_index: 

1087 raise KeyError( 

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

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

1090 ) 

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

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

1093 return slice(None) 

1094 else: 

1095 return indexer 

1096 

1097 # -------------------------------------------------------------------- 

1098 

1099 @property 

1100 def inferred_type(self) -> str: 

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

1102 # sure we can't have ambiguous indexing 

1103 return "datetime64" 

1104 

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

1106 """ 

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

1108 

1109 Parameters 

1110 ---------- 

1111 time : datetime.time or str 

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

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

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

1115 asof : bool, default False 

1116 This parameter is currently not supported. 

1117 

1118 Returns 

1119 ------- 

1120 np.ndarray[np.intp] 

1121 Index locations of values at given `time` of day. 

1122 

1123 See Also 

1124 -------- 

1125 indexer_between_time : Get index locations of values between particular 

1126 times of day. 

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

1128 

1129 Examples 

1130 -------- 

1131 >>> idx = pd.DatetimeIndex( 

1132 ... ["1/1/2020 10:00", "2/1/2020 11:00", "3/1/2020 10:00"] 

1133 ... ) 

1134 >>> idx.indexer_at_time("10:00") 

1135 array([0, 2]) 

1136 """ 

1137 if asof: 

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

1139 

1140 if isinstance(time, str): 

1141 from dateutil.parser import parse 

1142 

1143 orig = time 

1144 try: 

1145 alt = to_time(time) 

1146 except ValueError: 

1147 warnings.warn( 

1148 # GH#50839 

1149 f"The string '{orig}' cannot be parsed using pd.core.tools.to_time " 

1150 f"and in a future version will raise. " 

1151 "Use an unambiguous time string format or explicitly cast to " 

1152 "`datetime.time` before calling.", 

1153 Pandas4Warning, 

1154 stacklevel=find_stack_level(), 

1155 ) 

1156 time = parse(time).time() 

1157 else: 

1158 try: 

1159 time = parse(time).time() 

1160 except ValueError: 

1161 # e.g. '23550' raises dateutil.parser._parser.ParserError 

1162 time = alt 

1163 if alt != time: 

1164 warnings.warn( 

1165 # GH#50839 

1166 f"The string '{orig}' is currently parsed as {time} " 

1167 f"but in a future version will be parsed as {alt}, consistent" 

1168 "with `between_time` behavior. To avoid this warning, " 

1169 "use an unambiguous string format or explicitly cast to " 

1170 "`datetime.time` before calling.", 

1171 Pandas4Warning, 

1172 stacklevel=find_stack_level(), 

1173 ) 

1174 

1175 if time.tzinfo: 

1176 if self.tz is None: 

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

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

1179 else: 

1180 time_micros = self._get_time_micros() 

1181 micros = _time_to_micros(time) 

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

1183 

1184 def indexer_between_time( 

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

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

1187 """ 

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

1189 

1190 Parameters 

1191 ---------- 

1192 start_time, end_time : datetime.time, str 

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

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

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

1196 include_start : bool, default True 

1197 Include boundaries; whether to set start bound as closed or open. 

1198 include_end : bool, default True 

1199 Include boundaries; whether to set end bound as closed or open. 

1200 

1201 Returns 

1202 ------- 

1203 np.ndarray[np.intp] 

1204 Index locations of values between particular times of day. 

1205 

1206 See Also 

1207 -------- 

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

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

1210 

1211 Examples 

1212 -------- 

1213 >>> idx = pd.date_range("2023-01-01", periods=4, freq="h") 

1214 >>> idx 

1215 DatetimeIndex(['2023-01-01 00:00:00', '2023-01-01 01:00:00', 

1216 '2023-01-01 02:00:00', '2023-01-01 03:00:00'], 

1217 dtype='datetime64[us]', freq='h') 

1218 >>> idx.indexer_between_time("00:00", "2:00", include_end=False) 

1219 array([0, 1]) 

1220 """ 

1221 start_time = to_time(start_time) 

1222 end_time = to_time(end_time) 

1223 time_micros = self._get_time_micros() 

1224 start_micros = _time_to_micros(start_time) 

1225 end_micros = _time_to_micros(end_time) 

1226 

1227 if include_start and include_end: 

1228 lop = rop = operator.le 

1229 elif include_start: 

1230 lop = operator.le 

1231 rop = operator.lt 

1232 elif include_end: 

1233 lop = operator.lt 

1234 rop = operator.le 

1235 else: 

1236 lop = rop = operator.lt 

1237 

1238 if start_time <= end_time: 

1239 join_op = operator.and_ 

1240 else: 

1241 join_op = operator.or_ 

1242 

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

1244 

1245 return mask.nonzero()[0] 

1246 

1247 

1248@set_module("pandas") 

1249def date_range( 

1250 start=None, 

1251 end=None, 

1252 periods=None, 

1253 freq=None, 

1254 tz=None, 

1255 normalize: bool = False, 

1256 name: Hashable | None = None, 

1257 inclusive: IntervalClosedType = "both", 

1258 *, 

1259 unit: TimeUnit | None = None, 

1260 **kwargs, 

1261) -> DatetimeIndex: 

1262 """ 

1263 Return a fixed frequency DatetimeIndex. 

1264 

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

1266 two adjacent points is specified by the given frequency) such that they fall in the 

1267 range `[start, end]` , where the first one and the last one are, resp., the first 

1268 and last time points in that range that fall on the boundary of ``freq`` (if given 

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

1270 :class:`pandas.tseries.offsets.DateOffset`). If ``freq`` is positive, the points 

1271 satisfy `start <[=] x <[=] end`, and if ``freq`` is negative, the points satisfy 

1272 `end <[=] x <[=] start`. (If exactly one of ``start``, ``end``, or ``freq`` is *not* 

1273 specified, this missing parameter can be computed given ``periods``, the number of 

1274 timesteps in the range. See the note below.) 

1275 

1276 Parameters 

1277 ---------- 

1278 start : str or datetime-like, optional 

1279 Left bound for generating dates. 

1280 end : str or datetime-like, optional 

1281 Right bound for generating dates. 

1282 periods : int, optional 

1283 Number of periods to generate. 

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

1285 Frequency strings can have multiples, e.g. '5h'. See 

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

1287 frequency aliases. 

1288 tz : str or tzinfo, optional 

1289 Time zone name for returning localized DatetimeIndex, for example 

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

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

1292 normalize : bool, default False 

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

1294 name : Hashable, default None 

1295 Name of the resulting DatetimeIndex. 

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

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

1298 unit : {'s', 'ms', 'us', 'ns', None}, default None 

1299 Specify the desired resolution of the result. 

1300 If not specified, this is inferred from the 'start', 'end', and 'freq' 

1301 using the same inference as :class:`Timestamp` taking the highest 

1302 resolution of the three that are provided. 

1303 

1304 .. versionadded:: 2.0.0 

1305 **kwargs 

1306 For compatibility. Has no effect on the result. 

1307 

1308 Returns 

1309 ------- 

1310 DatetimeIndex 

1311 A DatetimeIndex object of the generated dates. 

1312 

1313 See Also 

1314 -------- 

1315 DatetimeIndex : An immutable container for datetimes. 

1316 timedelta_range : Return a fixed frequency TimedeltaIndex. 

1317 period_range : Return a fixed frequency PeriodIndex. 

1318 interval_range : Return a fixed frequency IntervalIndex. 

1319 

1320 Notes 

1321 ----- 

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

1323 a maximum of three can be specified at once. Of the three parameters 

1324 ``start``, ``end``, and ``periods``, at least two must be specified. 

1325 If ``freq`` is omitted, the resulting ``DatetimeIndex`` will have 

1326 ``periods`` linearly spaced elements between ``start`` and ``end`` 

1327 (closed on both sides). 

1328 

1329 To learn more about the frequency strings, please see 

1330 :ref:`this link<timeseries.offset_aliases>`. 

1331 

1332 Examples 

1333 -------- 

1334 **Specifying the values** 

1335 

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

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

1338 

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

1340 

1341 >>> pd.date_range(start="1/1/2018", end="1/08/2018") 

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

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

1344 dtype='datetime64[us]', freq='D') 

1345 

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

1347 

1348 >>> pd.date_range( 

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

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

1351 ... ) 

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

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

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

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

1356 dtype='datetime64[us, Europe/Berlin]', freq='D') 

1357 

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

1359 

1360 >>> pd.date_range(start="1/1/2018", periods=8) 

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

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

1363 dtype='datetime64[us]', freq='D') 

1364 

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

1366 

1367 >>> pd.date_range(end="1/1/2018", periods=8) 

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

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

1370 dtype='datetime64[us]', freq='D') 

1371 

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

1373 automatically (linearly spaced). 

1374 

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

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

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

1378 dtype='datetime64[us]', freq=None) 

1379 

1380 **Other Parameters** 

1381 

1382 Changed the `freq` (frequency) to ``'ME'`` (month end frequency). 

1383 

1384 >>> pd.date_range(start="1/1/2018", periods=5, freq="ME") 

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

1386 '2018-05-31'], 

1387 dtype='datetime64[us]', freq='ME') 

1388 

1389 Multiples are allowed 

1390 

1391 >>> pd.date_range(start="1/1/2018", periods=5, freq="3ME") 

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

1393 '2019-01-31'], 

1394 dtype='datetime64[us]', freq='3ME') 

1395 

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

1397 

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

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

1400 '2019-01-31'], 

1401 dtype='datetime64[us]', freq='3ME') 

1402 

1403 Specify `tz` to set the timezone. 

1404 

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

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

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

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

1409 dtype='datetime64[us, Asia/Tokyo]', freq='D') 

1410 

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

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

1413 

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

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

1416 dtype='datetime64[us]', freq='D') 

1417 

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

1419 

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

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

1422 dtype='datetime64[us]', freq='D') 

1423 

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

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

1426 

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

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

1429 dtype='datetime64[us]', freq='D') 

1430 

1431 **Specify a unit** 

1432 

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

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

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

1436 '2817-01-01', '2917-01-01'], 

1437 dtype='datetime64[s]', freq='100YS-JAN') 

1438 """ 

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

1440 freq = "D" 

1441 if freq is not None: 

1442 freq = to_offset(freq) 

1443 

1444 if start is NaT or end is NaT: 

1445 # This check needs to come before the `unit = start.unit` line below 

1446 raise ValueError("Neither `start` nor `end` can be NaT") 

1447 

1448 if unit is None: 

1449 # Infer the unit based on the inputs 

1450 

1451 if start is not None and end is not None: 

1452 start = Timestamp(start) 

1453 end = Timestamp(end) 

1454 if abbrev_to_npy_unit(start.unit) > abbrev_to_npy_unit(end.unit): 

1455 unit = start.unit 

1456 else: 

1457 unit = end.unit 

1458 elif start is not None: 

1459 start = Timestamp(start) 

1460 unit = start.unit 

1461 elif end is not None: 

1462 end = Timestamp(end) 

1463 unit = end.unit 

1464 else: 

1465 raise ValueError( 

1466 "Of the four parameters: start, end, periods, " 

1467 "and freq, exactly three must be specified" 

1468 ) 

1469 

1470 # Last we need to watch out for cases where the 'freq' implies a higher 

1471 # unit than either start or end 

1472 if freq is not None: 

1473 creso = abbrev_to_npy_unit(unit) 

1474 if isinstance(freq, Tick): 

1475 if freq._creso > creso: 

1476 unit = freq.base.freqstr # type: ignore[assignment] 

1477 elif hasattr(freq, "offset") and freq.offset is not None: 

1478 # e.g. BDay with an offset 

1479 td = Timedelta(freq.offset) 

1480 if abbrev_to_npy_unit(td.unit) > creso: 

1481 unit = td.unit 

1482 elif type(freq) is DateOffset: 

1483 if getattr(freq, "nanoseconds", 0) != 0: 

1484 # e.g. test_freq_dateoffset_with_relateivedelta_nanos 

1485 unit = "ns" 

1486 elif getattr(freq, "microseconds", 0) != 0 and unit != "ns": 

1487 unit = "us" 

1488 elif getattr(freq, "milliseconds", 0) != 0 and unit not in ["ns", "us"]: 

1489 unit = "ms" 

1490 

1491 dtarr = DatetimeArray._generate_range( 

1492 start=start, 

1493 end=end, 

1494 periods=periods, 

1495 freq=freq, 

1496 tz=tz, 

1497 normalize=normalize, 

1498 inclusive=inclusive, 

1499 unit=unit, 

1500 **kwargs, 

1501 ) 

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

1503 

1504 

1505@set_module("pandas") 

1506def bdate_range( 

1507 start=None, 

1508 end=None, 

1509 periods: int | None = None, 

1510 freq: Frequency | dt.timedelta = "B", 

1511 tz=None, 

1512 normalize: bool = True, 

1513 name: Hashable | None = None, 

1514 weekmask=None, 

1515 holidays=None, 

1516 inclusive: IntervalClosedType = "both", 

1517 **kwargs, 

1518) -> DatetimeIndex: 

1519 """ 

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

1521 

1522 Parameters 

1523 ---------- 

1524 start : str or datetime-like, default None 

1525 Left bound for generating dates. 

1526 end : str or datetime-like, default None 

1527 Right bound for generating dates. 

1528 periods : int, default None 

1529 Number of periods to generate. 

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

1531 Frequency strings can have multiples, e.g. '5h'. The default is 

1532 business daily ('B'). 

1533 tz : str or None 

1534 Time zone name for returning localized DatetimeIndex, for example 

1535 Asia/Beijing. 

1536 normalize : bool, default False 

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

1538 name : Hashable, default None 

1539 Name of the resulting DatetimeIndex. 

1540 weekmask : str or None, default None 

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

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

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

1544 holidays : list-like or None, default None 

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

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

1547 are passed. 

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

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

1550 **kwargs 

1551 For compatibility. Has no effect on the result. 

1552 

1553 Returns 

1554 ------- 

1555 DatetimeIndex 

1556 Fixed frequency DatetimeIndex. 

1557 

1558 See Also 

1559 -------- 

1560 date_range : Return a fixed frequency DatetimeIndex. 

1561 period_range : Return a fixed frequency PeriodIndex. 

1562 timedelta_range : Return a fixed frequency TimedeltaIndex. 

1563 

1564 Notes 

1565 ----- 

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

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

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

1569 desired. 

1570 

1571 To learn more about the frequency strings, please see 

1572 :ref:`this link<timeseries.offset_aliases>`. 

1573 

1574 Examples 

1575 -------- 

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

1577 

1578 >>> pd.bdate_range(start="1/1/2018", end="1/08/2018") 

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

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

1581 dtype='datetime64[us]', freq='B') 

1582 """ 

1583 if freq is None: 

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

1585 raise TypeError(msg) 

1586 

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

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

1589 if freq == "CBH": 

1590 raise ValueError(f"{msg}, did you mean cbh?") 

1591 try: 

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

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

1594 except (KeyError, TypeError) as err: 

1595 raise ValueError(msg) from err 

1596 elif holidays or weekmask: 

1597 msg = ( 

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

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

1600 ) 

1601 raise ValueError(msg) 

1602 

1603 return date_range( 

1604 start=start, 

1605 end=end, 

1606 periods=periods, 

1607 freq=freq, 

1608 tz=tz, 

1609 normalize=normalize, 

1610 name=name, 

1611 inclusive=inclusive, 

1612 **kwargs, 

1613 ) 

1614 

1615 

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

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

1618 return 1_000_000 * seconds + time_obj.microsecond