Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/arrays/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

655 statements  

1from __future__ import annotations 

2 

3from datetime import ( 

4 datetime, 

5 timedelta, 

6 tzinfo, 

7) 

8from typing import ( 

9 TYPE_CHECKING, 

10 Self, 

11 TypeVar, 

12 cast, 

13 overload, 

14) 

15import warnings 

16 

17import numpy as np 

18 

19from pandas._config import using_string_dtype 

20from pandas._config.config import get_option 

21 

22from pandas._libs import ( 

23 lib, 

24 tslib, 

25) 

26from pandas._libs.tslibs import ( 

27 BaseOffset, 

28 NaT, 

29 NaTType, 

30 Resolution, 

31 Timestamp, 

32 astype_overflowsafe, 

33 fields, 

34 get_resolution, 

35 get_supported_dtype, 

36 get_unit_from_dtype, 

37 ints_to_pydatetime, 

38 is_date_array_normalized, 

39 is_supported_dtype, 

40 is_unitless, 

41 normalize_i8_timestamps, 

42 timezones, 

43 to_offset, 

44 tz_convert_from_utc, 

45 tzconversion, 

46) 

47from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit 

48from pandas.errors import PerformanceWarning 

49from pandas.util._decorators import set_module 

50from pandas.util._exceptions import find_stack_level 

51from pandas.util._validators import validate_inclusive 

52 

53from pandas.core.dtypes.common import ( 

54 DT64NS_DTYPE, 

55 INT64_DTYPE, 

56 is_bool_dtype, 

57 is_float_dtype, 

58 is_string_dtype, 

59 pandas_dtype, 

60) 

61from pandas.core.dtypes.dtypes import ( 

62 DatetimeTZDtype, 

63 ExtensionDtype, 

64 PeriodDtype, 

65) 

66from pandas.core.dtypes.missing import isna 

67 

68from pandas.core.arrays import datetimelike as dtl 

69from pandas.core.arrays._ranges import generate_regular_range 

70import pandas.core.common as com 

71 

72from pandas.tseries.frequencies import get_period_alias 

73from pandas.tseries.offsets import ( 

74 Day, 

75 Tick, 

76) 

77 

78if TYPE_CHECKING: 

79 from collections.abc import ( 

80 Callable, 

81 Generator, 

82 Iterator, 

83 ) 

84 

85 from pandas._typing import ( 

86 ArrayLike, 

87 DateTimeErrorChoices, 

88 DtypeObj, 

89 IntervalClosedType, 

90 TimeAmbiguous, 

91 TimeNonexistent, 

92 TimeUnit, 

93 npt, 

94 ) 

95 

96 from pandas import ( 

97 DataFrame, 

98 Timedelta, 

99 ) 

100 from pandas.core.arrays import PeriodArray 

101 

102 _TimestampNoneT1 = TypeVar("_TimestampNoneT1", Timestamp, None) 

103 _TimestampNoneT2 = TypeVar("_TimestampNoneT2", Timestamp, None) 

104 

105 

106_ITER_CHUNKSIZE = 10_000 

107 

108 

109@overload 

110def tz_to_dtype(tz: tzinfo, unit: TimeUnit = ...) -> DatetimeTZDtype: ... 

111 

112 

113@overload 

114def tz_to_dtype(tz: None, unit: TimeUnit = ...) -> np.dtype[np.datetime64]: ... 

115 

116 

117def tz_to_dtype( 

118 tz: tzinfo | None, unit: TimeUnit = "ns" 

119) -> np.dtype[np.datetime64] | DatetimeTZDtype: 

120 """ 

121 Return a datetime64[ns] dtype appropriate for the given timezone. 

122 

123 Parameters 

124 ---------- 

125 tz : tzinfo or None 

126 unit : str, default "ns" 

127 

128 Returns 

129 ------- 

130 np.dtype or Datetime64TZDType 

131 """ 

132 if tz is None: 

133 return np.dtype(f"M8[{unit}]") 

134 else: 

135 return DatetimeTZDtype(tz=tz, unit=unit) 

136 

137 

138def _field_accessor(name: str, field: str, docstring: str | None = None): 

139 def f(self): 

140 values = self._local_timestamps() 

141 

142 if field in self._bool_ops: 

143 result: np.ndarray 

144 

145 if field.endswith(("start", "end")): 

146 freq = self.freq 

147 month_kw = 12 

148 if freq: 

149 kwds = freq.kwds 

150 month_kw = kwds.get("startingMonth", kwds.get("month", month_kw)) 

151 

152 if freq is not None: 

153 freq_name = freq.name 

154 else: 

155 freq_name = None 

156 result = fields.get_start_end_field( 

157 values, field, freq_name, month_kw, reso=self._creso 

158 ) 

159 else: 

160 result = fields.get_date_field(values, field, reso=self._creso) 

161 

162 # these return a boolean by-definition 

163 return result 

164 

165 result = fields.get_date_field(values, field, reso=self._creso) 

166 result = self._maybe_mask_results(result, fill_value=None, convert="float64") 

167 

168 return result 

169 

170 f.__name__ = name 

171 f.__doc__ = docstring 

172 return property(f) 

173 

174 

175@set_module("pandas.arrays") 

176class DatetimeArray(dtl.TimelikeOps, dtl.DatelikeOps): 

177 """ 

178 Pandas ExtensionArray for tz-naive or tz-aware datetime data. 

179 

180 .. warning:: 

181 

182 DatetimeArray is currently experimental, and its API may change 

183 without warning. In particular, :attr:`DatetimeArray.dtype` is 

184 expected to change to always be an instance of an ``ExtensionDtype`` 

185 subclass. 

186 

187 Parameters 

188 ---------- 

189 data : Series, Index, DatetimeArray, ndarray 

190 The datetime data. 

191 

192 For DatetimeArray `values` (or a Series or Index boxing one), 

193 `dtype` and `freq` will be extracted from `values`. 

194 

195 dtype : numpy.dtype or DatetimeTZDtype 

196 Note that the only NumPy dtype allowed is 'datetime64[ns]'. 

197 freq : str or Offset, optional 

198 The frequency. 

199 copy : bool, default False 

200 Whether to copy the underlying array of values. 

201 

202 Attributes 

203 ---------- 

204 None 

205 

206 Methods 

207 ------- 

208 None 

209 

210 See Also 

211 -------- 

212 DatetimeIndex : Immutable Index for datetime-like data. 

213 Series : One-dimensional labeled array capable of holding datetime-like data. 

214 Timestamp : Pandas replacement for python datetime.datetime object. 

215 to_datetime : Convert argument to datetime. 

216 period_range : Return a fixed frequency PeriodIndex. 

217 

218 Examples 

219 -------- 

220 >>> pd.arrays.DatetimeArray._from_sequence( 

221 ... pd.DatetimeIndex(["2023-01-01", "2023-01-02"], freq="D") 

222 ... ) 

223 <DatetimeArray> 

224 ['2023-01-01 00:00:00', '2023-01-02 00:00:00'] 

225 Length: 2, dtype: datetime64[us] 

226 """ 

227 

228 _typ = "datetimearray" 

229 _recognized_scalars = (datetime, np.datetime64) 

230 _is_recognized_dtype: Callable[[DtypeObj], bool] = lambda x: ( 

231 lib.is_np_dtype(x, "M") or isinstance(x, DatetimeTZDtype) 

232 ) 

233 _infer_matches = ("datetime", "datetime64", "date") 

234 

235 @property 

236 def _internal_fill_value(self) -> np.datetime64: 

237 return np.datetime64("NaT", self.unit) 

238 

239 @property 

240 def _scalar_type(self) -> type[Timestamp]: 

241 return Timestamp 

242 

243 # define my properties & methods for delegation 

244 _bool_ops: list[str] = [ 

245 "is_month_start", 

246 "is_month_end", 

247 "is_quarter_start", 

248 "is_quarter_end", 

249 "is_year_start", 

250 "is_year_end", 

251 "is_leap_year", 

252 ] 

253 _field_ops: list[str] = [ 

254 "year", 

255 "month", 

256 "day", 

257 "hour", 

258 "minute", 

259 "second", 

260 "weekday", 

261 "dayofweek", 

262 "day_of_week", 

263 "dayofyear", 

264 "day_of_year", 

265 "quarter", 

266 "days_in_month", 

267 "daysinmonth", 

268 "microsecond", 

269 "nanosecond", 

270 ] 

271 _other_ops: list[str] = ["date", "time", "timetz"] 

272 _datetimelike_ops: list[str] = ( 

273 _field_ops + _bool_ops + _other_ops + ["unit", "freq", "tz"] 

274 ) 

275 _datetimelike_methods: list[str] = [ 

276 "to_period", 

277 "tz_localize", 

278 "tz_convert", 

279 "normalize", 

280 "strftime", 

281 "round", 

282 "floor", 

283 "ceil", 

284 "month_name", 

285 "day_name", 

286 "as_unit", 

287 ] 

288 

289 # ndim is inherited from ExtensionArray, must exist to ensure 

290 # Timestamp.__richcmp__(DateTimeArray) operates pointwise 

291 

292 # ensure that operations with numpy arrays defer to our implementation 

293 __array_priority__ = 1000 

294 

295 # ----------------------------------------------------------------- 

296 # Constructors 

297 

298 _dtype: np.dtype[np.datetime64] | DatetimeTZDtype 

299 _freq: BaseOffset | None = None 

300 

301 @classmethod 

302 def _validate_dtype(cls, values, dtype): 

303 # used in TimeLikeOps.__init__ 

304 dtype = _validate_dt64_dtype(dtype) 

305 _validate_dt64_dtype(values.dtype) 

306 if isinstance(dtype, np.dtype): 

307 if values.dtype != dtype: 

308 raise ValueError("Values resolution does not match dtype.") 

309 else: 

310 vunit = np.datetime_data(values.dtype)[0] 

311 if vunit != dtype.unit: 

312 raise ValueError("Values resolution does not match dtype.") 

313 return dtype 

314 

315 # error: Signature of "_simple_new" incompatible with supertype "NDArrayBacked" 

316 @classmethod 

317 def _simple_new( # type: ignore[override] 

318 cls, 

319 values: npt.NDArray[np.datetime64], 

320 freq: BaseOffset | None = None, 

321 dtype: np.dtype[np.datetime64] | DatetimeTZDtype = DT64NS_DTYPE, 

322 ) -> Self: 

323 assert isinstance(values, np.ndarray) 

324 assert dtype.kind == "M" 

325 if isinstance(dtype, np.dtype): 

326 assert dtype == values.dtype 

327 assert not is_unitless(dtype) 

328 else: 

329 # DatetimeTZDtype. If we have e.g. DatetimeTZDtype[us, UTC], 

330 # then values.dtype should be M8[us]. 

331 assert dtype._creso == get_unit_from_dtype(values.dtype) 

332 

333 result = super()._simple_new(values, dtype) 

334 result._freq = freq 

335 return result 

336 

337 @classmethod 

338 def _from_sequence(cls, scalars, *, dtype=None, copy: bool = False) -> Self: 

339 return cls._from_sequence_not_strict(scalars, dtype=dtype, copy=copy) 

340 

341 @classmethod 

342 def _from_sequence_not_strict( 

343 cls, 

344 data, 

345 *, 

346 dtype=None, 

347 copy: bool = False, 

348 tz=lib.no_default, 

349 freq: str | BaseOffset | lib.NoDefault | None = lib.no_default, 

350 dayfirst: bool = False, 

351 yearfirst: bool = False, 

352 ambiguous: TimeAmbiguous = "raise", 

353 ) -> Self: 

354 """ 

355 A non-strict version of _from_sequence, called from DatetimeIndex.__new__. 

356 """ 

357 

358 # if the user either explicitly passes tz=None or a tz-naive dtype, we 

359 # disallows inferring a tz. 

360 explicit_tz_none = tz is None 

361 if tz is lib.no_default: 

362 tz = None 

363 else: 

364 tz = timezones.maybe_get_tz(tz) 

365 

366 dtype = _validate_dt64_dtype(dtype) 

367 # if dtype has an embedded tz, capture it 

368 tz = _validate_tz_from_dtype(dtype, tz, explicit_tz_none) 

369 

370 unit = None 

371 if dtype is not None: 

372 unit = dtl.dtype_to_unit(dtype) 

373 

374 data, copy = dtl.ensure_arraylike_for_datetimelike( 

375 data, copy, cls_name="DatetimeArray" 

376 ) 

377 inferred_freq = None 

378 if isinstance(data, DatetimeArray): 

379 inferred_freq = data.freq 

380 

381 subarr, tz = _sequence_to_dt64( 

382 data, 

383 copy=copy, 

384 tz=tz, 

385 dayfirst=dayfirst, 

386 yearfirst=yearfirst, 

387 ambiguous=ambiguous, 

388 out_unit=unit, 

389 ) 

390 # We have to call this again after possibly inferring a tz above 

391 _validate_tz_from_dtype(dtype, tz, explicit_tz_none) 

392 if tz is not None and explicit_tz_none: 

393 raise ValueError( 

394 "Passed data is timezone-aware, incompatible with 'tz=None'. " 

395 "Use obj.tz_localize(None) instead." 

396 ) 

397 

398 data_unit = np.datetime_data(subarr.dtype)[0] 

399 data_unit = cast("TimeUnit", data_unit) 

400 data_dtype = tz_to_dtype(tz, data_unit) 

401 result = cls._simple_new(subarr, freq=inferred_freq, dtype=data_dtype) 

402 if unit is not None and unit != result.unit: 

403 # If unit was specified in user-passed dtype, cast to it here 

404 # error: Argument 1 to "as_unit" of "TimelikeOps" has 

405 # incompatible type "str"; expected "Literal['s', 'ms', 'us', 'ns']" 

406 # [arg-type] 

407 result = result.as_unit(unit) # type: ignore[arg-type] 

408 

409 validate_kwds = {"ambiguous": ambiguous} 

410 result._maybe_pin_freq(freq, validate_kwds) 

411 return result 

412 

413 @classmethod 

414 def _generate_range( 

415 cls, 

416 start, 

417 end, 

418 periods: int | None, 

419 freq, 

420 tz=None, 

421 normalize: bool = False, 

422 ambiguous: TimeAmbiguous = "raise", 

423 nonexistent: TimeNonexistent = "raise", 

424 inclusive: IntervalClosedType = "both", 

425 *, 

426 unit: TimeUnit = "ns", 

427 ) -> Self: 

428 periods = dtl.validate_periods(periods) 

429 if freq is None and any(x is None for x in [periods, start, end]): 

430 raise ValueError("Must provide freq argument if no data is supplied") 

431 

432 if com.count_not_none(start, end, periods, freq) != 3: 

433 raise ValueError( 

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

435 "and freq, exactly three must be specified" 

436 ) 

437 freq = to_offset(freq) 

438 

439 if start is not None: 

440 start = Timestamp(start) 

441 

442 if end is not None: 

443 end = Timestamp(end) 

444 

445 if start is NaT or end is NaT: 

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

447 

448 if unit is not None: 

449 if unit not in ["s", "ms", "us", "ns"]: 

450 raise ValueError("'unit' must be one of 's', 'ms', 'us', 'ns'") 

451 else: 

452 unit = "ns" 

453 

454 if start is not None: 

455 start = start.as_unit(unit, round_ok=False) 

456 if end is not None: 

457 end = end.as_unit(unit, round_ok=False) 

458 

459 left_inclusive, right_inclusive = validate_inclusive(inclusive) 

460 start, end = _maybe_normalize_endpoints(start, end, normalize) 

461 tz = _infer_tz_from_endpoints(start, end, tz) 

462 

463 if tz is not None: 

464 # Localize the start and end arguments 

465 start = _maybe_localize_point(start, freq, tz, ambiguous, nonexistent) 

466 end = _maybe_localize_point(end, freq, tz, ambiguous, nonexistent) 

467 

468 if freq is not None: 

469 # Offset handling: 

470 # Ticks (fixed-duration like hours/minutes): keep tz; do absolute-time math. 

471 # Other calendar offsets: drop tz; do naive wall time; localize once later 

472 # so `ambiguous`/`nonexistent` are applied correctly. 

473 if not isinstance(freq, Tick): 

474 if start is not None and start.tz is not None: 

475 start = start.tz_localize(None) 

476 if end is not None and end.tz is not None: 

477 end = end.tz_localize(None) 

478 

479 if isinstance(freq, (Tick, Day)): 

480 i8values = generate_regular_range(start, end, periods, freq, unit=unit) 

481 else: 

482 xdr = _generate_range( 

483 start=start, end=end, periods=periods, offset=freq, unit=unit 

484 ) 

485 i8values = np.array([x._value for x in xdr], dtype=np.int64) 

486 

487 endpoint_tz = start.tz if start is not None else end.tz 

488 

489 if tz is not None and endpoint_tz is None: 

490 if not timezones.is_utc(tz): 

491 # short-circuit tz_localize_to_utc which would make 

492 # an unnecessary copy with UTC but be a no-op. 

493 creso = abbrev_to_npy_unit(unit) 

494 i8values = tzconversion.tz_localize_to_utc( 

495 i8values, 

496 tz, 

497 ambiguous=ambiguous, 

498 nonexistent=nonexistent, 

499 creso=creso, 

500 ) 

501 

502 # i8values is localized datetime64 array -> have to convert 

503 # start/end as well to compare 

504 if start is not None: 

505 start = start.tz_localize(tz, ambiguous, nonexistent) 

506 if end is not None: 

507 end = end.tz_localize(tz, ambiguous, nonexistent) 

508 else: 

509 # Create a linearly spaced date_range in local time 

510 # Nanosecond-granularity timestamps aren't always correctly 

511 # representable with doubles, so we limit the range that we 

512 # pass to np.linspace as much as possible 

513 periods = cast(int, periods) 

514 i8values = ( 

515 np.linspace(0, end._value - start._value, periods, dtype="int64") 

516 + start._value 

517 ) 

518 if i8values.dtype != "i8": 

519 # 2022-01-09 I (brock) am not sure if it is possible for this 

520 # to overflow and cast to e.g. f8, but if it does we need to cast 

521 i8values = i8values.astype("i8") 

522 

523 if start == end: 

524 if not left_inclusive and not right_inclusive: 

525 i8values = i8values[1:-1] 

526 else: 

527 start_i8 = Timestamp(start)._value 

528 end_i8 = Timestamp(end)._value 

529 if not left_inclusive or not right_inclusive: 

530 if not left_inclusive and len(i8values) and i8values[0] == start_i8: 

531 i8values = i8values[1:] 

532 if not right_inclusive and len(i8values) and i8values[-1] == end_i8: 

533 i8values = i8values[:-1] 

534 

535 dt64_values = i8values.view(f"datetime64[{unit}]") 

536 dtype = tz_to_dtype(tz, unit=unit) 

537 return cls._simple_new(dt64_values, freq=freq, dtype=dtype) 

538 

539 # ----------------------------------------------------------------- 

540 # DatetimeLike Interface 

541 

542 def _unbox_scalar(self, value) -> np.datetime64: 

543 if not isinstance(value, self._scalar_type) and value is not NaT: 

544 raise ValueError("'value' should be a Timestamp.") 

545 self._check_compatible_with(value) 

546 if value is NaT: 

547 return np.datetime64(value._value, self.unit) 

548 else: 

549 return value.as_unit(self.unit, round_ok=False).asm8 

550 

551 def _scalar_from_string(self, value) -> Timestamp | NaTType: 

552 return Timestamp(value, tz=self.tz) 

553 

554 def _check_compatible_with(self, other) -> None: 

555 if other is NaT: 

556 return 

557 self._assert_tzawareness_compat(other) 

558 

559 # ----------------------------------------------------------------- 

560 # Descriptive Properties 

561 

562 def _box_func(self, x: np.datetime64) -> Timestamp | NaTType: 

563 # GH#42228 

564 value = x.view("i8") 

565 ts = Timestamp._from_value_and_reso(value, reso=self._creso, tz=self.tz) 

566 return ts 

567 

568 @property 

569 # error: Return type "Union[dtype, DatetimeTZDtype]" of "dtype" 

570 # incompatible with return type "ExtensionDtype" in supertype 

571 # "ExtensionArray" 

572 def dtype(self) -> np.dtype[np.datetime64] | DatetimeTZDtype: # type: ignore[override] 

573 """ 

574 The dtype for the DatetimeArray. 

575 

576 .. warning:: 

577 

578 A future version of pandas will change dtype to never be a 

579 ``numpy.dtype``. Instead, :attr:`DatetimeArray.dtype` will 

580 always be an instance of an ``ExtensionDtype`` subclass. 

581 

582 Returns 

583 ------- 

584 numpy.dtype or DatetimeTZDtype 

585 If the values are tz-naive, then ``np.dtype('datetime64[ns]')`` 

586 is returned. 

587 

588 If the values are tz-aware, then the ``DatetimeTZDtype`` 

589 is returned. 

590 """ 

591 return self._dtype 

592 

593 @property 

594 def tz(self) -> tzinfo | None: 

595 """ 

596 Return the timezone. 

597 

598 Returns 

599 ------- 

600 zoneinfo.ZoneInfo,, datetime.tzinfo, pytz.tzinfo.BaseTZInfo, dateutil.tz.tz.tzfile, or None 

601 Returns None when the array is tz-naive. 

602 

603 See Also 

604 -------- 

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

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

607 DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from 

608 one time zone to another. 

609 

610 Examples 

611 -------- 

612 For Series: 

613 

614 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

615 >>> s = pd.to_datetime(s) 

616 >>> s 

617 0 2020-01-01 10:00:00+00:00 

618 1 2020-02-01 11:00:00+00:00 

619 dtype: datetime64[us, UTC] 

620 >>> s.dt.tz 

621 datetime.timezone.utc 

622 

623 For DatetimeIndex: 

624 

625 >>> idx = pd.DatetimeIndex( 

626 ... ["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"] 

627 ... ) 

628 >>> idx.tz 

629 datetime.timezone.utc 

630 """ # noqa: E501 

631 # GH 18595 

632 return getattr(self.dtype, "tz", None) 

633 

634 @tz.setter 

635 def tz(self, value): 

636 # GH 3746: Prevent localizing or converting the index by setting tz 

637 raise AttributeError( 

638 "Cannot directly set timezone. Use tz_localize() " 

639 "or tz_convert() as appropriate" 

640 ) 

641 

642 @property 

643 def tzinfo(self) -> tzinfo | None: 

644 """ 

645 Alias for tz attribute 

646 """ 

647 return self.tz 

648 

649 @property # NB: override with cache_readonly in immutable subclasses 

650 def is_normalized(self) -> bool: 

651 """ 

652 Returns True if all of the dates are at midnight ("no time") 

653 """ 

654 return is_date_array_normalized(self.asi8, self.tz, reso=self._creso) 

655 

656 @property # NB: override with cache_readonly in immutable subclasses 

657 def _resolution_obj(self) -> Resolution: 

658 return get_resolution(self.asi8, self.tz, reso=self._creso) 

659 

660 # ---------------------------------------------------------------- 

661 # Array-Like / EA-Interface Methods 

662 

663 def __array__(self, dtype=None, copy=None) -> np.ndarray: 

664 if dtype is None and self.tz: 

665 # The default for tz-aware is object, to preserve tz info 

666 dtype = object 

667 

668 return super().__array__(dtype=dtype, copy=copy) 

669 

670 def __iter__(self) -> Iterator: 

671 """ 

672 Return an iterator over the boxed values 

673 

674 Yields 

675 ------ 

676 tstamp : Timestamp 

677 """ 

678 if self.ndim > 1: 

679 for i in range(len(self)): 

680 yield self[i] 

681 else: 

682 # convert in chunks of 10k for efficiency 

683 data = self.asi8 

684 length = len(self) 

685 chunksize = _ITER_CHUNKSIZE 

686 chunks = (length // chunksize) + 1 

687 

688 for i in range(chunks): 

689 start_i = i * chunksize 

690 end_i = min((i + 1) * chunksize, length) 

691 converted = ints_to_pydatetime( 

692 data[start_i:end_i], 

693 tz=self.tz, 

694 box="timestamp", 

695 reso=self._creso, 

696 ) 

697 yield from converted 

698 

699 def astype(self, dtype, copy: bool = True): 

700 # We handle 

701 # --> datetime 

702 # --> period 

703 # DatetimeLikeArrayMixin Super handles the rest. 

704 dtype = pandas_dtype(dtype) 

705 

706 if dtype == self.dtype: 

707 if copy: 

708 return self.copy() 

709 return self 

710 

711 elif isinstance(dtype, ExtensionDtype): 

712 if not isinstance(dtype, DatetimeTZDtype): 

713 # e.g. Sparse[datetime64[ns]] 

714 return super().astype(dtype, copy=copy) 

715 elif self.tz is None: 

716 # pre-2.0 this did self.tz_localize(dtype.tz), which did not match 

717 # the Series behavior which did 

718 # values.tz_localize("UTC").tz_convert(dtype.tz) 

719 raise TypeError( 

720 "Cannot use .astype to convert from timezone-naive dtype to " 

721 "timezone-aware dtype. Use obj.tz_localize instead or " 

722 "series.dt.tz_localize instead" 

723 ) 

724 else: 

725 # tzaware unit conversion e.g. datetime64[s, UTC] 

726 np_dtype = np.dtype(dtype.str) 

727 res_values = astype_overflowsafe(self._ndarray, np_dtype, copy=copy) 

728 return type(self)._simple_new(res_values, dtype=dtype, freq=self.freq) 

729 

730 elif ( 

731 self.tz is None 

732 and lib.is_np_dtype(dtype, "M") 

733 and not is_unitless(dtype) 

734 and is_supported_dtype(dtype) 

735 ): 

736 # unit conversion e.g. datetime64[s] 

737 res_values = astype_overflowsafe(self._ndarray, dtype, copy=True) 

738 return type(self)._simple_new(res_values, dtype=res_values.dtype) 

739 # TODO: preserve freq? 

740 

741 elif self.tz is not None and lib.is_np_dtype(dtype, "M"): 

742 # pre-2.0 behavior for DTA/DTI was 

743 # values.tz_convert("UTC").tz_localize(None), which did not match 

744 # the Series behavior 

745 raise TypeError( 

746 "Cannot use .astype to convert from timezone-aware dtype to " 

747 "timezone-naive dtype. Use obj.tz_localize(None) or " 

748 "obj.tz_convert('UTC').tz_localize(None) instead." 

749 ) 

750 

751 elif ( 

752 self.tz is None 

753 and lib.is_np_dtype(dtype, "M") 

754 and dtype != self.dtype 

755 and is_unitless(dtype) 

756 ): 

757 raise TypeError( 

758 "Casting to unit-less dtype 'datetime64' is not supported. " 

759 "Pass e.g. 'datetime64[ns]' instead." 

760 ) 

761 

762 elif isinstance(dtype, PeriodDtype): 

763 return self.to_period(freq=dtype.freq) 

764 return dtl.DatetimeLikeArrayMixin.astype(self, dtype, copy) 

765 

766 # ----------------------------------------------------------------- 

767 # Rendering Methods 

768 

769 def _format_native_types( 

770 self, *, na_rep: str | float = "NaT", date_format=None, **kwargs 

771 ) -> npt.NDArray[np.object_]: 

772 if date_format is None and self._is_dates_only: 

773 # Only dates and no timezone: provide a default format 

774 date_format = "%Y-%m-%d" 

775 

776 return tslib.format_array_from_datetime( 

777 self.asi8, tz=self.tz, format=date_format, na_rep=na_rep, reso=self._creso 

778 ) 

779 

780 # ----------------------------------------------------------------- 

781 # Comparison Methods 

782 

783 def _assert_tzawareness_compat(self, other) -> None: 

784 # adapted from _Timestamp._assert_tzawareness_compat 

785 other_tz = getattr(other, "tzinfo", None) 

786 other_dtype = getattr(other, "dtype", None) 

787 

788 if isinstance(other_dtype, DatetimeTZDtype): 

789 # Get tzinfo from Series dtype 

790 other_tz = other.dtype.tz 

791 if other is NaT: 

792 # pd.NaT quacks both aware and naive 

793 pass 

794 elif self.tz is None: 

795 if other_tz is not None: 

796 raise TypeError( 

797 "Cannot compare tz-naive and tz-aware datetime-like objects." 

798 ) 

799 elif other_tz is None: 

800 raise TypeError( 

801 "Cannot compare tz-naive and tz-aware datetime-like objects" 

802 ) 

803 

804 # ----------------------------------------------------------------- 

805 # Arithmetic Methods 

806 

807 def _add_offset(self, offset: BaseOffset) -> Self: 

808 assert not isinstance(offset, Tick) 

809 

810 if self.tz is not None: 

811 values = self.tz_localize(None) 

812 else: 

813 values = self 

814 

815 try: 

816 res_values = offset._apply_array(values._ndarray) 

817 if res_values.dtype.kind == "i": 

818 # error: Argument 1 to "view" of "ndarray" has 

819 # incompatible type 

820 # "dtype[datetime64[date | int | None]] | DatetimeTZDtype"; 

821 # expected "dtype[Any] | _HasDType[dtype[Any]]" [arg-type] 

822 res_values = res_values.view(values.dtype) # type: ignore[arg-type] 

823 except NotImplementedError: 

824 if get_option("performance_warnings"): 

825 warnings.warn( 

826 "Non-vectorized DateOffset being applied to Series or " 

827 "DatetimeIndex.", 

828 PerformanceWarning, 

829 stacklevel=find_stack_level(), 

830 ) 

831 res_values = self.astype("O") + offset 

832 result = type(self)._from_sequence(res_values, dtype=self.dtype) 

833 

834 else: 

835 result = type(self)._simple_new(res_values, dtype=res_values.dtype) 

836 if offset.normalize: 

837 result = result.normalize() 

838 result._freq = None 

839 

840 if self.tz is not None: 

841 result = result.tz_localize(self.tz) 

842 

843 return result 

844 

845 # ----------------------------------------------------------------- 

846 # Timezone Conversion and Localization Methods 

847 

848 def _local_timestamps(self) -> npt.NDArray[np.int64]: 

849 """ 

850 Convert to an i8 (unix-like nanosecond timestamp) representation 

851 while keeping the local timezone and not using UTC. 

852 This is used to calculate time-of-day information as if the timestamps 

853 were timezone-naive. 

854 """ 

855 if self.tz is None or timezones.is_utc(self.tz): 

856 # Avoid the copy that would be made in tzconversion 

857 return self.asi8 

858 return tz_convert_from_utc(self.asi8, self.tz, reso=self._creso) 

859 

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

861 """ 

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

863 

864 Parameters 

865 ---------- 

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

867 Time zone for time. Corresponding timestamps would be converted 

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

869 convert to UTC and remove the timezone information. 

870 

871 Returns 

872 ------- 

873 Array or Index 

874 Datetme Array/Index with target `tz`. 

875 

876 Raises 

877 ------ 

878 TypeError 

879 If Datetime Array/Index is tz-naive. 

880 

881 See Also 

882 -------- 

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

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

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

886 

887 Examples 

888 -------- 

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

890 to other time zones: 

891 

892 >>> dti = pd.date_range( 

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

894 ... ) 

895 

896 >>> dti 

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

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

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

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

901 

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

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

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

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

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

907 

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

909 to UTC if necessary): 

910 

911 >>> dti = pd.date_range( 

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

913 ... ) 

914 

915 >>> dti 

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

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

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

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

920 

921 >>> dti.tz_convert(None) 

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

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

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

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

926 """ # noqa: E501 

927 tz = timezones.maybe_get_tz(tz) 

928 

929 if self.tz is None: 

930 # tz naive, use tz_localize 

931 raise TypeError( 

932 "Cannot convert tz-naive timestamps, use tz_localize to localize" 

933 ) 

934 

935 # No conversion since timestamps are all UTC to begin with 

936 dtype = tz_to_dtype(tz, unit=self.unit) 

937 new_freq = None 

938 if isinstance(self.freq, Tick): 

939 new_freq = self.freq 

940 return self._simple_new(self._ndarray, dtype=dtype, freq=new_freq) 

941 

942 @dtl.ravel_compat 

943 def tz_localize( 

944 self, 

945 tz, 

946 ambiguous: TimeAmbiguous = "raise", 

947 nonexistent: TimeNonexistent = "raise", 

948 ) -> Self: 

949 """ 

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

951 

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

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

954 time zone. 

955 

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

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

958 

959 Parameters 

960 ---------- 

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

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

963 remove the time zone information preserving local time. 

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

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

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

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

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

969 `ambiguous` parameter dictates how ambiguous times should be 

970 handled. 

971 

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

973 order 

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

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

976 ambiguous times) 

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

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

979 times. 

980 

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

982default 'raise' 

983 A nonexistent time does not exist in a particular timezone 

984 where clocks moved forward due to DST. 

985 

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

987 closest existing time 

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

989 closest existing time 

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

991 - timedelta objects will shift nonexistent times by the timedelta 

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

993 nonexistent times. 

994 

995 Returns 

996 ------- 

997 Same type as self 

998 Array/Index converted to the specified time zone. 

999 

1000 Raises 

1001 ------ 

1002 TypeError 

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

1004 

1005 See Also 

1006 -------- 

1007 DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from 

1008 one time zone to another. 

1009 

1010 Examples 

1011 -------- 

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

1013 >>> tz_naive 

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

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

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

1017 

1018 Localize DatetimeIndex in US/Eastern time zone: 

1019 

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

1021 >>> tz_aware 

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

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

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

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

1026 

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

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

1029 

1030 >>> tz_aware.tz_localize(None) 

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

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

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

1034 

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

1036 infer the DST time: 

1037 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1053 dtype: datetime64[us, CET] 

1054 

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

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

1057 

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

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

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

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

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

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

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

1065 dtype: datetime64[us, CET] 

1066 

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

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

1069 or `'shift_backwards'`. 

1070 

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

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

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

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

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

1076 dtype: datetime64[ns, Europe/Warsaw] 

1077 

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

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

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

1081 dtype: datetime64[ns, Europe/Warsaw] 

1082 

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

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

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

1086 dtype: datetime64[ns, Europe/Warsaw] 

1087 """ # noqa: E501 

1088 nonexistent_options = ("raise", "NaT", "shift_forward", "shift_backward") 

1089 if nonexistent not in nonexistent_options and not isinstance( 

1090 nonexistent, timedelta 

1091 ): 

1092 raise ValueError( 

1093 "The nonexistent argument must be one of 'raise', " 

1094 "'NaT', 'shift_forward', 'shift_backward' or " 

1095 "a timedelta object" 

1096 ) 

1097 

1098 if self.tz is not None: 

1099 if tz is None: 

1100 new_dates = tz_convert_from_utc(self.asi8, self.tz, reso=self._creso) 

1101 else: 

1102 raise TypeError("Already tz-aware, use tz_convert to convert.") 

1103 else: 

1104 tz = timezones.maybe_get_tz(tz) 

1105 # Convert to UTC 

1106 

1107 new_dates = tzconversion.tz_localize_to_utc( 

1108 self.asi8, 

1109 tz, 

1110 ambiguous=ambiguous, 

1111 nonexistent=nonexistent, 

1112 creso=self._creso, 

1113 ) 

1114 new_dates_dt64 = new_dates.view(f"M8[{self.unit}]") 

1115 dtype = tz_to_dtype(tz, unit=self.unit) 

1116 

1117 freq = None 

1118 if timezones.is_utc(tz) or (len(self) == 1 and not isna(new_dates_dt64[0])): 

1119 # we can preserve freq 

1120 # TODO: Also for fixed-offsets 

1121 freq = self.freq 

1122 elif tz is None and self.tz is None: 

1123 # no-op 

1124 freq = self.freq 

1125 return self._simple_new(new_dates_dt64, dtype=dtype, freq=freq) 

1126 

1127 # ---------------------------------------------------------------- 

1128 # Conversion Methods - Vectorized analogues of Timestamp methods 

1129 

1130 def to_pydatetime(self) -> npt.NDArray[np.object_]: 

1131 """ 

1132 Return an ndarray of ``datetime.datetime`` objects. 

1133 

1134 Returns 

1135 ------- 

1136 numpy.ndarray 

1137 An ndarray of ``datetime.datetime`` objects. 

1138 

1139 See Also 

1140 -------- 

1141 DatetimeIndex.to_julian_date : Converts Datetime Array to float64 ndarray 

1142 of Julian Dates. 

1143 

1144 Examples 

1145 -------- 

1146 >>> idx = pd.date_range("2018-02-27", periods=3) 

1147 >>> idx.to_pydatetime() 

1148 array([datetime.datetime(2018, 2, 27, 0, 0), 

1149 datetime.datetime(2018, 2, 28, 0, 0), 

1150 datetime.datetime(2018, 3, 1, 0, 0)], dtype=object) 

1151 """ 

1152 return ints_to_pydatetime(self.asi8, tz=self.tz, reso=self._creso) 

1153 

1154 def normalize(self) -> Self: 

1155 """ 

1156 Convert times to midnight. 

1157 

1158 The time component of the date-time is converted to midnight i.e. 

1159 00:00:00. This is useful in cases, when the time does not matter. 

1160 Length is unaltered. The timezones are unaffected. 

1161 

1162 This method is available on Series with datetime values under 

1163 the ``.dt`` accessor, and directly on Datetime Array/Index. 

1164 

1165 Returns 

1166 ------- 

1167 DatetimeArray, DatetimeIndex or Series 

1168 The same type as the original data. Series will have the same 

1169 name and index. DatetimeIndex will have the same name. 

1170 

1171 See Also 

1172 -------- 

1173 floor : Floor the datetimes to the specified freq. 

1174 ceil : Ceil the datetimes to the specified freq. 

1175 round : Round the datetimes to the specified freq. 

1176 

1177 Examples 

1178 -------- 

1179 >>> idx = pd.date_range( 

1180 ... start="2014-08-01 10:00", freq="h", periods=3, tz="Asia/Calcutta" 

1181 ... ) 

1182 >>> idx 

1183 DatetimeIndex(['2014-08-01 10:00:00+05:30', 

1184 '2014-08-01 11:00:00+05:30', 

1185 '2014-08-01 12:00:00+05:30'], 

1186 dtype='datetime64[us, Asia/Calcutta]', freq='h') 

1187 >>> idx.normalize() 

1188 DatetimeIndex(['2014-08-01 00:00:00+05:30', 

1189 '2014-08-01 00:00:00+05:30', 

1190 '2014-08-01 00:00:00+05:30'], 

1191 dtype='datetime64[us, Asia/Calcutta]', freq=None) 

1192 """ 

1193 new_values = normalize_i8_timestamps(self.asi8, self.tz, reso=self._creso) 

1194 dt64_values = new_values.view(self._ndarray.dtype) 

1195 

1196 dta = type(self)._simple_new(dt64_values, dtype=dt64_values.dtype) 

1197 dta = dta._with_freq("infer") 

1198 if self.tz is not None: 

1199 dta = dta.tz_localize(self.tz) 

1200 return dta 

1201 

1202 def to_period(self, freq=None) -> PeriodArray: 

1203 """ 

1204 Cast to PeriodArray/PeriodIndex at a particular frequency. 

1205 

1206 Converts DatetimeArray/Index to PeriodArray/PeriodIndex. 

1207 

1208 Parameters 

1209 ---------- 

1210 freq : str or Period, optional 

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

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

1213 

1214 Returns 

1215 ------- 

1216 PeriodArray/PeriodIndex 

1217 Immutable ndarray holding ordinal values at a particular frequency. 

1218 

1219 Raises 

1220 ------ 

1221 ValueError 

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

1223 so that a frequency cannot be inferred. 

1224 

1225 See Also 

1226 -------- 

1227 PeriodIndex: Immutable ndarray holding ordinal values. 

1228 DatetimeIndex.to_pydatetime: Return DatetimeIndex as object. 

1229 

1230 Examples 

1231 -------- 

1232 >>> df = pd.DataFrame( 

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

1234 ... index=pd.to_datetime( 

1235 ... [ 

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

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

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

1239 ... ] 

1240 ... ), 

1241 ... ) 

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

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

1244 dtype='period[M]') 

1245 

1246 Infer the daily frequency 

1247 

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

1249 >>> idx.to_period() 

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

1251 dtype='period[D]') 

1252 """ 

1253 from pandas.core.arrays import PeriodArray 

1254 

1255 if self.tz is not None: 

1256 warnings.warn( 

1257 "Converting to PeriodArray/Index representation " 

1258 "will drop timezone information.", 

1259 UserWarning, 

1260 stacklevel=find_stack_level(), 

1261 ) 

1262 

1263 if freq is None: 

1264 freq = self.freqstr or self.inferred_freq 

1265 if isinstance(self.freq, BaseOffset) and hasattr( 

1266 self.freq, "_period_dtype_code" 

1267 ): 

1268 freq = PeriodDtype(self.freq)._freqstr 

1269 

1270 if freq is None: 

1271 raise ValueError( 

1272 "You must pass a freq argument as current index has none." 

1273 ) 

1274 

1275 res = get_period_alias(freq) 

1276 

1277 # https://github.com/pandas-dev/pandas/issues/33358 

1278 if res is None: 

1279 res = freq 

1280 

1281 freq = res 

1282 return PeriodArray._from_datetime64(self._ndarray, freq, tz=self.tz) 

1283 

1284 # ----------------------------------------------------------------- 

1285 # Properties - Vectorized Timestamp Properties/Methods 

1286 

1287 def month_name(self, locale=None) -> npt.NDArray[np.object_]: 

1288 """ 

1289 Return the month names with specified locale. 

1290 

1291 Parameters 

1292 ---------- 

1293 locale : str, optional 

1294 Locale determining the language in which to return the month name. 

1295 Default is English locale (``'en_US.utf8'``). Use the command 

1296 ``locale -a`` on your terminal on Unix systems to find your locale 

1297 language code. 

1298 

1299 Returns 

1300 ------- 

1301 Series or Index 

1302 Series or Index of month names. 

1303 

1304 See Also 

1305 -------- 

1306 DatetimeIndex.day_name : Return the day names with specified locale. 

1307 

1308 Examples 

1309 -------- 

1310 >>> s = pd.Series(pd.date_range(start="2018-01", freq="ME", periods=3)) 

1311 >>> s 

1312 0 2018-01-31 

1313 1 2018-02-28 

1314 2 2018-03-31 

1315 dtype: datetime64[us] 

1316 >>> s.dt.month_name() 

1317 0 January 

1318 1 February 

1319 2 March 

1320 dtype: str 

1321 

1322 >>> idx = pd.date_range(start="2018-01", freq="ME", periods=3) 

1323 >>> idx 

1324 DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31'], 

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

1326 >>> idx.month_name() 

1327 Index(['January', 'February', 'March'], dtype='str') 

1328 

1329 Using the ``locale`` parameter you can set a different locale language, 

1330 for example: ``idx.month_name(locale='pt_BR.utf8')`` will return month 

1331 names in Brazilian Portuguese language. 

1332 

1333 >>> idx = pd.date_range(start="2018-01", freq="ME", periods=3) 

1334 >>> idx 

1335 DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31'], 

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

1337 >>> idx.month_name(locale="pt_BR.utf8") # doctest: +SKIP 

1338 Index(['Janeiro', 'Fevereiro', 'Março'], dtype='str') 

1339 """ 

1340 values = self._local_timestamps() 

1341 

1342 result = fields.get_date_name_field( 

1343 values, "month_name", locale=locale, reso=self._creso 

1344 ) 

1345 result = self._maybe_mask_results(result, fill_value=None) 

1346 if using_string_dtype(): 

1347 from pandas import ( 

1348 StringDtype, 

1349 array as pd_array, 

1350 ) 

1351 

1352 return pd_array(result, dtype=StringDtype(na_value=np.nan)) # type: ignore[return-value] 

1353 return result 

1354 

1355 def day_name(self, locale=None) -> npt.NDArray[np.object_]: 

1356 """ 

1357 Return the day names with specified locale. 

1358 

1359 Parameters 

1360 ---------- 

1361 locale : str, optional 

1362 Locale determining the language in which to return the day name. 

1363 Default is English locale (``'en_US.utf8'``). Use the command 

1364 ``locale -a`` on your terminal on Unix systems to find your locale 

1365 language code. 

1366 

1367 Returns 

1368 ------- 

1369 Series or Index 

1370 Series or Index of day names. 

1371 

1372 See Also 

1373 -------- 

1374 DatetimeIndex.month_name : Return the month names with specified locale. 

1375 

1376 Examples 

1377 -------- 

1378 >>> s = pd.Series(pd.date_range(start="2018-01-01", freq="D", periods=3)) 

1379 >>> s 

1380 0 2018-01-01 

1381 1 2018-01-02 

1382 2 2018-01-03 

1383 dtype: datetime64[us] 

1384 >>> s.dt.day_name() 

1385 0 Monday 

1386 1 Tuesday 

1387 2 Wednesday 

1388 dtype: str 

1389 

1390 >>> idx = pd.date_range(start="2018-01-01", freq="D", periods=3) 

1391 >>> idx 

1392 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03'], 

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

1394 >>> idx.day_name() 

1395 Index(['Monday', 'Tuesday', 'Wednesday'], dtype='str') 

1396 

1397 Using the ``locale`` parameter you can set a different locale language, 

1398 for example: ``idx.day_name(locale='pt_BR.utf8')`` will return day 

1399 names in Brazilian Portuguese language. 

1400 

1401 >>> idx = pd.date_range(start="2018-01-01", freq="D", periods=3) 

1402 >>> idx 

1403 DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03'], 

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

1405 >>> idx.day_name(locale="pt_BR.utf8") # doctest: +SKIP 

1406 Index(['Segunda', 'Terça', 'Quarta'], dtype='str') 

1407 """ 

1408 values = self._local_timestamps() 

1409 

1410 result = fields.get_date_name_field( 

1411 values, "day_name", locale=locale, reso=self._creso 

1412 ) 

1413 result = self._maybe_mask_results(result, fill_value=None) 

1414 if using_string_dtype(): 

1415 # TODO: no tests that check for dtype of result as of 2024-08-15 

1416 from pandas import ( 

1417 StringDtype, 

1418 array as pd_array, 

1419 ) 

1420 

1421 return pd_array(result, dtype=StringDtype(na_value=np.nan)) # type: ignore[return-value] 

1422 return result 

1423 

1424 @property 

1425 def time(self) -> npt.NDArray[np.object_]: 

1426 """ 

1427 Returns numpy array of :class:`datetime.time` objects. 

1428 

1429 The time part of the Timestamps. 

1430 

1431 See Also 

1432 -------- 

1433 DatetimeIndex.timetz : Returns numpy array of :class:`datetime.time` 

1434 objects with timezones. The time part of the Timestamps. 

1435 DatetimeIndex.date : Returns numpy array of python :class:`datetime.date` 

1436 objects. Namely, the date part of Timestamps without time and timezone 

1437 information. 

1438 

1439 Examples 

1440 -------- 

1441 For Series: 

1442 

1443 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

1444 >>> s = pd.to_datetime(s) 

1445 >>> s 

1446 0 2020-01-01 10:00:00+00:00 

1447 1 2020-02-01 11:00:00+00:00 

1448 dtype: datetime64[us, UTC] 

1449 >>> s.dt.time 

1450 0 10:00:00 

1451 1 11:00:00 

1452 dtype: object 

1453 

1454 For DatetimeIndex: 

1455 

1456 >>> idx = pd.DatetimeIndex( 

1457 ... ["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"] 

1458 ... ) 

1459 >>> idx.time 

1460 array([datetime.time(10, 0), datetime.time(11, 0)], dtype=object) 

1461 """ 

1462 # If the Timestamps have a timezone that is not UTC, 

1463 # convert them into their i8 representation while 

1464 # keeping their timezone and not using UTC 

1465 timestamps = self._local_timestamps() 

1466 

1467 return ints_to_pydatetime(timestamps, box="time", reso=self._creso) 

1468 

1469 @property 

1470 def timetz(self) -> npt.NDArray[np.object_]: 

1471 """ 

1472 Returns numpy array of :class:`datetime.time` objects with timezones. 

1473 

1474 The time part of the Timestamps. 

1475 

1476 See Also 

1477 -------- 

1478 DatetimeIndex.time : Returns numpy array of :class:`datetime.time` objects. 

1479 The time part of the Timestamps. 

1480 DatetimeIndex.tz : Return the timezone. 

1481 

1482 Examples 

1483 -------- 

1484 For Series: 

1485 

1486 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

1487 >>> s = pd.to_datetime(s) 

1488 >>> s 

1489 0 2020-01-01 10:00:00+00:00 

1490 1 2020-02-01 11:00:00+00:00 

1491 dtype: datetime64[us, UTC] 

1492 >>> s.dt.timetz 

1493 0 10:00:00+00:00 

1494 1 11:00:00+00:00 

1495 dtype: object 

1496 

1497 For DatetimeIndex: 

1498 

1499 >>> idx = pd.DatetimeIndex( 

1500 ... ["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"] 

1501 ... ) 

1502 >>> idx.timetz 

1503 array([datetime.time(10, 0, tzinfo=datetime.timezone.utc), 

1504 datetime.time(11, 0, tzinfo=datetime.timezone.utc)], dtype=object) 

1505 """ 

1506 return ints_to_pydatetime(self.asi8, self.tz, box="time", reso=self._creso) 

1507 

1508 @property 

1509 def date(self) -> npt.NDArray[np.object_]: 

1510 """ 

1511 Returns numpy array of python :class:`datetime.date` objects. 

1512 

1513 Namely, the date part of Timestamps without time and 

1514 timezone information. 

1515 

1516 See Also 

1517 -------- 

1518 DatetimeIndex.time : Returns numpy array of :class:`datetime.time` objects. 

1519 The time part of the Timestamps. 

1520 DatetimeIndex.year : The year of the datetime. 

1521 DatetimeIndex.month : The month as January=1, December=12. 

1522 DatetimeIndex.day : The day of the datetime. 

1523 

1524 Examples 

1525 -------- 

1526 For Series: 

1527 

1528 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

1529 >>> s = pd.to_datetime(s) 

1530 >>> s 

1531 0 2020-01-01 10:00:00+00:00 

1532 1 2020-02-01 11:00:00+00:00 

1533 dtype: datetime64[us, UTC] 

1534 >>> s.dt.date 

1535 0 2020-01-01 

1536 1 2020-02-01 

1537 dtype: object 

1538 

1539 For DatetimeIndex: 

1540 

1541 >>> idx = pd.DatetimeIndex( 

1542 ... ["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"] 

1543 ... ) 

1544 >>> idx.date 

1545 array([datetime.date(2020, 1, 1), datetime.date(2020, 2, 1)], dtype=object) 

1546 """ 

1547 # If the Timestamps have a timezone that is not UTC, 

1548 # convert them into their i8 representation while 

1549 # keeping their timezone and not using UTC 

1550 timestamps = self._local_timestamps() 

1551 

1552 return ints_to_pydatetime(timestamps, box="date", reso=self._creso) 

1553 

1554 def isocalendar(self) -> DataFrame: 

1555 """ 

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

1557 

1558 Returns 

1559 ------- 

1560 DataFrame 

1561 With columns year, week and day. 

1562 

1563 See Also 

1564 -------- 

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

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

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

1568 three components: year, week and weekday. 

1569 

1570 Examples 

1571 -------- 

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

1573 >>> idx.isocalendar() 

1574 year week day 

1575 2019-12-29 2019 52 7 

1576 2019-12-30 2020 1 1 

1577 2019-12-31 2020 1 2 

1578 2020-01-01 2020 1 3 

1579 >>> idx.isocalendar().week 

1580 2019-12-29 52 

1581 2019-12-30 1 

1582 2019-12-31 1 

1583 2020-01-01 1 

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

1585 """ 

1586 from pandas import DataFrame 

1587 

1588 values = self._local_timestamps() 

1589 sarray = fields.build_isocalendar_sarray(values, reso=self._creso) 

1590 iso_calendar_df = DataFrame( 

1591 sarray, columns=["year", "week", "day"], dtype="UInt32" 

1592 ) 

1593 if self._hasna: 

1594 iso_calendar_df.iloc[self._isnan] = None 

1595 return iso_calendar_df 

1596 

1597 year = _field_accessor( 

1598 "year", 

1599 "Y", 

1600 """ 

1601 The year of the datetime. 

1602 

1603 See Also 

1604 -------- 

1605 DatetimeIndex.month: The month as January=1, December=12. 

1606 DatetimeIndex.day: The day of the datetime. 

1607 

1608 Examples 

1609 -------- 

1610 >>> datetime_series = pd.Series( 

1611 ... pd.date_range("2000-01-01", periods=3, freq="YE") 

1612 ... ) 

1613 >>> datetime_series 

1614 0 2000-12-31 

1615 1 2001-12-31 

1616 2 2002-12-31 

1617 dtype: datetime64[us] 

1618 >>> datetime_series.dt.year 

1619 0 2000 

1620 1 2001 

1621 2 2002 

1622 dtype: int32 

1623 """, 

1624 ) 

1625 month = _field_accessor( 

1626 "month", 

1627 "M", 

1628 """ 

1629 The month as January=1, December=12. 

1630 

1631 See Also 

1632 -------- 

1633 DatetimeIndex.year: The year of the datetime. 

1634 DatetimeIndex.day: The day of the datetime. 

1635 

1636 Examples 

1637 -------- 

1638 >>> datetime_series = pd.Series( 

1639 ... pd.date_range("2000-01-01", periods=3, freq="ME") 

1640 ... ) 

1641 >>> datetime_series 

1642 0 2000-01-31 

1643 1 2000-02-29 

1644 2 2000-03-31 

1645 dtype: datetime64[us] 

1646 >>> datetime_series.dt.month 

1647 0 1 

1648 1 2 

1649 2 3 

1650 dtype: int32 

1651 """, 

1652 ) 

1653 day = _field_accessor( 

1654 "day", 

1655 "D", 

1656 """ 

1657 The day of the datetime. 

1658 

1659 See Also 

1660 -------- 

1661 DatetimeIndex.year: The year of the datetime. 

1662 DatetimeIndex.month: The month as January=1, December=12. 

1663 DatetimeIndex.hour: The hours of the datetime. 

1664 

1665 Examples 

1666 -------- 

1667 >>> datetime_series = pd.Series( 

1668 ... pd.date_range("2000-01-01", periods=3, freq="D") 

1669 ... ) 

1670 >>> datetime_series 

1671 0 2000-01-01 

1672 1 2000-01-02 

1673 2 2000-01-03 

1674 dtype: datetime64[us] 

1675 >>> datetime_series.dt.day 

1676 0 1 

1677 1 2 

1678 2 3 

1679 dtype: int32 

1680 """, 

1681 ) 

1682 hour = _field_accessor( 

1683 "hour", 

1684 "h", 

1685 """ 

1686 The hours of the datetime. 

1687 

1688 See Also 

1689 -------- 

1690 DatetimeIndex.day: The day of the datetime. 

1691 DatetimeIndex.minute: The minutes of the datetime. 

1692 DatetimeIndex.second: The seconds of the datetime. 

1693 

1694 Examples 

1695 -------- 

1696 >>> datetime_series = pd.Series( 

1697 ... pd.date_range("2000-01-01", periods=3, freq="h") 

1698 ... ) 

1699 >>> datetime_series 

1700 0 2000-01-01 00:00:00 

1701 1 2000-01-01 01:00:00 

1702 2 2000-01-01 02:00:00 

1703 dtype: datetime64[us] 

1704 >>> datetime_series.dt.hour 

1705 0 0 

1706 1 1 

1707 2 2 

1708 dtype: int32 

1709 """, 

1710 ) 

1711 minute = _field_accessor( 

1712 "minute", 

1713 "m", 

1714 """ 

1715 The minutes of the datetime. 

1716 

1717 See Also 

1718 -------- 

1719 DatetimeIndex.hour: The hours of the datetime. 

1720 DatetimeIndex.second: The seconds of the datetime. 

1721 

1722 Examples 

1723 -------- 

1724 >>> datetime_series = pd.Series( 

1725 ... pd.date_range("2000-01-01", periods=3, freq="min") 

1726 ... ) 

1727 >>> datetime_series 

1728 0 2000-01-01 00:00:00 

1729 1 2000-01-01 00:01:00 

1730 2 2000-01-01 00:02:00 

1731 dtype: datetime64[us] 

1732 >>> datetime_series.dt.minute 

1733 0 0 

1734 1 1 

1735 2 2 

1736 dtype: int32 

1737 """, 

1738 ) 

1739 second = _field_accessor( 

1740 "second", 

1741 "s", 

1742 """ 

1743 The seconds of the datetime. 

1744 

1745 See Also 

1746 -------- 

1747 DatetimeIndex.minute: The minutes of the datetime. 

1748 DatetimeIndex.microsecond: The microseconds of the datetime. 

1749 DatetimeIndex.nanosecond: The nanoseconds of the datetime. 

1750 

1751 Examples 

1752 -------- 

1753 >>> datetime_series = pd.Series( 

1754 ... pd.date_range("2000-01-01", periods=3, freq="s") 

1755 ... ) 

1756 >>> datetime_series 

1757 0 2000-01-01 00:00:00 

1758 1 2000-01-01 00:00:01 

1759 2 2000-01-01 00:00:02 

1760 dtype: datetime64[us] 

1761 >>> datetime_series.dt.second 

1762 0 0 

1763 1 1 

1764 2 2 

1765 dtype: int32 

1766 """, 

1767 ) 

1768 microsecond = _field_accessor( 

1769 "microsecond", 

1770 "us", 

1771 """ 

1772 The microseconds of the datetime. 

1773 

1774 See Also 

1775 -------- 

1776 DatetimeIndex.second: The seconds of the datetime. 

1777 DatetimeIndex.nanosecond: The nanoseconds of the datetime. 

1778 

1779 Examples 

1780 -------- 

1781 >>> datetime_series = pd.Series( 

1782 ... pd.date_range("2000-01-01", periods=3, freq="us") 

1783 ... ) 

1784 >>> datetime_series 

1785 0 2000-01-01 00:00:00.000000 

1786 1 2000-01-01 00:00:00.000001 

1787 2 2000-01-01 00:00:00.000002 

1788 dtype: datetime64[us] 

1789 >>> datetime_series.dt.microsecond 

1790 0 0 

1791 1 1 

1792 2 2 

1793 dtype: int32 

1794 """, 

1795 ) 

1796 nanosecond = _field_accessor( 

1797 "nanosecond", 

1798 "ns", 

1799 """ 

1800 The nanoseconds of the datetime. 

1801 

1802 See Also 

1803 -------- 

1804 DatetimeIndex.second: The seconds of the datetime. 

1805 DatetimeIndex.microsecond: The microseconds of the datetime. 

1806 

1807 Examples 

1808 -------- 

1809 >>> datetime_series = pd.Series( 

1810 ... pd.date_range("2000-01-01", periods=3, freq="ns") 

1811 ... ) 

1812 >>> datetime_series 

1813 0 2000-01-01 00:00:00.000000000 

1814 1 2000-01-01 00:00:00.000000001 

1815 2 2000-01-01 00:00:00.000000002 

1816 dtype: datetime64[ns] 

1817 >>> datetime_series.dt.nanosecond 

1818 0 0 

1819 1 1 

1820 2 2 

1821 dtype: int32 

1822 """, 

1823 ) 

1824 _dayofweek_doc = """ 

1825 The day of the week with Monday=0, Sunday=6. 

1826 

1827 Return the day of the week. It is assumed the week starts on 

1828 Monday, which is denoted by 0 and ends on Sunday which is denoted 

1829 by 6. This method is available on both Series with datetime 

1830 values (using the `dt` accessor) or DatetimeIndex. 

1831 

1832 Returns 

1833 ------- 

1834 Series or Index 

1835 Containing integers indicating the day number. 

1836 

1837 See Also 

1838 -------- 

1839 Series.dt.dayofweek : Alias. 

1840 Series.dt.weekday : Alias. 

1841 Series.dt.day_name : Returns the name of the day of the week. 

1842 

1843 Examples 

1844 -------- 

1845 >>> s = pd.date_range('2016-12-31', '2017-01-08', freq='D').to_series() 

1846 >>> s.dt.dayofweek 

1847 2016-12-31 5 

1848 2017-01-01 6 

1849 2017-01-02 0 

1850 2017-01-03 1 

1851 2017-01-04 2 

1852 2017-01-05 3 

1853 2017-01-06 4 

1854 2017-01-07 5 

1855 2017-01-08 6 

1856 Freq: D, dtype: int32 

1857 """ 

1858 day_of_week = _field_accessor("day_of_week", "dow", _dayofweek_doc) 

1859 dayofweek = day_of_week 

1860 weekday = day_of_week 

1861 

1862 day_of_year = _field_accessor( 

1863 "dayofyear", 

1864 "doy", 

1865 """ 

1866 The ordinal day of the year. 

1867 

1868 See Also 

1869 -------- 

1870 DatetimeIndex.dayofweek : The day of the week with Monday=0, Sunday=6. 

1871 DatetimeIndex.day : The day of the datetime. 

1872 

1873 Examples 

1874 -------- 

1875 For Series: 

1876 

1877 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

1878 >>> s = pd.to_datetime(s) 

1879 >>> s 

1880 0 2020-01-01 10:00:00+00:00 

1881 1 2020-02-01 11:00:00+00:00 

1882 dtype: datetime64[us, UTC] 

1883 >>> s.dt.dayofyear 

1884 0 1 

1885 1 32 

1886 dtype: int32 

1887 

1888 For DatetimeIndex: 

1889 

1890 >>> idx = pd.DatetimeIndex(["1/1/2020 10:00:00+00:00", 

1891 ... "2/1/2020 11:00:00+00:00"]) 

1892 >>> idx.dayofyear 

1893 Index([1, 32], dtype='int32') 

1894 """, 

1895 ) 

1896 dayofyear = day_of_year 

1897 quarter = _field_accessor( 

1898 "quarter", 

1899 "q", 

1900 """ 

1901 The quarter of the date. 

1902 

1903 See Also 

1904 -------- 

1905 DatetimeIndex.snap : Snap time stamps to nearest occurring frequency. 

1906 DatetimeIndex.time : Returns numpy array of datetime.time objects. 

1907 The time part of the Timestamps. 

1908 

1909 Examples 

1910 -------- 

1911 For Series: 

1912 

1913 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "4/1/2020 11:00:00+00:00"]) 

1914 >>> s = pd.to_datetime(s) 

1915 >>> s 

1916 0 2020-01-01 10:00:00+00:00 

1917 1 2020-04-01 11:00:00+00:00 

1918 dtype: datetime64[us, UTC] 

1919 >>> s.dt.quarter 

1920 0 1 

1921 1 2 

1922 dtype: int32 

1923 

1924 For DatetimeIndex: 

1925 

1926 >>> idx = pd.DatetimeIndex(["1/1/2020 10:00:00+00:00", 

1927 ... "2/1/2020 11:00:00+00:00"]) 

1928 >>> idx.quarter 

1929 Index([1, 1], dtype='int32') 

1930 """, 

1931 ) 

1932 days_in_month = _field_accessor( 

1933 "days_in_month", 

1934 "dim", 

1935 """ 

1936 The number of days in the month. 

1937 

1938 See Also 

1939 -------- 

1940 Series.dt.day : Return the day of the month. 

1941 Series.dt.is_month_end : Return a boolean indicating if the 

1942 date is the last day of the month. 

1943 Series.dt.is_month_start : Return a boolean indicating if the 

1944 date is the first day of the month. 

1945 Series.dt.month : Return the month as January=1 through December=12. 

1946 

1947 Examples 

1948 -------- 

1949 >>> s = pd.Series(["1/1/2020 10:00:00+00:00", "2/1/2020 11:00:00+00:00"]) 

1950 >>> s = pd.to_datetime(s) 

1951 >>> s 

1952 0 2020-01-01 10:00:00+00:00 

1953 1 2020-02-01 11:00:00+00:00 

1954 dtype: datetime64[us, UTC] 

1955 >>> s.dt.daysinmonth 

1956 0 31 

1957 1 29 

1958 dtype: int32 

1959 """, 

1960 ) 

1961 daysinmonth = days_in_month 

1962 _is_month_doc = """ 

1963 Indicates whether the date is the {first_or_last} day of the month. 

1964 

1965 Returns 

1966 ------- 

1967 Series or array 

1968 For Series, returns a Series with boolean values. 

1969 For DatetimeIndex, returns a boolean array. 

1970 

1971 See Also 

1972 -------- 

1973 is_month_start : Return a boolean indicating whether the date 

1974 is the first day of the month. 

1975 is_month_end : Return a boolean indicating whether the date 

1976 is the last day of the month. 

1977 

1978 Examples 

1979 -------- 

1980 This method is available on Series with datetime values under 

1981 the ``.dt`` accessor, and directly on DatetimeIndex. 

1982 

1983 >>> s = pd.Series(pd.date_range("2018-02-27", periods=3)) 

1984 >>> s 

1985 0 2018-02-27 

1986 1 2018-02-28 

1987 2 2018-03-01 

1988 dtype: datetime64[us] 

1989 >>> s.dt.is_month_start 

1990 0 False 

1991 1 False 

1992 2 True 

1993 dtype: bool 

1994 >>> s.dt.is_month_end 

1995 0 False 

1996 1 True 

1997 2 False 

1998 dtype: bool 

1999 

2000 >>> idx = pd.date_range("2018-02-27", periods=3) 

2001 >>> idx.is_month_start 

2002 array([False, False, True]) 

2003 >>> idx.is_month_end 

2004 array([False, True, False]) 

2005 """ 

2006 is_month_start = _field_accessor( 

2007 "is_month_start", "is_month_start", _is_month_doc.format(first_or_last="first") 

2008 ) 

2009 

2010 is_month_end = _field_accessor( 

2011 "is_month_end", "is_month_end", _is_month_doc.format(first_or_last="last") 

2012 ) 

2013 

2014 is_quarter_start = _field_accessor( 

2015 "is_quarter_start", 

2016 "is_quarter_start", 

2017 """ 

2018 Indicator for whether the date is the first day of a quarter. 

2019 

2020 Returns 

2021 ------- 

2022 is_quarter_start : Series or DatetimeIndex 

2023 The same type as the original data with boolean values. Series will 

2024 have the same name and index. DatetimeIndex will have the same 

2025 name. 

2026 

2027 See Also 

2028 -------- 

2029 quarter : Return the quarter of the date. 

2030 is_quarter_end : Similar property for indicating the quarter end. 

2031 

2032 Examples 

2033 -------- 

2034 This method is available on Series with datetime values under 

2035 the ``.dt`` accessor, and directly on DatetimeIndex. 

2036 

2037 >>> df = pd.DataFrame({'dates': pd.date_range("2017-03-30", 

2038 ... periods=4)}) 

2039 >>> df.assign(quarter=df.dates.dt.quarter, 

2040 ... is_quarter_start=df.dates.dt.is_quarter_start) 

2041 dates quarter is_quarter_start 

2042 0 2017-03-30 1 False 

2043 1 2017-03-31 1 False 

2044 2 2017-04-01 2 True 

2045 3 2017-04-02 2 False 

2046 

2047 >>> idx = pd.date_range('2017-03-30', periods=4) 

2048 >>> idx 

2049 DatetimeIndex(['2017-03-30', '2017-03-31', '2017-04-01', '2017-04-02'], 

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

2051 

2052 >>> idx.is_quarter_start 

2053 array([False, False, True, False]) 

2054 """, 

2055 ) 

2056 is_quarter_end = _field_accessor( 

2057 "is_quarter_end", 

2058 "is_quarter_end", 

2059 """ 

2060 Indicator for whether the date is the last day of a quarter. 

2061 

2062 Returns 

2063 ------- 

2064 is_quarter_end : Series or DatetimeIndex 

2065 The same type as the original data with boolean values. Series will 

2066 have the same name and index. DatetimeIndex will have the same 

2067 name. 

2068 

2069 See Also 

2070 -------- 

2071 quarter : Return the quarter of the date. 

2072 is_quarter_start : Similar property indicating the quarter start. 

2073 

2074 Examples 

2075 -------- 

2076 This method is available on Series with datetime values under 

2077 the ``.dt`` accessor, and directly on DatetimeIndex. 

2078 

2079 >>> df = pd.DataFrame({'dates': pd.date_range("2017-03-30", 

2080 ... periods=4)}) 

2081 >>> df.assign(quarter=df.dates.dt.quarter, 

2082 ... is_quarter_end=df.dates.dt.is_quarter_end) 

2083 dates quarter is_quarter_end 

2084 0 2017-03-30 1 False 

2085 1 2017-03-31 1 True 

2086 2 2017-04-01 2 False 

2087 3 2017-04-02 2 False 

2088 

2089 >>> idx = pd.date_range('2017-03-30', periods=4) 

2090 >>> idx 

2091 DatetimeIndex(['2017-03-30', '2017-03-31', '2017-04-01', '2017-04-02'], 

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

2093 

2094 >>> idx.is_quarter_end 

2095 array([False, True, False, False]) 

2096 """, 

2097 ) 

2098 is_year_start = _field_accessor( 

2099 "is_year_start", 

2100 "is_year_start", 

2101 """ 

2102 Indicate whether the date is the first day of a year. 

2103 

2104 Returns 

2105 ------- 

2106 Series or DatetimeIndex 

2107 The same type as the original data with boolean values. Series will 

2108 have the same name and index. DatetimeIndex will have the same 

2109 name. 

2110 

2111 See Also 

2112 -------- 

2113 is_year_end : Similar property indicating the last day of the year. 

2114 

2115 Examples 

2116 -------- 

2117 This method is available on Series with datetime values under 

2118 the ``.dt`` accessor, and directly on DatetimeIndex. 

2119 

2120 >>> dates = pd.Series(pd.date_range("2017-12-30", periods=3)) 

2121 >>> dates 

2122 0 2017-12-30 

2123 1 2017-12-31 

2124 2 2018-01-01 

2125 dtype: datetime64[us] 

2126 

2127 >>> dates.dt.is_year_start 

2128 0 False 

2129 1 False 

2130 2 True 

2131 dtype: bool 

2132 

2133 >>> idx = pd.date_range("2017-12-30", periods=3) 

2134 >>> idx 

2135 DatetimeIndex(['2017-12-30', '2017-12-31', '2018-01-01'], 

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

2137 

2138 >>> idx.is_year_start 

2139 array([False, False, True]) 

2140 

2141 This method, when applied to Series with datetime values under 

2142 the ``.dt`` accessor, will lose information about Business offsets. 

2143 

2144 >>> dates = pd.Series(pd.date_range("2020-10-30", periods=4, freq="BYS")) 

2145 >>> dates 

2146 0 2021-01-01 

2147 1 2022-01-03 

2148 2 2023-01-02 

2149 3 2024-01-01 

2150 dtype: datetime64[us] 

2151 

2152 >>> dates.dt.is_year_start 

2153 0 True 

2154 1 False 

2155 2 False 

2156 3 True 

2157 dtype: bool 

2158 

2159 >>> idx = pd.date_range("2020-10-30", periods=4, freq="BYS") 

2160 >>> idx 

2161 DatetimeIndex(['2021-01-01', '2022-01-03', '2023-01-02', '2024-01-01'], 

2162 dtype='datetime64[us]', freq='BYS-JAN') 

2163 

2164 >>> idx.is_year_start 

2165 array([ True, True, True, True]) 

2166 """, 

2167 ) 

2168 is_year_end = _field_accessor( 

2169 "is_year_end", 

2170 "is_year_end", 

2171 """ 

2172 Indicate whether the date is the last day of the year. 

2173 

2174 Returns 

2175 ------- 

2176 Series or DatetimeIndex 

2177 The same type as the original data with boolean values. Series will 

2178 have the same name and index. DatetimeIndex will have the same 

2179 name. 

2180 

2181 See Also 

2182 -------- 

2183 is_year_start : Similar property indicating the start of the year. 

2184 

2185 Examples 

2186 -------- 

2187 This method is available on Series with datetime values under 

2188 the ``.dt`` accessor, and directly on DatetimeIndex. 

2189 

2190 >>> dates = pd.Series(pd.date_range("2017-12-30", periods=3)) 

2191 >>> dates 

2192 0 2017-12-30 

2193 1 2017-12-31 

2194 2 2018-01-01 

2195 dtype: datetime64[us] 

2196 

2197 >>> dates.dt.is_year_end 

2198 0 False 

2199 1 True 

2200 2 False 

2201 dtype: bool 

2202 

2203 >>> idx = pd.date_range("2017-12-30", periods=3) 

2204 >>> idx 

2205 DatetimeIndex(['2017-12-30', '2017-12-31', '2018-01-01'], 

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

2207 

2208 >>> idx.is_year_end 

2209 array([False, True, False]) 

2210 """, 

2211 ) 

2212 is_leap_year = _field_accessor( 

2213 "is_leap_year", 

2214 "is_leap_year", 

2215 """ 

2216 Boolean indicator if the date belongs to a leap year. 

2217 

2218 A leap year is a year, which has 366 days (instead of 365) including 

2219 29th of February as an intercalary day. 

2220 Leap years are years which are multiples of four with the exception 

2221 of years divisible by 100 but not by 400. 

2222 

2223 Returns 

2224 ------- 

2225 Series or ndarray 

2226 Booleans indicating if dates belong to a leap year. 

2227 

2228 See Also 

2229 -------- 

2230 DatetimeIndex.is_year_end : Indicate whether the date is the 

2231 last day of the year. 

2232 DatetimeIndex.is_year_start : Indicate whether the date is the first 

2233 day of a year. 

2234 

2235 Examples 

2236 -------- 

2237 This method is available on Series with datetime values under 

2238 the ``.dt`` accessor, and directly on DatetimeIndex. 

2239 

2240 >>> idx = pd.date_range("2012-01-01", "2015-01-01", freq="YE") 

2241 >>> idx 

2242 DatetimeIndex(['2012-12-31', '2013-12-31', '2014-12-31'], 

2243 dtype='datetime64[us]', freq='YE-DEC') 

2244 >>> idx.is_leap_year 

2245 array([ True, False, False]) 

2246 

2247 >>> dates_series = pd.Series(idx) 

2248 >>> dates_series 

2249 0 2012-12-31 

2250 1 2013-12-31 

2251 2 2014-12-31 

2252 dtype: datetime64[us] 

2253 >>> dates_series.dt.is_leap_year 

2254 0 True 

2255 1 False 

2256 2 False 

2257 dtype: bool 

2258 """, 

2259 ) 

2260 

2261 def to_julian_date(self) -> npt.NDArray[np.float64]: 

2262 """ 

2263 Convert TimeStamp to a Julian Date. 

2264 

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

2266 

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

2268 

2269 Returns 

2270 ------- 

2271 ndarray or Index 

2272 Float values that represent each date in Julian Calendar. 

2273 

2274 See Also 

2275 -------- 

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

2277 

2278 Examples 

2279 -------- 

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

2281 >>> idx.to_julian_date() 

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

2283 """ 

2284 

2285 # http://mysite.verizon.net/aesir_research/date/jdalg2.htm 

2286 year = np.asarray(self.year) 

2287 month = np.asarray(self.month) 

2288 day = np.asarray(self.day) 

2289 testarr = month < 3 

2290 year[testarr] -= 1 

2291 month[testarr] += 12 

2292 return ( 

2293 day 

2294 + np.trunc((153 * month - 457) / 5) 

2295 + 365 * year 

2296 + np.floor(year / 4) 

2297 - np.floor(year / 100) 

2298 + np.floor(year / 400) 

2299 + 1_721_118.5 

2300 + ( 

2301 self.hour 

2302 + self.minute / 60 

2303 + self.second / 3600 

2304 + self.microsecond / 3600 / 10**6 

2305 + self.nanosecond / 3600 / 10**9 

2306 ) 

2307 / 24 

2308 ) 

2309 

2310 # ----------------------------------------------------------------- 

2311 # Reductions 

2312 

2313 def _reduce( 

2314 self, name: str, *, skipna: bool = True, keepdims: bool = False, **kwargs 

2315 ): 

2316 result = super()._reduce(name, skipna=skipna, keepdims=keepdims, **kwargs) 

2317 if keepdims and isinstance(result, np.ndarray): 

2318 if name == "std": 

2319 from pandas.core.arrays import TimedeltaArray 

2320 

2321 return TimedeltaArray._from_sequence(result) 

2322 else: 

2323 return self._from_sequence(result, dtype=self.dtype) 

2324 return result 

2325 

2326 def std( 

2327 self, 

2328 axis=None, 

2329 dtype=None, 

2330 out=None, 

2331 ddof: int = 1, 

2332 keepdims: bool = False, 

2333 skipna: bool = True, 

2334 ) -> Timedelta: 

2335 """ 

2336 Return sample standard deviation over requested axis. 

2337 

2338 Normalized by `N-1` by default. This can be changed using ``ddof``. 

2339 

2340 Parameters 

2341 ---------- 

2342 axis : int, optional 

2343 Axis for the function to be applied on. For :class:`pandas.Series` 

2344 this parameter is unused and defaults to ``None``. 

2345 dtype : dtype, optional, default None 

2346 Type to use in computing the standard deviation. For arrays of 

2347 integer type the default is float64, for arrays of float types 

2348 it is the same as the array type. 

2349 out : ndarray, optional, default None 

2350 Alternative output array in which to place the result. It must have 

2351 the same shape as the expected output but the type (of the 

2352 calculated values) will be cast if necessary. 

2353 ddof : int, default 1 

2354 Degrees of Freedom. The divisor used in calculations is `N - ddof`, 

2355 where `N` represents the number of elements. 

2356 keepdims : bool, optional 

2357 If this is set to True, the axes which are reduced are left in the 

2358 result as dimensions with size one. With this option, the result 

2359 will broadcast correctly against the input array. If the default 

2360 value is passed, then keepdims will not be passed through to the 

2361 std method of sub-classes of ndarray, however any non-default value 

2362 will be. If the sub-class method does not implement keepdims any 

2363 exceptions will be raised. 

2364 skipna : bool, default True 

2365 Exclude NA/null values. If an entire row/column is ``NA``, the result 

2366 will be ``NA``. 

2367 

2368 Returns 

2369 ------- 

2370 Timedelta 

2371 Standard deviation over requested axis. 

2372 

2373 See Also 

2374 -------- 

2375 numpy.ndarray.std : Returns the standard deviation of the array elements 

2376 along given axis. 

2377 Series.std : Return sample standard deviation over requested axis. 

2378 

2379 Examples 

2380 -------- 

2381 For :class:`pandas.DatetimeIndex`: 

2382 

2383 >>> idx = pd.date_range("2001-01-01 00:00", periods=3) 

2384 >>> idx 

2385 DatetimeIndex(['2001-01-01', '2001-01-02', '2001-01-03'], 

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

2387 >>> idx.std() 

2388 Timedelta('1 days 00:00:00') 

2389 """ 

2390 # Because std is translation-invariant, we can get self.std 

2391 # by calculating (self - Timestamp(0)).std, and we can do it 

2392 # without creating a copy by using a view on self._ndarray 

2393 from pandas.core.arrays import TimedeltaArray 

2394 

2395 # Find the td64 dtype with the same resolution as our dt64 dtype 

2396 dtype_str = self._ndarray.dtype.name.replace("datetime64", "timedelta64") 

2397 dtype = np.dtype(dtype_str) 

2398 

2399 tda = TimedeltaArray._simple_new(self._ndarray.view(dtype), dtype=dtype) 

2400 

2401 return tda.std(axis=axis, out=out, ddof=ddof, keepdims=keepdims, skipna=skipna) 

2402 

2403 

2404# ------------------------------------------------------------------- 

2405# Constructor Helpers 

2406 

2407 

2408def _sequence_to_dt64( 

2409 data: ArrayLike, 

2410 *, 

2411 copy: bool = False, 

2412 tz: tzinfo | None = None, 

2413 dayfirst: bool = False, 

2414 yearfirst: bool = False, 

2415 ambiguous: TimeAmbiguous = "raise", 

2416 out_unit: str | None = None, 

2417) -> tuple[np.ndarray, tzinfo | None]: 

2418 """ 

2419 Parameters 

2420 ---------- 

2421 data : np.ndarray or ExtensionArray 

2422 dtl.ensure_arraylike_for_datetimelike has already been called. 

2423 copy : bool, default False 

2424 tz : tzinfo or None, default None 

2425 dayfirst : bool, default False 

2426 yearfirst : bool, default False 

2427 ambiguous : str, bool, or arraylike, default 'raise' 

2428 See pandas._libs.tslibs.tzconversion.tz_localize_to_utc. 

2429 out_unit : str or None, default None 

2430 Desired output resolution. 

2431 

2432 Returns 

2433 ------- 

2434 result : numpy.ndarray 

2435 The sequence converted to a numpy array with dtype ``datetime64[unit]``. 

2436 Where `unit` is "ns" unless specified otherwise by `out_unit`. 

2437 tz : tzinfo or None 

2438 Either the user-provided tzinfo or one inferred from the data. 

2439 

2440 Raises 

2441 ------ 

2442 TypeError : PeriodDType data is passed 

2443 """ 

2444 

2445 # By this point we are assured to have either a numpy array or Index 

2446 data, copy = maybe_convert_dtype(data, copy, tz=tz) 

2447 data_dtype = getattr(data, "dtype", None) 

2448 

2449 out_dtype = DT64NS_DTYPE 

2450 if out_unit is not None: 

2451 out_dtype = np.dtype(f"M8[{out_unit}]") 

2452 

2453 if data_dtype == object or is_string_dtype(data_dtype): 

2454 # TODO: We do not have tests specific to string-dtypes, 

2455 # also complex or categorical or other extension 

2456 data = cast(np.ndarray, data) 

2457 copy = False 

2458 if lib.infer_dtype(data, skipna=False) == "integer": 

2459 # Much more performant than going through array_to_datetime 

2460 data = data.astype(np.int64) 

2461 elif tz is not None and ambiguous == "raise": 

2462 obj_data = np.asarray(data, dtype=object) 

2463 result = tslib.array_to_datetime_with_tz( 

2464 obj_data, 

2465 tz=tz, 

2466 dayfirst=dayfirst, 

2467 yearfirst=yearfirst, 

2468 creso=abbrev_to_npy_unit(out_unit), 

2469 ) 

2470 return result, tz 

2471 else: 

2472 converted, inferred_tz = objects_to_datetime64( 

2473 data, 

2474 dayfirst=dayfirst, 

2475 yearfirst=yearfirst, 

2476 allow_object=False, 

2477 out_unit=out_unit, 

2478 ) 

2479 copy = False 

2480 if tz and inferred_tz: 

2481 # two timezones: convert to intended from base UTC repr 

2482 # GH#42505 by convention, these are _already_ UTC 

2483 result = converted 

2484 

2485 elif inferred_tz: 

2486 tz = inferred_tz 

2487 result = converted 

2488 

2489 else: 

2490 result, _ = _construct_from_dt64_naive( 

2491 converted, tz=tz, copy=copy, ambiguous=ambiguous 

2492 ) 

2493 return result, tz 

2494 

2495 data_dtype = data.dtype 

2496 

2497 # `data` may have originally been a Categorical[datetime64[ns, tz]], 

2498 # so we need to handle these types. 

2499 if isinstance(data_dtype, DatetimeTZDtype): 

2500 # DatetimeArray -> ndarray 

2501 data = cast(DatetimeArray, data) 

2502 tz = _maybe_infer_tz(tz, data.tz) 

2503 result = data._ndarray 

2504 

2505 elif lib.is_np_dtype(data_dtype, "M"): 

2506 # tz-naive DatetimeArray or ndarray[datetime64] 

2507 if isinstance(data, DatetimeArray): 

2508 data = data._ndarray 

2509 

2510 data = cast(np.ndarray, data) 

2511 result, copy = _construct_from_dt64_naive( 

2512 data, tz=tz, copy=copy, ambiguous=ambiguous 

2513 ) 

2514 

2515 else: 

2516 # must be integer dtype otherwise 

2517 # assume this data are epoch timestamps 

2518 if data.dtype != INT64_DTYPE: 

2519 data = data.astype(np.int64, copy=False) 

2520 copy = False 

2521 data = cast(np.ndarray, data) 

2522 result = data.view(out_dtype) 

2523 

2524 if copy: 

2525 result = result.copy() 

2526 

2527 assert isinstance(result, np.ndarray), type(result) 

2528 assert result.dtype.kind == "M" 

2529 assert result.dtype != "M8" 

2530 assert is_supported_dtype(result.dtype) 

2531 return result, tz 

2532 

2533 

2534def _construct_from_dt64_naive( 

2535 data: np.ndarray, *, tz: tzinfo | None, copy: bool, ambiguous: TimeAmbiguous 

2536) -> tuple[np.ndarray, bool]: 

2537 """ 

2538 Convert datetime64 data to a supported dtype, localizing if necessary. 

2539 """ 

2540 # Caller is responsible for ensuring 

2541 # lib.is_np_dtype(data.dtype) 

2542 

2543 new_dtype = data.dtype 

2544 if not is_supported_dtype(new_dtype): 

2545 # Cast to the nearest supported unit, generally "s" 

2546 new_dtype = get_supported_dtype(new_dtype) 

2547 data = astype_overflowsafe(data, dtype=new_dtype, copy=False) 

2548 copy = False 

2549 

2550 if data.dtype.byteorder == ">": 

2551 # TODO: better way to handle this? non-copying alternative? 

2552 # without this, test_constructor_datetime64_bigendian fails 

2553 data = data.astype(data.dtype.newbyteorder("<")) 

2554 new_dtype = data.dtype 

2555 copy = False 

2556 

2557 if tz is not None: 

2558 # Convert tz-naive to UTC 

2559 # TODO: if tz is UTC, are there situations where we *don't* want a 

2560 # copy? tz_localize_to_utc always makes one. 

2561 shape = data.shape 

2562 if data.ndim > 1: 

2563 data = data.ravel() 

2564 

2565 data_unit = get_unit_from_dtype(new_dtype) 

2566 data = tzconversion.tz_localize_to_utc( 

2567 data.view("i8"), tz, ambiguous=ambiguous, creso=data_unit 

2568 ) 

2569 data = data.view(new_dtype) 

2570 data = data.reshape(shape) 

2571 

2572 assert data.dtype == new_dtype, data.dtype 

2573 result = data 

2574 

2575 return result, copy 

2576 

2577 

2578def objects_to_datetime64( 

2579 data: np.ndarray, 

2580 dayfirst, 

2581 yearfirst, 

2582 utc: bool = False, 

2583 errors: DateTimeErrorChoices = "raise", 

2584 allow_object: bool = False, 

2585 out_unit: str | None = None, 

2586) -> tuple[np.ndarray, tzinfo | None]: 

2587 """ 

2588 Convert data to array of timestamps. 

2589 

2590 Parameters 

2591 ---------- 

2592 data : np.ndarray[object] 

2593 dayfirst : bool 

2594 yearfirst : bool 

2595 utc : bool, default False 

2596 Whether to convert/localize timestamps to UTC. 

2597 errors : {'raise', 'coerce'} 

2598 allow_object : bool 

2599 Whether to return an object-dtype ndarray instead of raising if the 

2600 data contains more than one timezone. 

2601 out_unit : str or None, default None 

2602 None indicates we should do resolution inference. 

2603 

2604 Returns 

2605 ------- 

2606 result : ndarray 

2607 np.datetime64[out_unit] if returned values represent wall times or UTC 

2608 timestamps. 

2609 object if mixed timezones 

2610 inferred_tz : tzinfo or None 

2611 If not None, then the datetime64 values in `result` denote UTC timestamps. 

2612 

2613 Raises 

2614 ------ 

2615 ValueError : if data cannot be converted to datetimes 

2616 TypeError : When a type cannot be converted to datetime 

2617 """ 

2618 assert errors in ["raise", "coerce"] 

2619 

2620 # if str-dtype, convert 

2621 data = np.asarray(data, dtype=np.object_) 

2622 

2623 result, tz_parsed = tslib.array_to_datetime( 

2624 data, 

2625 errors=errors, 

2626 utc=utc, 

2627 dayfirst=dayfirst, 

2628 yearfirst=yearfirst, 

2629 creso=abbrev_to_npy_unit(out_unit), 

2630 ) 

2631 

2632 if tz_parsed is not None: 

2633 # We can take a shortcut since the datetime64 numpy array 

2634 # is in UTC 

2635 return result, tz_parsed 

2636 elif result.dtype.kind == "M": 

2637 return result, tz_parsed 

2638 elif result.dtype == object: 

2639 # GH#23675 when called via `pd.to_datetime`, returning an object-dtype 

2640 # array is allowed. When called via `pd.DatetimeIndex`, we can 

2641 # only accept datetime64 dtype, so raise TypeError if object-dtype 

2642 # is returned, as that indicates the values can be recognized as 

2643 # datetimes but they have conflicting timezones/awareness 

2644 if allow_object: 

2645 return result, tz_parsed 

2646 raise TypeError("DatetimeIndex has mixed timezones") 

2647 else: # pragma: no cover 

2648 # GH#23675 this TypeError should never be hit, whereas the TypeError 

2649 # in the object-dtype branch above is reachable. 

2650 raise TypeError(result) 

2651 

2652 

2653def maybe_convert_dtype(data, copy: bool, tz: tzinfo | None = None): 

2654 """ 

2655 Convert data based on dtype conventions, issuing 

2656 errors where appropriate. 

2657 

2658 Parameters 

2659 ---------- 

2660 data : np.ndarray or pd.Index 

2661 copy : bool 

2662 tz : tzinfo or None, default None 

2663 

2664 Returns 

2665 ------- 

2666 data : np.ndarray or pd.Index 

2667 copy : bool 

2668 

2669 Raises 

2670 ------ 

2671 TypeError : PeriodDType data is passed 

2672 """ 

2673 if not hasattr(data, "dtype"): 

2674 # e.g. collections.deque 

2675 return data, copy 

2676 

2677 if is_float_dtype(data.dtype): 

2678 # pre-2.0 we treated these as wall-times, inconsistent with ints 

2679 # GH#23675, GH#45573 deprecated to treat symmetrically with integer dtypes. 

2680 # Note: data.astype(np.int64) fails ARM tests, see 

2681 # https://github.com/pandas-dev/pandas/issues/49468. 

2682 data = data.astype(DT64NS_DTYPE).view("i8") 

2683 copy = False 

2684 

2685 elif lib.is_np_dtype(data.dtype, "m") or is_bool_dtype(data.dtype): 

2686 # GH#29794 enforcing deprecation introduced in GH#23539 

2687 raise TypeError(f"dtype {data.dtype} cannot be converted to datetime64[ns]") 

2688 elif isinstance(data.dtype, PeriodDtype): 

2689 # Note: without explicitly raising here, PeriodIndex 

2690 # test_setops.test_join_does_not_recur fails 

2691 raise TypeError( 

2692 "Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead" 

2693 ) 

2694 

2695 elif isinstance(data.dtype, ExtensionDtype) and not isinstance( 

2696 data.dtype, DatetimeTZDtype 

2697 ): 

2698 # TODO: We have no tests for these 

2699 data = np.array(data, dtype=np.object_) 

2700 copy = False 

2701 

2702 return data, copy 

2703 

2704 

2705# ------------------------------------------------------------------- 

2706# Validation and Inference 

2707 

2708 

2709def _maybe_infer_tz(tz: tzinfo | None, inferred_tz: tzinfo | None) -> tzinfo | None: 

2710 """ 

2711 If a timezone is inferred from data, check that it is compatible with 

2712 the user-provided timezone, if any. 

2713 

2714 Parameters 

2715 ---------- 

2716 tz : tzinfo or None 

2717 inferred_tz : tzinfo or None 

2718 

2719 Returns 

2720 ------- 

2721 tz : tzinfo or None 

2722 

2723 Raises 

2724 ------ 

2725 TypeError : if both timezones are present but do not match 

2726 """ 

2727 if tz is None: 

2728 tz = inferred_tz 

2729 elif inferred_tz is None: 

2730 pass 

2731 elif not timezones.tz_compare(tz, inferred_tz): 

2732 raise TypeError( 

2733 f"data is already tz-aware {inferred_tz}, unable to set specified tz: {tz}" 

2734 ) 

2735 return tz 

2736 

2737 

2738def _validate_dt64_dtype(dtype): 

2739 """ 

2740 Check that a dtype, if passed, represents either a numpy datetime64[ns] 

2741 dtype or a pandas DatetimeTZDtype. 

2742 

2743 Parameters 

2744 ---------- 

2745 dtype : object 

2746 

2747 Returns 

2748 ------- 

2749 dtype : None, numpy.dtype, or DatetimeTZDtype 

2750 

2751 Raises 

2752 ------ 

2753 ValueError : invalid dtype 

2754 

2755 Notes 

2756 ----- 

2757 Unlike _validate_tz_from_dtype, this does _not_ allow non-existent 

2758 tz errors to go through 

2759 """ 

2760 if dtype is not None: 

2761 dtype = pandas_dtype(dtype) 

2762 if dtype == np.dtype("M8"): 

2763 # no precision, disallowed GH#24806 

2764 msg = ( 

2765 "Passing in 'datetime64' dtype with no precision is not allowed. " 

2766 "Please pass in 'datetime64[ns]' instead." 

2767 ) 

2768 raise ValueError(msg) 

2769 

2770 if ( 

2771 isinstance(dtype, np.dtype) 

2772 and (dtype.kind != "M" or not is_supported_dtype(dtype)) 

2773 ) or not isinstance(dtype, (np.dtype, DatetimeTZDtype)): 

2774 raise ValueError( 

2775 f"Unexpected value for 'dtype': '{dtype}'. " 

2776 "Must be 'datetime64[s]', 'datetime64[ms]', 'datetime64[us]', " 

2777 "'datetime64[ns]' or DatetimeTZDtype'." 

2778 ) 

2779 

2780 if getattr(dtype, "tz", None): 

2781 # https://github.com/pandas-dev/pandas/issues/18595 

2782 # Ensure that we have a standard timezone for pytz objects. 

2783 # Without this, things like adding an array of timedeltas and 

2784 # a tz-aware Timestamp (with a tz specific to its datetime) will 

2785 # be incorrect(ish?) for the array as a whole 

2786 dtype = cast(DatetimeTZDtype, dtype) 

2787 dtype = DatetimeTZDtype( 

2788 unit=dtype.unit, tz=timezones.tz_standardize(dtype.tz) 

2789 ) 

2790 

2791 return dtype 

2792 

2793 

2794def _validate_tz_from_dtype( 

2795 dtype, tz: tzinfo | None, explicit_tz_none: bool = False 

2796) -> tzinfo | None: 

2797 """ 

2798 If the given dtype is a DatetimeTZDtype, extract the implied 

2799 tzinfo object from it and check that it does not conflict with the given 

2800 tz. 

2801 

2802 Parameters 

2803 ---------- 

2804 dtype : dtype, str 

2805 tz : None, tzinfo 

2806 explicit_tz_none : bool, default False 

2807 Whether tz=None was passed explicitly, as opposed to lib.no_default. 

2808 

2809 Returns 

2810 ------- 

2811 tz : consensus tzinfo 

2812 

2813 Raises 

2814 ------ 

2815 ValueError : on tzinfo mismatch 

2816 """ 

2817 if dtype is not None: 

2818 if isinstance(dtype, str): 

2819 try: 

2820 dtype = DatetimeTZDtype.construct_from_string(dtype) 

2821 except TypeError: 

2822 # Things like `datetime64[ns]`, which is OK for the 

2823 # constructors, but also nonsense, which should be validated 

2824 # but not by us. We *do* allow non-existent tz errors to 

2825 # go through 

2826 pass 

2827 dtz = getattr(dtype, "tz", None) 

2828 if dtz is not None: 

2829 if tz is not None and not timezones.tz_compare(tz, dtz): 

2830 raise ValueError("cannot supply both a tz and a dtype with a tz") 

2831 if explicit_tz_none: 

2832 raise ValueError("Cannot pass both a timezone-aware dtype and tz=None") 

2833 tz = dtz 

2834 

2835 if tz is not None and lib.is_np_dtype(dtype, "M"): 

2836 # We also need to check for the case where the user passed a 

2837 # tz-naive dtype (i.e. datetime64[ns]) 

2838 if tz is not None and not timezones.tz_compare(tz, dtz): 

2839 raise ValueError( 

2840 "cannot supply both a tz and a " 

2841 "timezone-naive dtype (i.e. datetime64[ns])" 

2842 ) 

2843 

2844 return tz 

2845 

2846 

2847def _infer_tz_from_endpoints( 

2848 start: Timestamp, end: Timestamp, tz: tzinfo | None 

2849) -> tzinfo | None: 

2850 """ 

2851 If a timezone is not explicitly given via `tz`, see if one can 

2852 be inferred from the `start` and `end` endpoints. If more than one 

2853 of these inputs provides a timezone, require that they all agree. 

2854 

2855 Parameters 

2856 ---------- 

2857 start : Timestamp 

2858 end : Timestamp 

2859 tz : tzinfo or None 

2860 

2861 Returns 

2862 ------- 

2863 tz : tzinfo or None 

2864 

2865 Raises 

2866 ------ 

2867 TypeError : if start and end timezones do not agree 

2868 """ 

2869 try: 

2870 inferred_tz = timezones.infer_tzinfo(start, end) 

2871 except AssertionError as err: 

2872 # infer_tzinfo raises AssertionError if passed mismatched timezones 

2873 raise TypeError( 

2874 "Start and end cannot both be tz-aware with different timezones" 

2875 ) from err 

2876 

2877 inferred_tz = timezones.maybe_get_tz(inferred_tz) 

2878 tz = timezones.maybe_get_tz(tz) 

2879 

2880 if tz is not None and inferred_tz is not None: 

2881 if not timezones.tz_compare(inferred_tz, tz): 

2882 raise AssertionError("Inferred time zone not equal to passed time zone") 

2883 

2884 elif inferred_tz is not None: 

2885 tz = inferred_tz 

2886 

2887 return tz 

2888 

2889 

2890def _maybe_normalize_endpoints( 

2891 start: _TimestampNoneT1, end: _TimestampNoneT2, normalize: bool 

2892) -> tuple[_TimestampNoneT1, _TimestampNoneT2]: 

2893 if normalize: 

2894 if start is not None: 

2895 start = start.normalize() 

2896 

2897 if end is not None: 

2898 end = end.normalize() 

2899 

2900 return start, end 

2901 

2902 

2903def _maybe_localize_point( 

2904 ts: Timestamp | None, freq, tz, ambiguous, nonexistent 

2905) -> Timestamp | None: 

2906 """ 

2907 Localize a start or end Timestamp to the timezone of the corresponding 

2908 start or end Timestamp 

2909 

2910 Parameters 

2911 ---------- 

2912 ts : start or end Timestamp to potentially localize 

2913 freq : Tick, DateOffset, or None 

2914 tz : str, timezone object or None 

2915 ambiguous: str, localization behavior for ambiguous times 

2916 nonexistent: str, localization behavior for nonexistent times 

2917 

2918 Returns 

2919 ------- 

2920 ts : Timestamp 

2921 """ 

2922 # Make sure start and end are timezone localized if: 

2923 # 1) freq = a Timedelta-like frequency (Tick) 

2924 # 2) freq = None i.e. generating a linspaced range 

2925 if ts is not None and ts.tzinfo is None: 

2926 # Note: We can't ambiguous='infer' a singular ambiguous time; however, 

2927 # we have historically defaulted ambiguous=False 

2928 ambiguous = ambiguous if ambiguous != "infer" else False 

2929 localize_args = {"ambiguous": ambiguous, "nonexistent": nonexistent, "tz": None} 

2930 if isinstance(freq, Tick) or freq is None: 

2931 localize_args["tz"] = tz 

2932 ts = ts.tz_localize(**localize_args) 

2933 return ts 

2934 

2935 

2936def _generate_range( 

2937 start: Timestamp | None, 

2938 end: Timestamp | None, 

2939 periods: int | None, 

2940 offset: BaseOffset, 

2941 *, 

2942 unit: TimeUnit, 

2943) -> Generator[Timestamp]: 

2944 """ 

2945 Generates a sequence of dates corresponding to the specified time 

2946 offset. Similar to dateutil.rrule except uses pandas DateOffset 

2947 objects to represent time increments. 

2948 

2949 Parameters 

2950 ---------- 

2951 start : Timestamp or None 

2952 end : Timestamp or None 

2953 periods : int or None 

2954 offset : DateOffset 

2955 unit : str 

2956 

2957 Notes 

2958 ----- 

2959 * This method is faster for generating weekdays than dateutil.rrule 

2960 * At least two of (start, end, periods) must be specified. 

2961 * If both start and end are specified, the returned dates will 

2962 satisfy start <= date <= end. 

2963 

2964 Returns 

2965 ------- 

2966 dates : generator object 

2967 """ 

2968 offset = to_offset(offset) 

2969 

2970 # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]"; 

2971 # expected "Union[integer[Any], float, str, date, datetime64]" 

2972 start = Timestamp(start) # type: ignore[arg-type] 

2973 if start is not NaT: 

2974 start = start.as_unit(unit) 

2975 else: 

2976 start = None 

2977 

2978 # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]"; 

2979 # expected "Union[integer[Any], float, str, date, datetime64]" 

2980 end = Timestamp(end) # type: ignore[arg-type] 

2981 if end is not NaT: 

2982 end = end.as_unit(unit) 

2983 else: 

2984 end = None 

2985 

2986 # GH #64834 FIX for bdate_range regression 

2987 if end is not None and periods is not None and not offset.is_on_offset(end): 

2988 if offset.n >= 0: 

2989 end = offset.rollback(end) # type: ignore[assignment] 

2990 else: 

2991 end = offset.rollforward(end) # type: ignore[assignment] 

2992 

2993 if start and not offset.is_on_offset(start): 

2994 # Incompatible types in assignment (expression has type "datetime", 

2995 # variable has type "Optional[Timestamp]") 

2996 

2997 # GH #56147 account for negative direction and range bounds 

2998 if offset.n >= 0: 

2999 start = offset.rollforward(start) # type: ignore[assignment] 

3000 else: 

3001 start = offset.rollback(start) # type: ignore[assignment] 

3002 

3003 # Unsupported operand types for < ("Timestamp" and "None") 

3004 if periods is None and end < start and offset.n >= 0: # type: ignore[operator] 

3005 end = None 

3006 periods = 0 

3007 

3008 if end is None: 

3009 # error: No overload variant of "__radd__" of "BaseOffset" matches 

3010 # argument type "None" 

3011 end = start + (periods - 1) * offset # type: ignore[operator] 

3012 

3013 if start is None: 

3014 # error: No overload variant of "__radd__" of "BaseOffset" matches 

3015 # argument type "None" 

3016 start = end - (periods - 1) * offset # type: ignore[operator] 

3017 

3018 start = cast(Timestamp, start) 

3019 end = cast(Timestamp, end) 

3020 

3021 cur = start 

3022 if offset.n >= 0: 

3023 while cur <= end: 

3024 yield cur 

3025 

3026 if cur == end: 

3027 # GH#24252 avoid overflows by not performing the addition 

3028 # in offset.apply unless we have to 

3029 break 

3030 

3031 # faster than cur + offset 

3032 next_date = offset._apply(cur) 

3033 next_date = next_date.as_unit(unit) 

3034 if next_date <= cur: 

3035 raise ValueError(f"Offset {offset} did not increment date") 

3036 cur = next_date 

3037 else: 

3038 while cur >= end: 

3039 yield cur 

3040 

3041 if cur == end: 

3042 # GH#24252 avoid overflows by not performing the addition 

3043 # in offset.apply unless we have to 

3044 break 

3045 

3046 # faster than cur + offset 

3047 next_date = offset._apply(cur) 

3048 next_date = next_date.as_unit(unit) 

3049 if next_date >= cur: 

3050 raise ValueError(f"Offset {offset} did not decrement date") 

3051 cur = next_date