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

686 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 _global_config as config 

21 

22from pandas._libs import ( 

23 lib, 

24 tslib, 

25) 

26from pandas._libs.tslibs import ( 

27 BaseOffset, 

28 NaT, 

29 NaTType, 

30 OutOfBoundsDatetime, 

31 OutOfBoundsTimedelta, 

32 Timedelta, 

33 Timestamp, 

34 astype_overflowsafe, 

35 fields, 

36 get_supported_dtype, 

37 get_unit_from_dtype, 

38 ints_to_pydatetime, 

39 is_date_array_normalized, 

40 is_supported_dtype, 

41 is_unitless, 

42 normalize_i8_timestamps, 

43 timezones, 

44 to_offset, 

45 tz_convert_from_utc, 

46 tzconversion, 

47) 

48from pandas._libs.tslibs.ccalendar import ( 

49 DAYS_FULL, 

50 MONTHS_FULL, 

51) 

52from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit 

53from pandas._libs.tslibs.offsets import RelativeDeltaOffset 

54from pandas.compat.pyarrow import HAS_PYARROW 

55from pandas.errors import ( 

56 Pandas4Warning, 

57 PerformanceWarning, 

58) 

59from pandas.util._decorators import set_module 

60from pandas.util._exceptions import find_stack_level 

61from pandas.util._validators import validate_inclusive 

62 

63from pandas.core.dtypes.common import ( 

64 DT64NS_DTYPE, 

65 INT64_DTYPE, 

66 is_bool_dtype, 

67 is_float_dtype, 

68 is_string_dtype, 

69 pandas_dtype, 

70) 

71from pandas.core.dtypes.dtypes import ( 

72 DatetimeTZDtype, 

73 ExtensionDtype, 

74 PeriodDtype, 

75) 

76 

77from pandas.core.arrays import datetimelike as dtl 

78from pandas.core.arrays._ranges import ( 

79 generate_daily_offset_range, 

80 generate_regular_range, 

81) 

82import pandas.core.common as com 

83 

84from pandas.tseries.frequencies import get_period_alias 

85from pandas.tseries.offsets import ( 

86 Day, 

87 Tick, 

88) 

89 

90if TYPE_CHECKING: 

91 from collections.abc import ( 

92 Callable, 

93 Generator, 

94 ) 

95 

96 import pyarrow as pa 

97 

98 from pandas._typing import ( 

99 ArrayLike, 

100 DateTimeErrorChoices, 

101 DtypeObj, 

102 IntervalClosedType, 

103 TimeAmbiguous, 

104 TimeNonexistent, 

105 TimeUnit, 

106 npt, 

107 ) 

108 

109 from pandas import DataFrame 

110 from pandas.core.arrays import PeriodArray 

111 from pandas.core.arrays.string_arrow import ArrowStringArray 

112 

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

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

115 

116 

117@overload 

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

119 

120 

121@overload 

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

123 

124 

125def tz_to_dtype( 

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

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

128 """ 

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

130 

131 Parameters 

132 ---------- 

133 tz : tzinfo or None 

134 unit : str, default "ns" 

135 

136 Returns 

137 ------- 

138 np.dtype or DatetimeTZDtype 

139 """ 

140 if tz is None: 

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

142 else: 

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

144 

145 

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

147 def f(self): 

148 values = self._local_timestamps() 

149 

150 if field in self._bool_ops: 

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

152 return self._get_start_end_field(field, freq=None) 

153 return fields.get_date_field(values, field, reso=self._creso) 

154 

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

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

157 

158 return result 

159 

160 f.__name__ = name 

161 f.__doc__ = docstring 

162 return property(f) 

163 

164 

165@set_module("pandas.arrays") 

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

167 """ 

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

169 

170 .. warning:: 

171 

172 DatetimeArray is currently experimental, and its API may change 

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

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

175 subclass. 

176 

177 Parameters 

178 ---------- 

179 data : Series, Index, DatetimeArray, ndarray 

180 The datetime data. 

181 

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

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

184 

185 dtype : numpy.dtype or DatetimeTZDtype 

186 Note that the only NumPy dtypes allowed are 'datetime64[ns]', 

187 'datetime64[us]', 'datetime64[ms]', 'datetime64[s]'. 

188 freq : str or Offset, optional 

189 The frequency. 

190 copy : bool, default False 

191 Whether to copy the underlying array of values. 

192 

193 Attributes 

194 ---------- 

195 None 

196 

197 Methods 

198 ------- 

199 None 

200 

201 See Also 

202 -------- 

203 DatetimeIndex : Immutable Index for datetime-like data. 

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

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

206 to_datetime : Convert argument to datetime. 

207 period_range : Return a fixed frequency PeriodIndex. 

208 

209 Examples 

210 -------- 

211 >>> pd.arrays.DatetimeArray._from_sequence( 

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

213 ... ) 

214 <DatetimeArray> 

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

216 Length: 2, dtype: datetime64[us] 

217 """ 

218 

219 _typ = "datetimearray" 

220 _recognized_scalars = (datetime, np.datetime64) 

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

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

223 ) 

224 

225 @property 

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

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

228 

229 @property 

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

231 return Timestamp 

232 

233 # define my properties & methods for delegation 

234 _bool_ops: list[str] = [ 

235 "is_month_start", 

236 "is_month_end", 

237 "is_quarter_start", 

238 "is_quarter_end", 

239 "is_year_start", 

240 "is_year_end", 

241 "is_leap_year", 

242 ] 

243 _field_ops: list[str] = [ 

244 "year", 

245 "month", 

246 "day", 

247 "hour", 

248 "minute", 

249 "second", 

250 "day_of_week", 

251 "day_of_year", 

252 "quarter", 

253 "days_in_month", 

254 "microsecond", 

255 "nanosecond", 

256 ] 

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

258 _datetimelike_ops: list[str] = _field_ops + _bool_ops + _other_ops + ["unit", "tz"] 

259 _datetimelike_methods: list[str] = [ 

260 "to_period", 

261 "tz_localize", 

262 "tz_convert", 

263 "normalize", 

264 "strftime", 

265 "round", 

266 "floor", 

267 "ceil", 

268 "month_name", 

269 "day_name", 

270 "as_unit", 

271 ] 

272 

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

274 # Timestamp.__richcmp__(DateTimeArray) operates pointwise 

275 

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

277 __array_priority__ = 1000 

278 

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

280 # Constructors 

281 

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

283 

284 @classmethod 

285 def _validate_dtype(cls, values, dtype): 

286 # used in TimeLikeOps.__init__ 

287 dtype = _validate_dt64_dtype(dtype) 

288 _validate_dt64_dtype(values.dtype) 

289 if isinstance(dtype, np.dtype): 

290 if values.dtype != dtype: 

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

292 else: 

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

294 if vunit != dtype.unit: 

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

296 return dtype 

297 

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

299 @classmethod 

300 def _simple_new( # type: ignore[override] 

301 cls, 

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

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

304 ) -> Self: 

305 assert isinstance(values, np.ndarray) 

306 assert dtype.kind == "M" 

307 if isinstance(dtype, np.dtype): 

308 assert dtype == values.dtype 

309 assert not is_unitless(dtype) 

310 else: 

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

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

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

314 

315 return super()._simple_new(values, dtype) 

316 

317 @classmethod 

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

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

320 

321 @classmethod 

322 def _from_sequence_not_strict( 

323 cls, 

324 data, 

325 *, 

326 dtype=None, 

327 copy: bool = False, 

328 tz=lib.no_default, 

329 dayfirst: bool = False, 

330 yearfirst: bool = False, 

331 ambiguous: TimeAmbiguous = "raise", 

332 ) -> Self: 

333 """ 

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

335 """ 

336 

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

338 # disallow inferring a tz. 

339 explicit_tz_none = tz is None 

340 if tz is lib.no_default: 

341 tz = None 

342 else: 

343 tz = timezones.maybe_get_tz(tz) 

344 

345 dtype = _validate_dt64_dtype(dtype) 

346 # if dtype has an embedded tz, capture it 

347 tz = _validate_tz_from_dtype(dtype, tz, explicit_tz_none) 

348 

349 unit = None 

350 if dtype is not None: 

351 unit = dtl.dtype_to_unit(dtype) 

352 

353 data, copy = dtl.ensure_arraylike_for_datetimelike( 

354 data, copy, cls_name="DatetimeArray" 

355 ) 

356 

357 subarr, tz = _sequence_to_dt64( 

358 data, 

359 copy=copy, 

360 tz=tz, 

361 dayfirst=dayfirst, 

362 yearfirst=yearfirst, 

363 ambiguous=ambiguous, 

364 out_unit=unit, 

365 ) 

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

367 _validate_tz_from_dtype(dtype, tz, explicit_tz_none) 

368 if tz is not None and explicit_tz_none: 

369 raise ValueError( 

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

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

372 ) 

373 

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

375 data_unit = cast("TimeUnit", data_unit) 

376 data_dtype = tz_to_dtype(tz, data_unit) 

377 result = cls._simple_new(subarr, dtype=data_dtype) 

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

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

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

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

382 # [arg-type] 

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

384 

385 return result 

386 

387 @classmethod 

388 def _generate_range( 

389 cls, 

390 start, 

391 end, 

392 periods: int | None, 

393 freq, 

394 tz=None, 

395 normalize: bool = False, 

396 ambiguous: TimeAmbiguous = "raise", 

397 nonexistent: TimeNonexistent = "raise", 

398 inclusive: IntervalClosedType = "both", 

399 *, 

400 unit: TimeUnit = "ns", 

401 ) -> Self: 

402 periods = dtl.validate_periods(periods) 

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

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

405 

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

407 raise ValueError( 

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

409 "and freq, exactly three must be specified" 

410 ) 

411 freq = to_offset(freq) 

412 

413 if start is not None: 

414 start = Timestamp(start) 

415 

416 if end is not None: 

417 end = Timestamp(end) 

418 

419 if start is NaT or end is NaT: 

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

421 

422 if unit is not None: 

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

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

425 else: 

426 unit = "ns" 

427 

428 if start is not None: 

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

430 if end is not None: 

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

432 

433 left_inclusive, right_inclusive = validate_inclusive(inclusive) 

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

435 tz = _infer_tz_from_endpoints(start, end, tz) 

436 

437 if tz is not None: 

438 # Localize the start and end arguments 

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

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

441 

442 if freq is not None: 

443 # Offset handling: 

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

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

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

447 if not isinstance(freq, Tick): 

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

449 start = start.tz_localize(None) 

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

451 end = end.tz_localize(None) 

452 

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

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

455 elif freq.n >= 1 and freq._supports_daily_offset_mask: 

456 # GH#16463: fast "daily range + filter" path for offsets 

457 # that support vectorized on-offset checks. 

458 try: 

459 i8values = generate_daily_offset_range( 

460 start, end, periods, freq, unit=unit 

461 ) 

462 except (OutOfBoundsDatetime, OutOfBoundsTimedelta, OverflowError): 

463 # Near Timestamp bounds with periods; the iterative 

464 # path handles this by stopping before overflow. 

465 xdr = _generate_range( 

466 start=start, 

467 end=end, 

468 periods=periods, 

469 offset=freq, 

470 unit=unit, 

471 ) 

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

473 else: 

474 xdr = _generate_range( 

475 start=start, 

476 end=end, 

477 periods=periods, 

478 offset=freq, 

479 unit=unit, 

480 ) 

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

482 

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

484 

485 if tz is not None and endpoint_tz is None: 

486 if not timezones.is_utc(tz): 

487 # short-circuit tz_localize_to_utc which would make 

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

489 creso = abbrev_to_npy_unit(unit) 

490 i8values = tzconversion.tz_localize_to_utc( 

491 i8values, 

492 tz, 

493 ambiguous=ambiguous, 

494 nonexistent=nonexistent, 

495 creso=creso, 

496 ) 

497 

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

499 # start/end as well to compare 

500 if start is not None: 

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

502 if end is not None: 

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

504 else: 

505 # Create a linearly spaced date_range in local time 

506 # Nanosecond-granularity timestamps aren't always correctly 

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

508 # pass to np.linspace as much as possible 

509 periods = cast("int", periods) 

510 i8values = ( 

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

512 + start._value 

513 ) 

514 if i8values.dtype != "i8": 

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

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

517 i8values = i8values.astype("i8") 

518 

519 if start == end: 

520 if not left_inclusive or not right_inclusive: 

521 i8values = i8values[1:-1] 

522 else: 

523 start_i8 = ( 

524 Timestamp(start)._value 

525 if start is not None 

526 else i8values[0] 

527 if len(i8values) 

528 else 0 

529 ) 

530 end_i8 = ( 

531 Timestamp(end)._value 

532 if end is not None 

533 else i8values[-1] 

534 if len(i8values) 

535 else 0 

536 ) 

537 if not left_inclusive or not right_inclusive: 

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

539 i8values = i8values[1:] 

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

541 i8values = i8values[:-1] 

542 

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

544 dtype = tz_to_dtype(tz, unit=unit) 

545 return cls._simple_new(dt64_values, dtype=dtype) 

546 

547 # ----------------------------------------------------------------- 

548 # DatetimeLike Interface 

549 

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

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

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

553 self._check_compatible_with(value) 

554 if value is NaT: 

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

556 else: 

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

558 

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

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

561 

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

563 if other is NaT: 

564 return 

565 self._assert_tzawareness_compat(other) 

566 

567 # ----------------------------------------------------------------- 

568 # Descriptive Properties 

569 

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

571 # GH#42228 

572 value = x.view("i8") 

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

574 return ts 

575 

576 @property 

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

578 # incompatible with return type "ExtensionDtype" in supertype 

579 # "ExtensionArray" 

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

581 """ 

582 The dtype for the DatetimeArray. 

583 

584 .. warning:: 

585 

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

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

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

589 

590 Returns 

591 ------- 

592 numpy.dtype or DatetimeTZDtype 

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

594 is returned. 

595 

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

597 is returned. 

598 """ 

599 return self._dtype 

600 

601 @property 

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

603 """ 

604 Return the timezone. 

605 

606 This property returns the timezone information associated with the 

607 datetime data. If the data is timezone-naive (i.e. has no timezone 

608 information), it returns None. 

609 

610 Returns 

611 ------- 

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

613 Returns None when the array is tz-naive. 

614 

615 See Also 

616 -------- 

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

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

619 DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from 

620 one time zone to another. 

621 

622 Examples 

623 -------- 

624 For Series: 

625 

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

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

628 >>> s 

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

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

631 dtype: datetime64[us, UTC] 

632 >>> s.dt.tz 

633 datetime.timezone.utc 

634 

635 For DatetimeIndex: 

636 

637 >>> idx = pd.DatetimeIndex( 

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

639 ... ) 

640 >>> idx.tz 

641 datetime.timezone.utc 

642 """ # noqa: E501 

643 # GH 18595 

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

645 

646 @tz.setter 

647 def tz(self, value): 

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

649 raise AttributeError( 

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

651 "or tz_convert() as appropriate" 

652 ) 

653 

654 @property 

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

656 """ 

657 Alias for tz attribute 

658 """ 

659 return self.tz 

660 

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

662 def is_normalized(self) -> bool: 

663 """ 

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

665 """ 

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

667 

668 # ---------------------------------------------------------------- 

669 # Array-Like / EA-Interface Methods 

670 

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

672 if dtype is None and self.tz: 

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

674 dtype = object 

675 

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

677 

678 def _iter_convert_chunk(self, data: np.ndarray) -> np.ndarray: 

679 return ints_to_pydatetime( 

680 data.view("i8"), tz=self.tz, box="timestamp", reso=self._creso 

681 ) 

682 

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

684 # We handle 

685 # --> datetime 

686 # --> period 

687 # DatetimeLikeArrayMixin Super handles the rest. 

688 dtype = pandas_dtype(dtype) 

689 

690 if dtype == self.dtype: 

691 if copy: 

692 return self.copy() 

693 return self 

694 

695 elif isinstance(dtype, ExtensionDtype): 

696 if not isinstance(dtype, DatetimeTZDtype): 

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

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

699 elif self.tz is None: 

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

701 # the Series behavior which did 

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

703 raise TypeError( 

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

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

706 "series.dt.tz_localize instead" 

707 ) 

708 else: 

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

710 np_dtype = np.dtype(dtype.str) 

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

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

713 

714 elif ( 

715 self.tz is None 

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

717 and not is_unitless(dtype) 

718 and is_supported_dtype(dtype) 

719 ): 

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

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

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

723 # TODO: preserve freq? 

724 

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

726 # pre-2.0 behavior for DTA/DTI was 

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

728 # the Series behavior 

729 raise TypeError( 

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

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

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

733 ) 

734 

735 elif ( 

736 self.tz is None 

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

738 and dtype != self.dtype 

739 and is_unitless(dtype) 

740 ): 

741 raise TypeError( 

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

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

744 ) 

745 

746 elif isinstance(dtype, PeriodDtype): 

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

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

749 

750 # ----------------------------------------------------------------- 

751 # Rendering Methods 

752 

753 def _format_native_types( 

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

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

756 if date_format is None and self._is_dates_only: 

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

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

759 

760 return tslib.format_array_from_datetime( 

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

762 ) 

763 

764 # ----------------------------------------------------------------- 

765 # Comparison Methods 

766 

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

768 # adapted from _Timestamp._assert_tzawareness_compat 

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

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

771 

772 if isinstance(other_dtype, DatetimeTZDtype): 

773 # Get tzinfo from Series dtype 

774 other_tz = other.dtype.tz 

775 if other is NaT: 

776 # pd.NaT quacks both aware and naive 

777 pass 

778 elif self.tz is None: 

779 if other_tz is not None: 

780 raise TypeError( 

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

782 ) 

783 elif other_tz is None: 

784 raise TypeError( 

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

786 ) 

787 

788 # ----------------------------------------------------------------- 

789 # Arithmetic Methods 

790 

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

792 assert not isinstance(offset, Tick) 

793 

794 # For pure-timedelta DateOffset with tz-aware data, add to UTC values 

795 # directly to avoid nonexistent/ambiguous time errors from 

796 # re-localizing wall-time results near DST (GH#28610). 

797 if ( 

798 self.tz is not None 

799 and isinstance(offset, RelativeDeltaOffset) 

800 and not offset._use_relativedelta 

801 ): 

802 # GH#64806 _add_timedeltalike_scalar casts to the finer of the two 

803 # resolutions, so the result keeps offset._pd_timedelta's unit when 

804 # that is finer than self's. 

805 result = self._add_timedeltalike_scalar(offset._pd_timedelta) 

806 if offset.normalize: 

807 result = result.normalize() 

808 return result 

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._add_datetime_ndarray(values._ndarray) 

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

818 # values is tz-naive here, so its dtype is the ndarray's 

819 res_values = res_values.view(values._ndarray.dtype) 

820 except NotImplementedError: 

821 if config["mode"]["performance_warnings"]: 

822 warnings.warn( 

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

824 "DatetimeIndex.", 

825 PerformanceWarning, 

826 stacklevel=find_stack_level(), 

827 ) 

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

829 # GH#56586 use the finer resolution between self and offset.offset 

830 # to avoid truncating sub-unit precision 

831 res_unit = self.unit 

832 if hasattr(offset, "offset") and offset.offset: 

833 offset_unit = Timedelta(offset.offset).unit 

834 if abbrev_to_npy_unit(offset_unit) > abbrev_to_npy_unit(res_unit): 

835 res_unit = offset_unit 

836 dtype = tz_to_dtype(self.tz, unit=res_unit) 

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

838 

839 else: 

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

841 if offset.normalize: 

842 result = result.normalize() 

843 

844 if self.tz is not None: 

845 result = result.tz_localize(self.tz) 

846 

847 return result 

848 

849 # ----------------------------------------------------------------- 

850 # Timezone Conversion and Localization Methods 

851 

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

853 """ 

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

855 while keeping the local timezone and not using UTC. 

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

857 were timezone-naive. 

858 """ 

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

860 # Avoid the copy that would be made in tzconversion 

861 return self.asi8 

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

863 

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

865 """ 

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

867 

868 This method converts datetime values from their current timezone to a 

869 different timezone. The underlying UTC time remains the same, only the 

870 local time representation changes to reflect the target timezone. 

871 

872 Parameters 

873 ---------- 

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

875 Time zone for time. Corresponding timestamps would be converted 

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

877 convert to UTC and remove the timezone information. 

878 

879 Returns 

880 ------- 

881 Array or Index 

882 Datetime Array/Index with target `tz`. 

883 

884 Raises 

885 ------ 

886 TypeError 

887 If Datetime Array/Index is tz-naive. 

888 

889 See Also 

890 -------- 

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

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

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

894 Series.dt.tz : A timezone that has a variable offset from UTC. 

895 Series.dt.tz_localize : Localize tz-naive Series datetimes to a given 

896 time zone, or remove timezone from tz-aware Series datetimes. 

897 

898 Examples 

899 -------- 

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

901 to other time zones: 

902 

903 >>> dti = pd.date_range( 

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

905 ... ) 

906 

907 >>> dti 

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

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

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

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

912 

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

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

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

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

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

918 

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

920 to UTC if necessary): 

921 

922 >>> dti = pd.date_range( 

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

924 ... ) 

925 

926 >>> dti 

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

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

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

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

931 

932 >>> dti.tz_convert(None) 

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

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

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

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

937 """ # noqa: E501 

938 tz = timezones.maybe_get_tz(tz) 

939 

940 if self.tz is None: 

941 # tz naive, use tz_localize 

942 raise TypeError( 

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

944 ) 

945 

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

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

948 return self._simple_new(self._ndarray, dtype=dtype) 

949 

950 @dtl.ravel_compat 

951 def tz_localize( 

952 self, 

953 tz, 

954 ambiguous: TimeAmbiguous = "raise", 

955 nonexistent: TimeNonexistent = "raise", 

956 ) -> Self: 

957 """ 

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

959 

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

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

962 time zone. 

963 

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

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

966 

967 Parameters 

968 ---------- 

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

970 Time zone to attach to the tz-naive timestamps; the wall time is 

971 preserved. Passing ``None`` detaches the time zone from a tz-aware 

972 Array/Index, returning a tz-naive result with the same wall time. 

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

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

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

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

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

978 `ambiguous` parameter dictates how ambiguous times should be 

979 handled. 

980 

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

982 order. Requires that the timestamps are monotonically increasing. 

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

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

985 ambiguous times) 

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

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

988 times. 

989 

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

991default 'raise' 

992 A nonexistent time does not exist in a particular timezone 

993 where clocks moved forward due to DST. 

994 

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

996 closest existing time 

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

998 closest existing time 

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

1000 - timedelta objects will shift nonexistent times by the timedelta 

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

1002 nonexistent times. 

1003 

1004 Returns 

1005 ------- 

1006 Same type as self 

1007 Array/Index converted to the specified time zone. 

1008 

1009 Raises 

1010 ------ 

1011 TypeError 

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

1013 

1014 See Also 

1015 -------- 

1016 DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from 

1017 one time zone to another. 

1018 

1019 Examples 

1020 -------- 

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

1022 >>> tz_naive 

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

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

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

1026 

1027 Localize DatetimeIndex in US/Eastern time zone: 

1028 

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

1030 >>> tz_aware 

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

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

1033 dtype='datetime64[us, US/Eastern]', freq='D') 

1034 

1035 With ``tz=None`` we can remove the time zone information while 

1036 preserving the wall time (no conversion to UTC): 

1037 

1038 >>> tz_aware.tz_localize(None) 

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

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

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

1042 

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

1044 infer the DST time: 

1045 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1061 dtype: datetime64[us, CET] 

1062 

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

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

1065 

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

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

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

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

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

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

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

1073 dtype: datetime64[us, CET] 

1074 

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

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

1077 or `'shift_backward'`. 

1078 

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

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

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

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

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

1084 dtype: datetime64[ns, Europe/Warsaw] 

1085 

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

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

1088 1 2015-03-29 03:30:00.000000000+02:00 

1089 dtype: datetime64[ns, Europe/Warsaw] 

1090 

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

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

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

1094 dtype: datetime64[ns, Europe/Warsaw] 

1095 """ # noqa: E501 

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

1097 if nonexistent not in nonexistent_options and not isinstance( 

1098 nonexistent, timedelta 

1099 ): 

1100 raise ValueError( 

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

1102 "'NaT', 'shift_forward', 'shift_backward' or " 

1103 "a timedelta object" 

1104 ) 

1105 

1106 if self.tz is not None: 

1107 if tz is None: 

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

1109 else: 

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

1111 else: 

1112 tz = timezones.maybe_get_tz(tz) 

1113 # Convert to UTC 

1114 

1115 new_dates = tzconversion.tz_localize_to_utc( 

1116 self.asi8, 

1117 tz, 

1118 ambiguous=ambiguous, 

1119 nonexistent=nonexistent, 

1120 creso=self._creso, 

1121 ) 

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

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

1124 

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

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 This method converts each element in the DatetimeArray to a native 

1135 Python ``datetime.datetime`` object, including timezone information 

1136 if present. 

1137 

1138 Returns 

1139 ------- 

1140 numpy.ndarray 

1141 An ndarray of ``datetime.datetime`` objects. 

1142 

1143 See Also 

1144 -------- 

1145 DatetimeIndex.to_julian_date : Converts Datetime Array to float64 ndarray 

1146 of Julian Dates. 

1147 

1148 Examples 

1149 -------- 

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

1151 >>> idx.to_pydatetime() 

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

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

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

1155 """ 

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

1157 

1158 def normalize(self) -> Self: 

1159 """ 

1160 Convert times to midnight. 

1161 

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

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

1164 Length is unaltered. The timezones are unaffected. 

1165 

1166 This method is available on Series with datetime values under 

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

1168 

1169 Returns 

1170 ------- 

1171 DatetimeArray, DatetimeIndex or Series 

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

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

1174 

1175 See Also 

1176 -------- 

1177 floor : Floor the datetimes to the specified freq. 

1178 ceil : Ceil the datetimes to the specified freq. 

1179 round : Round the datetimes to the specified freq. 

1180 

1181 Examples 

1182 -------- 

1183 >>> idx = pd.date_range( 

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

1185 ... ) 

1186 >>> idx 

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

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

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

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

1191 >>> idx.normalize() 

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

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

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

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

1196 """ 

1197 dta = self._normalize_naive() 

1198 if self.tz is not None: 

1199 dta = dta.tz_localize(self.tz) 

1200 return dta 

1201 

1202 def _normalize_naive(self) -> Self: 

1203 """Normalize times to midnight, ignoring the timezone.""" 

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

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

1206 return type(self)._simple_new(dt64_values, dtype=dt64_values.dtype) 

1207 

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

1209 """ 

1210 Cast to PeriodArray/PeriodIndex at a particular frequency. 

1211 

1212 Converts DatetimeArray/Index to PeriodArray/PeriodIndex. 

1213 

1214 Parameters 

1215 ---------- 

1216 freq : str, optional 

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

1218 Will be inferred by default. 

1219 

1220 Returns 

1221 ------- 

1222 PeriodArray/PeriodIndex 

1223 Immutable ndarray holding ordinal values at a particular frequency. 

1224 

1225 Raises 

1226 ------ 

1227 ValueError 

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

1229 so that a frequency cannot be inferred. 

1230 

1231 See Also 

1232 -------- 

1233 PeriodIndex: Immutable ndarray holding ordinal values. 

1234 DatetimeIndex.to_pydatetime: Return DatetimeIndex as object. 

1235 

1236 Examples 

1237 -------- 

1238 >>> df = pd.DataFrame( 

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

1240 ... index=pd.to_datetime( 

1241 ... [ 

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

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

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

1245 ... ] 

1246 ... ), 

1247 ... ) 

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

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

1250 dtype='period[M]') 

1251 

1252 Infer the daily frequency 

1253 

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

1255 >>> idx.to_period() 

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

1257 dtype='period[D]') 

1258 """ 

1259 from pandas.core.arrays import PeriodArray 

1260 

1261 if self.tz is not None: 

1262 warnings.warn( 

1263 "Converting to PeriodArray/Index representation " 

1264 "will drop timezone information.", 

1265 UserWarning, 

1266 stacklevel=find_stack_level(), 

1267 ) 

1268 

1269 if freq is None: 

1270 freq = self._inferred_freq_str 

1271 

1272 if freq is None: 

1273 raise ValueError( 

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

1275 ) 

1276 

1277 res = get_period_alias(freq) 

1278 

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

1280 if res is None: 

1281 res = freq 

1282 

1283 freq = res 

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

1285 

1286 # ----------------------------------------------------------------- 

1287 # Properties - Vectorized Timestamp Properties/Methods 

1288 

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

1290 """ 

1291 Return the month names with specified locale. 

1292 

1293 This method returns the full name of the month (e.g., "January", "February") 

1294 for each datetime value in the Series/Index. The names can be localized 

1295 to different languages using the locale parameter. 

1296 

1297 Parameters 

1298 ---------- 

1299 locale : str, optional 

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

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

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

1303 language code. 

1304 

1305 Returns 

1306 ------- 

1307 Series or Index 

1308 Series or Index of month names. 

1309 

1310 See Also 

1311 -------- 

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

1313 

1314 Examples 

1315 -------- 

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

1317 >>> s 

1318 0 2018-01-31 

1319 1 2018-02-28 

1320 2 2018-03-31 

1321 dtype: datetime64[us] 

1322 >>> s.dt.month_name() 

1323 0 January 

1324 1 February 

1325 2 March 

1326 dtype: str 

1327 

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

1329 >>> idx 

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

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

1332 >>> idx.month_name() 

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

1334 

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

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

1337 names in Brazilian Portuguese language. 

1338 

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

1340 >>> idx 

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

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

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

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

1345 """ 

1346 if locale is None and HAS_PYARROW and using_string_dtype(): 

1347 return self._month_name_arrow() # type: ignore[return-value] 

1348 

1349 values = self._local_timestamps() 

1350 

1351 result = fields.get_date_name_field( 

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

1353 ) 

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

1355 if using_string_dtype(): 

1356 from pandas import ( 

1357 StringDtype, 

1358 array as pd_array, 

1359 ) 

1360 

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

1362 return result 

1363 

1364 def _month_name_arrow(self) -> ArrowStringArray: 

1365 import pyarrow as pa 

1366 import pyarrow.compute as pc 

1367 

1368 pa_months = pa.array(MONTHS_FULL, type=pa.large_string()) 

1369 pa_ts = self._to_local_pa_timestamp() 

1370 result = pc.take(pa_months, pc.month(pa_ts)) 

1371 return self._wrap_pa_str_result(result) 

1372 

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

1374 """ 

1375 Return the day names with specified locale. 

1376 

1377 This method returns the full name of the day of the week (e.g., "Monday", 

1378 "Tuesday") for each datetime value in the Series/Index. The names can be 

1379 localized to different languages using the locale parameter. 

1380 

1381 Parameters 

1382 ---------- 

1383 locale : str, optional 

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

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

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

1387 language code. 

1388 

1389 Returns 

1390 ------- 

1391 Series or Index 

1392 Series or Index of day names. 

1393 

1394 See Also 

1395 -------- 

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

1397 

1398 Examples 

1399 -------- 

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

1401 >>> s 

1402 0 2018-01-01 

1403 1 2018-01-02 

1404 2 2018-01-03 

1405 dtype: datetime64[us] 

1406 >>> s.dt.day_name() 

1407 0 Monday 

1408 1 Tuesday 

1409 2 Wednesday 

1410 dtype: str 

1411 

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

1413 >>> idx 

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

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

1416 >>> idx.day_name() 

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

1418 

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

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

1421 names in Brazilian Portuguese language. 

1422 

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

1424 >>> idx 

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

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

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

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

1429 """ 

1430 if locale is None and HAS_PYARROW and using_string_dtype(): 

1431 return self._day_name_arrow() # type: ignore[return-value] 

1432 

1433 values = self._local_timestamps() 

1434 

1435 result = fields.get_date_name_field( 

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

1437 ) 

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

1439 if using_string_dtype(): 

1440 from pandas import ( 

1441 StringDtype, 

1442 array as pd_array, 

1443 ) 

1444 

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

1446 return result 

1447 

1448 def _day_name_arrow(self) -> ArrowStringArray: 

1449 import pyarrow as pa 

1450 import pyarrow.compute as pc 

1451 

1452 pa_days = pa.array(DAYS_FULL, type=pa.large_string()) 

1453 pa_ts = self._to_local_pa_timestamp() 

1454 result = pc.take(pa_days, pc.day_of_week(pa_ts)) 

1455 return self._wrap_pa_str_result(result) 

1456 

1457 def _to_local_pa_timestamp(self) -> pa.Array: 

1458 """Convert to a pyarrow TimestampArray with local timestamps.""" 

1459 import pyarrow as pa 

1460 

1461 values = self._local_timestamps() 

1462 mask = self._isnan 

1463 return pa.array(values, mask=mask, type=pa.timestamp(self.unit)) 

1464 

1465 @staticmethod 

1466 def _wrap_pa_str_result(pa_arr: pa.Array) -> ArrowStringArray: 

1467 """Wrap a pyarrow StringArray in an ArrowStringArray with StringDtype.""" 

1468 from pandas.core.arrays.string_ import StringDtype 

1469 

1470 dtype = StringDtype(na_value=np.nan) 

1471 cls = dtype.construct_array_type() 

1472 return cls(pa_arr, dtype=dtype) # type: ignore[call-arg, return-value] 

1473 

1474 @property 

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

1476 """ 

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

1478 

1479 The time part of the Timestamps. 

1480 

1481 See Also 

1482 -------- 

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

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

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

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

1487 information. 

1488 

1489 Examples 

1490 -------- 

1491 For Series: 

1492 

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

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

1495 >>> s 

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

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

1498 dtype: datetime64[us, UTC] 

1499 >>> s.dt.time 

1500 0 10:00:00 

1501 1 11:00:00 

1502 dtype: object 

1503 

1504 For DatetimeIndex: 

1505 

1506 >>> idx = pd.DatetimeIndex( 

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

1508 ... ) 

1509 >>> idx.time 

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

1511 """ 

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

1513 # convert them into their i8 representation while 

1514 # keeping their timezone and not using UTC 

1515 timestamps = self._local_timestamps() 

1516 

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

1518 

1519 @property 

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

1521 """ 

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

1523 

1524 The time part of the Timestamps. 

1525 

1526 See Also 

1527 -------- 

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

1529 The time part of the Timestamps. 

1530 DatetimeIndex.tz : Return the timezone. 

1531 

1532 Examples 

1533 -------- 

1534 For Series: 

1535 

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

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

1538 >>> s 

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

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

1541 dtype: datetime64[us, UTC] 

1542 >>> s.dt.timetz 

1543 0 10:00:00+00:00 

1544 1 11:00:00+00:00 

1545 dtype: object 

1546 

1547 For DatetimeIndex: 

1548 

1549 >>> idx = pd.DatetimeIndex( 

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

1551 ... ) 

1552 >>> idx.timetz 

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

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

1555 """ 

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

1557 

1558 @property 

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

1560 """ 

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

1562 

1563 Namely, the date part of Timestamps without time and 

1564 timezone information. 

1565 

1566 See Also 

1567 -------- 

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

1569 The time part of the Timestamps. 

1570 DatetimeIndex.year : The year of the datetime. 

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

1572 DatetimeIndex.day : The day of the datetime. 

1573 

1574 Examples 

1575 -------- 

1576 For Series: 

1577 

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

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

1580 >>> s 

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

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

1583 dtype: datetime64[us, UTC] 

1584 >>> s.dt.date 

1585 0 2020-01-01 

1586 1 2020-02-01 

1587 dtype: object 

1588 

1589 For DatetimeIndex: 

1590 

1591 >>> idx = pd.DatetimeIndex( 

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

1593 ... ) 

1594 >>> idx.date 

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

1596 """ 

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

1598 # convert them into their i8 representation while 

1599 # keeping their timezone and not using UTC 

1600 timestamps = self._local_timestamps() 

1601 

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

1603 

1604 def isocalendar(self) -> DataFrame: 

1605 """ 

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

1607 

1608 The ISO 8601 standard defines the first week of the year as the week 

1609 containing the first Thursday. This method returns a DataFrame with 

1610 columns for the ISO year, ISO week number, and ISO day of week. 

1611 

1612 Returns 

1613 ------- 

1614 DataFrame 

1615 With columns year, week and day. 

1616 

1617 See Also 

1618 -------- 

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

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

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

1622 three components: year, week and weekday. 

1623 

1624 Examples 

1625 -------- 

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

1627 >>> idx.isocalendar() 

1628 year week day 

1629 2019-12-29 2019 52 7 

1630 2019-12-30 2020 1 1 

1631 2019-12-31 2020 1 2 

1632 2020-01-01 2020 1 3 

1633 >>> idx.isocalendar().week 

1634 2019-12-29 52 

1635 2019-12-30 1 

1636 2019-12-31 1 

1637 2020-01-01 1 

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

1639 """ 

1640 from pandas import DataFrame 

1641 

1642 values = self._local_timestamps() 

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

1644 iso_calendar_df = DataFrame(sarray, columns=["year", "week", "day"]).astype( 

1645 {"year": "Int32", "week": "UInt32", "day": "UInt32"} 

1646 ) 

1647 if self._hasna: 

1648 iso_calendar_df.iloc[self._isnan] = None 

1649 return iso_calendar_df 

1650 

1651 year = _field_accessor( 

1652 "year", 

1653 "Y", 

1654 """ 

1655 The year of the datetime. 

1656 

1657 This attribute returns the year component of each datetime value 

1658 in the Series/Index. The returned values are integers representing 

1659 the calendar year. 

1660 

1661 See Also 

1662 -------- 

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

1664 DatetimeIndex.day: The day of the datetime. 

1665 

1666 Examples 

1667 -------- 

1668 >>> datetime_series = pd.Series( 

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

1670 ... ) 

1671 >>> datetime_series 

1672 0 2000-12-31 

1673 1 2001-12-31 

1674 2 2002-12-31 

1675 dtype: datetime64[us] 

1676 >>> datetime_series.dt.year 

1677 0 2000 

1678 1 2001 

1679 2 2002 

1680 dtype: int32 

1681 """, 

1682 ) 

1683 month = _field_accessor( 

1684 "month", 

1685 "M", 

1686 """ 

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

1688 

1689 This attribute returns the month component of each datetime value 

1690 in the Series/Index. Months are numbered from 1 (January) through 

1691 12 (December). 

1692 

1693 See Also 

1694 -------- 

1695 DatetimeIndex.year: The year of the datetime. 

1696 DatetimeIndex.day: The day of the datetime. 

1697 

1698 Examples 

1699 -------- 

1700 >>> datetime_series = pd.Series( 

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

1702 ... ) 

1703 >>> datetime_series 

1704 0 2000-01-31 

1705 1 2000-02-29 

1706 2 2000-03-31 

1707 dtype: datetime64[us] 

1708 >>> datetime_series.dt.month 

1709 0 1 

1710 1 2 

1711 2 3 

1712 dtype: int32 

1713 """, 

1714 ) 

1715 day = _field_accessor( 

1716 "day", 

1717 "D", 

1718 """ 

1719 The day of the datetime. 

1720 

1721 This attribute returns the day of the month component of each 

1722 datetime value in the Series/Index. Values range from 1 to 31, 

1723 depending on the month. 

1724 

1725 See Also 

1726 -------- 

1727 DatetimeIndex.year: The year of the datetime. 

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

1729 DatetimeIndex.hour: The hours of the datetime. 

1730 

1731 Examples 

1732 -------- 

1733 >>> datetime_series = pd.Series( 

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

1735 ... ) 

1736 >>> datetime_series 

1737 0 2000-01-01 

1738 1 2000-01-02 

1739 2 2000-01-03 

1740 dtype: datetime64[us] 

1741 >>> datetime_series.dt.day 

1742 0 1 

1743 1 2 

1744 2 3 

1745 dtype: int32 

1746 """, 

1747 ) 

1748 hour = _field_accessor( 

1749 "hour", 

1750 "h", 

1751 """ 

1752 The hours of the datetime. 

1753 

1754 This attribute returns the hour component of each datetime value 

1755 in the Series/Index. Values range from 0 to 23, representing the 

1756 hour of the day in 24-hour format. 

1757 

1758 See Also 

1759 -------- 

1760 DatetimeIndex.day: The day of the datetime. 

1761 DatetimeIndex.minute: The minutes of the datetime. 

1762 DatetimeIndex.second: The seconds of the datetime. 

1763 

1764 Examples 

1765 -------- 

1766 >>> datetime_series = pd.Series( 

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

1768 ... ) 

1769 >>> datetime_series 

1770 0 2000-01-01 00:00:00 

1771 1 2000-01-01 01:00:00 

1772 2 2000-01-01 02:00:00 

1773 dtype: datetime64[us] 

1774 >>> datetime_series.dt.hour 

1775 0 0 

1776 1 1 

1777 2 2 

1778 dtype: int32 

1779 """, 

1780 ) 

1781 minute = _field_accessor( 

1782 "minute", 

1783 "m", 

1784 """ 

1785 The minutes of the datetime. 

1786 

1787 This attribute returns the minute component of each datetime value 

1788 in the Series/Index. Values range from 0 to 59. 

1789 

1790 See Also 

1791 -------- 

1792 DatetimeIndex.hour: The hours of the datetime. 

1793 DatetimeIndex.second: The seconds of the datetime. 

1794 

1795 Examples 

1796 -------- 

1797 >>> datetime_series = pd.Series( 

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

1799 ... ) 

1800 >>> datetime_series 

1801 0 2000-01-01 00:00:00 

1802 1 2000-01-01 00:01:00 

1803 2 2000-01-01 00:02:00 

1804 dtype: datetime64[us] 

1805 >>> datetime_series.dt.minute 

1806 0 0 

1807 1 1 

1808 2 2 

1809 dtype: int32 

1810 """, 

1811 ) 

1812 second = _field_accessor( 

1813 "second", 

1814 "s", 

1815 """ 

1816 The seconds of the datetime. 

1817 

1818 This attribute returns the second component of each datetime value 

1819 in the Series/Index. Values range from 0 to 59. 

1820 

1821 See Also 

1822 -------- 

1823 DatetimeIndex.minute: The minutes of the datetime. 

1824 DatetimeIndex.microsecond: The microseconds of the datetime. 

1825 DatetimeIndex.nanosecond: The nanoseconds of the datetime. 

1826 

1827 Examples 

1828 -------- 

1829 >>> datetime_series = pd.Series( 

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

1831 ... ) 

1832 >>> datetime_series 

1833 0 2000-01-01 00:00:00 

1834 1 2000-01-01 00:00:01 

1835 2 2000-01-01 00:00:02 

1836 dtype: datetime64[us] 

1837 >>> datetime_series.dt.second 

1838 0 0 

1839 1 1 

1840 2 2 

1841 dtype: int32 

1842 """, 

1843 ) 

1844 microsecond = _field_accessor( 

1845 "microsecond", 

1846 "us", 

1847 """ 

1848 The microseconds of the datetime. 

1849 

1850 This attribute returns the microsecond component of each datetime 

1851 value in the Series/Index. Values range from 0 to 999999 

1852 (one microsecond is one millionth of a second). 

1853 

1854 See Also 

1855 -------- 

1856 DatetimeIndex.second: The seconds of the datetime. 

1857 DatetimeIndex.nanosecond: The nanoseconds of the datetime. 

1858 

1859 Examples 

1860 -------- 

1861 >>> datetime_series = pd.Series( 

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

1863 ... ) 

1864 >>> datetime_series 

1865 0 2000-01-01 00:00:00.000000 

1866 1 2000-01-01 00:00:00.000001 

1867 2 2000-01-01 00:00:00.000002 

1868 dtype: datetime64[us] 

1869 >>> datetime_series.dt.microsecond 

1870 0 0 

1871 1 1 

1872 2 2 

1873 dtype: int32 

1874 """, 

1875 ) 

1876 nanosecond = _field_accessor( 

1877 "nanosecond", 

1878 "ns", 

1879 """ 

1880 The nanoseconds of the datetime. 

1881 

1882 This attribute returns the nanosecond component of each datetime 

1883 value in the Series/Index. Values range from 0 to 999 

1884 (one nanosecond is one billionth of a second). 

1885 

1886 See Also 

1887 -------- 

1888 DatetimeIndex.second: The seconds of the datetime. 

1889 DatetimeIndex.microsecond: The microseconds of the datetime. 

1890 

1891 Examples 

1892 -------- 

1893 >>> datetime_series = pd.Series( 

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

1895 ... ) 

1896 >>> datetime_series 

1897 0 2000-01-01 00:00:00.000000000 

1898 1 2000-01-01 00:00:00.000000001 

1899 2 2000-01-01 00:00:00.000000002 

1900 dtype: datetime64[ns] 

1901 >>> datetime_series.dt.nanosecond 

1902 0 0 

1903 1 1 

1904 2 2 

1905 dtype: int32 

1906 """, 

1907 ) 

1908 _dayofweek_doc = """ 

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

1910 

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

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

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

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

1915 

1916 Returns 

1917 ------- 

1918 Series or Index 

1919 Containing integers indicating the day number. 

1920 

1921 See Also 

1922 -------- 

1923 Series.dt.weekday : Alias. 

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

1925 

1926 Examples 

1927 -------- 

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

1929 >>> s.dt.day_of_week 

1930 2016-12-31 5 

1931 2017-01-01 6 

1932 2017-01-02 0 

1933 2017-01-03 1 

1934 2017-01-04 2 

1935 2017-01-05 3 

1936 2017-01-06 4 

1937 2017-01-07 5 

1938 2017-01-08 6 

1939 Freq: D, dtype: int32 

1940 """ 

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

1942 

1943 @property 

1944 def weekday(self): 

1945 """ 

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

1947 

1948 .. deprecated:: 3.1.0 

1949 Use :attr:`DatetimeArray.day_of_week` instead. 

1950 """ 

1951 # GH#12816 

1952 warnings.warn( 

1953 "DatetimeArray.weekday is deprecated and will be removed " 

1954 "in a future version. Use DatetimeArray.day_of_week or " 

1955 "Series.dt.day_of_week instead.", 

1956 Pandas4Warning, 

1957 stacklevel=find_stack_level(), 

1958 ) 

1959 return self.day_of_week 

1960 

1961 @property 

1962 def dayofweek(self): 

1963 """ 

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

1965 

1966 .. deprecated:: 3.1.0 

1967 Use :attr:`DatetimeArray.day_of_week` instead. 

1968 """ 

1969 warnings.warn( 

1970 "DatetimeArray.dayofweek is deprecated and will be removed in a " 

1971 "future version. Use DatetimeArray.day_of_week instead.", 

1972 Pandas4Warning, 

1973 stacklevel=find_stack_level(), 

1974 ) 

1975 return self.day_of_week 

1976 

1977 day_of_year = _field_accessor( 

1978 "dayofyear", 

1979 "doy", 

1980 """ 

1981 The ordinal day of the year. 

1982 

1983 This attribute returns the day of the year for each datetime value 

1984 in the Series/Index. Values range from 1 to 365 (or 366 for leap years). 

1985 

1986 See Also 

1987 -------- 

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

1989 DatetimeIndex.day : The day of the datetime. 

1990 

1991 Examples 

1992 -------- 

1993 For Series: 

1994 

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

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

1997 >>> s 

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

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

2000 dtype: datetime64[us, UTC] 

2001 >>> s.dt.day_of_year 

2002 0 1 

2003 1 32 

2004 dtype: int32 

2005 

2006 For DatetimeIndex: 

2007 

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

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

2010 >>> idx.day_of_year 

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

2012 """, 

2013 ) 

2014 

2015 @property 

2016 def dayofyear(self): 

2017 """ 

2018 The ordinal day of the year. 

2019 

2020 .. deprecated:: 3.1.0 

2021 Use :attr:`DatetimeArray.day_of_year` instead. 

2022 """ 

2023 warnings.warn( 

2024 "DatetimeArray.dayofyear is deprecated and will be removed in a " 

2025 "future version. Use DatetimeArray.day_of_year instead.", 

2026 Pandas4Warning, 

2027 stacklevel=find_stack_level(), 

2028 ) 

2029 return self.day_of_year 

2030 

2031 quarter = _field_accessor( 

2032 "quarter", 

2033 "q", 

2034 """ 

2035 The quarter of the date. 

2036 

2037 This attribute returns the quarter of the year for each datetime value 

2038 in the Series/Index. Quarter 1 includes January through March, quarter 2 

2039 includes April through June, quarter 3 includes July through September, 

2040 and quarter 4 includes October through December. 

2041 

2042 See Also 

2043 -------- 

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

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

2046 The time part of the Timestamps. 

2047 

2048 Examples 

2049 -------- 

2050 For Series: 

2051 

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

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

2054 >>> s 

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

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

2057 dtype: datetime64[us, UTC] 

2058 >>> s.dt.quarter 

2059 0 1 

2060 1 2 

2061 dtype: int32 

2062 

2063 For DatetimeIndex: 

2064 

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

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

2067 >>> idx.quarter 

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

2069 """, 

2070 ) 

2071 days_in_month = _field_accessor( 

2072 "days_in_month", 

2073 "dim", 

2074 """ 

2075 The number of days in the month. 

2076 

2077 This attribute returns the total number of days in the month for each 

2078 datetime value in the Series/Index. The value depends on the month and 

2079 whether the year is a leap year (e.g., February has 29 days in a leap year). 

2080 

2081 See Also 

2082 -------- 

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

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

2085 date is the last day of the month. 

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

2087 date is the first day of the month. 

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

2089 

2090 Examples 

2091 -------- 

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

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

2094 >>> s 

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

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

2097 dtype: datetime64[us, UTC] 

2098 >>> s.dt.days_in_month 

2099 0 31 

2100 1 29 

2101 dtype: int32 

2102 """, 

2103 ) 

2104 

2105 @property 

2106 def daysinmonth(self): 

2107 """ 

2108 The number of days in the month. 

2109 

2110 .. deprecated:: 3.1.0 

2111 Use :attr:`DatetimeArray.days_in_month` instead. 

2112 """ 

2113 warnings.warn( 

2114 "DatetimeArray.daysinmonth is deprecated and will be removed in a " 

2115 "future version. Use DatetimeArray.days_in_month instead.", 

2116 Pandas4Warning, 

2117 stacklevel=find_stack_level(), 

2118 ) 

2119 return self.days_in_month 

2120 

2121 def _get_start_end_field(self, field: str, freq: BaseOffset | None) -> np.ndarray: 

2122 """ 

2123 Return boolean array for is_month_start, is_quarter_end, etc. 

2124 

2125 Parameters 

2126 ---------- 

2127 field : str 

2128 freq : BaseOffset or None 

2129 """ 

2130 month_kw = 12 

2131 if freq: 

2132 kwds = freq.kwds 

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

2134 

2135 if freq is not None: 

2136 freq_name = freq.rule_code 

2137 else: 

2138 freq_name = None 

2139 

2140 values = self._local_timestamps() 

2141 return fields.get_start_end_field( 

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

2143 ) 

2144 

2145 _is_month_doc = """ 

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

2147 

2148 This boolean attribute evaluates to True if the date falls on the 

2149 {first_or_last} day of a calendar month, and False otherwise. 

2150 

2151 Returns 

2152 ------- 

2153 Series or array 

2154 For Series, returns a Series with boolean values. 

2155 For DatetimeIndex, returns a boolean array. 

2156 

2157 See Also 

2158 -------- 

2159 is_month_start : Return a boolean indicating whether the date 

2160 is the first day of the month. 

2161 is_month_end : Return a boolean indicating whether the date 

2162 is the last day of the month. 

2163 

2164 Examples 

2165 -------- 

2166 This method is available on Series with datetime values under 

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

2168 

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

2170 >>> s 

2171 0 2018-02-27 

2172 1 2018-02-28 

2173 2 2018-03-01 

2174 dtype: datetime64[us] 

2175 >>> s.dt.is_month_start 

2176 0 False 

2177 1 False 

2178 2 True 

2179 dtype: bool 

2180 >>> s.dt.is_month_end 

2181 0 False 

2182 1 True 

2183 2 False 

2184 dtype: bool 

2185 

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

2187 >>> idx.is_month_start 

2188 array([False, False, True]) 

2189 >>> idx.is_month_end 

2190 array([False, True, False]) 

2191 """ 

2192 is_month_start = _field_accessor( 

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

2194 ) 

2195 

2196 is_month_end = _field_accessor( 

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

2198 ) 

2199 

2200 is_quarter_start = _field_accessor( 

2201 "is_quarter_start", 

2202 "is_quarter_start", 

2203 """ 

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

2205 

2206 This boolean attribute evaluates to True if the date falls on the 

2207 first day of a calendar quarter (January 1, April 1, July 1, or 

2208 October 1), and False otherwise. 

2209 

2210 Returns 

2211 ------- 

2212 is_quarter_start : Series or DatetimeIndex 

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

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

2215 name. 

2216 

2217 See Also 

2218 -------- 

2219 quarter : Return the quarter of the date. 

2220 is_quarter_end : Similar property for indicating the quarter end. 

2221 

2222 Examples 

2223 -------- 

2224 This method is available on Series with datetime values under 

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

2226 

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

2228 ... periods=4)}) 

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

2230 ... is_quarter_start=df.dates.dt.is_quarter_start) 

2231 dates quarter is_quarter_start 

2232 0 2017-03-30 1 False 

2233 1 2017-03-31 1 False 

2234 2 2017-04-01 2 True 

2235 3 2017-04-02 2 False 

2236 

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

2238 >>> idx 

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

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

2241 

2242 >>> idx.is_quarter_start 

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

2244 """, 

2245 ) 

2246 is_quarter_end = _field_accessor( 

2247 "is_quarter_end", 

2248 "is_quarter_end", 

2249 """ 

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

2251 

2252 This boolean attribute evaluates to True if the date falls on the 

2253 last day of a calendar quarter (March 31, June 30, September 30, or 

2254 December 31), and False otherwise. 

2255 

2256 Returns 

2257 ------- 

2258 is_quarter_end : Series or DatetimeIndex 

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

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

2261 name. 

2262 

2263 See Also 

2264 -------- 

2265 quarter : Return the quarter of the date. 

2266 is_quarter_start : Similar property indicating the quarter start. 

2267 

2268 Examples 

2269 -------- 

2270 This method is available on Series with datetime values under 

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

2272 

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

2274 ... periods=4)}) 

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

2276 ... is_quarter_end=df.dates.dt.is_quarter_end) 

2277 dates quarter is_quarter_end 

2278 0 2017-03-30 1 False 

2279 1 2017-03-31 1 True 

2280 2 2017-04-01 2 False 

2281 3 2017-04-02 2 False 

2282 

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

2284 >>> idx 

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

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

2287 

2288 >>> idx.is_quarter_end 

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

2290 """, 

2291 ) 

2292 is_year_start = _field_accessor( 

2293 "is_year_start", 

2294 "is_year_start", 

2295 """ 

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

2297 

2298 This boolean attribute evaluates to True if the date is January 1st, 

2299 and False otherwise. 

2300 

2301 Returns 

2302 ------- 

2303 Series or DatetimeIndex 

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

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

2306 name. 

2307 

2308 See Also 

2309 -------- 

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

2311 

2312 Examples 

2313 -------- 

2314 This method is available on Series with datetime values under 

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

2316 

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

2318 >>> dates 

2319 0 2017-12-30 

2320 1 2017-12-31 

2321 2 2018-01-01 

2322 dtype: datetime64[us] 

2323 

2324 >>> dates.dt.is_year_start 

2325 0 False 

2326 1 False 

2327 2 True 

2328 dtype: bool 

2329 

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

2331 >>> idx 

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

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

2334 

2335 >>> idx.is_year_start 

2336 array([False, False, True]) 

2337 

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

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

2340 

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

2342 >>> dates 

2343 0 2021-01-01 

2344 1 2022-01-03 

2345 2 2023-01-02 

2346 3 2024-01-01 

2347 dtype: datetime64[us] 

2348 

2349 >>> dates.dt.is_year_start 

2350 0 True 

2351 1 False 

2352 2 False 

2353 3 True 

2354 dtype: bool 

2355 

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

2357 >>> idx 

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

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

2360 

2361 >>> idx.is_year_start 

2362 array([ True, True, True, True]) 

2363 """, 

2364 ) 

2365 is_year_end = _field_accessor( 

2366 "is_year_end", 

2367 "is_year_end", 

2368 """ 

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

2370 

2371 This boolean attribute evaluates to True if the date is December 31st, 

2372 and False otherwise. 

2373 

2374 Returns 

2375 ------- 

2376 Series or DatetimeIndex 

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

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

2379 name. 

2380 

2381 See Also 

2382 -------- 

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

2384 

2385 Examples 

2386 -------- 

2387 This method is available on Series with datetime values under 

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

2389 

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

2391 >>> dates 

2392 0 2017-12-30 

2393 1 2017-12-31 

2394 2 2018-01-01 

2395 dtype: datetime64[us] 

2396 

2397 >>> dates.dt.is_year_end 

2398 0 False 

2399 1 True 

2400 2 False 

2401 dtype: bool 

2402 

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

2404 >>> idx 

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

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

2407 

2408 >>> idx.is_year_end 

2409 array([False, True, False]) 

2410 """, 

2411 ) 

2412 is_leap_year = _field_accessor( 

2413 "is_leap_year", 

2414 "is_leap_year", 

2415 """ 

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

2417 

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

2419 29th of February as an intercalary day. 

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

2421 of years divisible by 100 but not by 400. 

2422 

2423 Returns 

2424 ------- 

2425 Series or ndarray 

2426 Booleans indicating if dates belong to a leap year. 

2427 

2428 See Also 

2429 -------- 

2430 DatetimeIndex.is_year_end : Indicate whether the date is the 

2431 last day of the year. 

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

2433 day of a year. 

2434 

2435 Examples 

2436 -------- 

2437 This method is available on Series with datetime values under 

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

2439 

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

2441 >>> idx 

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

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

2444 >>> idx.is_leap_year 

2445 array([ True, False, False]) 

2446 

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

2448 >>> dates_series 

2449 0 2012-12-31 

2450 1 2013-12-31 

2451 2 2014-12-31 

2452 dtype: datetime64[us] 

2453 >>> dates_series.dt.is_leap_year 

2454 0 True 

2455 1 False 

2456 2 False 

2457 dtype: bool 

2458 """, 

2459 ) 

2460 

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

2462 """ 

2463 Convert Timestamp to a Julian Date. 

2464 

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

2466 

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

2468 

2469 Returns 

2470 ------- 

2471 ndarray or Index 

2472 Float values that represent each date in Julian Calendar. 

2473 

2474 See Also 

2475 -------- 

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

2477 

2478 Examples 

2479 -------- 

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

2481 >>> idx.to_julian_date() 

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

2483 """ 

2484 if self.tz is not None: 

2485 # GH#54763 JD is absolute-time, so use the UTC instant 

2486 return self.tz_convert("UTC").tz_localize(None).to_julian_date() 

2487 

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

2489 year = np.asarray(self.year) 

2490 month = np.asarray(self.month) 

2491 day = np.asarray(self.day) 

2492 testarr = month < 3 

2493 year[testarr] -= 1 

2494 month[testarr] += 12 

2495 return ( 

2496 day 

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

2498 + 365 * year 

2499 + np.floor(year / 4) 

2500 - np.floor(year / 100) 

2501 + np.floor(year / 400) 

2502 + 1_721_118.5 

2503 + ( 

2504 self.hour 

2505 + self.minute / 60 

2506 + self.second / 3600 

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

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

2509 ) 

2510 / 24 

2511 ) 

2512 

2513 # ----------------------------------------------------------------- 

2514 # Reductions 

2515 

2516 def _reduce( 

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

2518 ): 

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

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

2521 if name == "std": 

2522 from pandas.core.arrays import TimedeltaArray 

2523 

2524 return TimedeltaArray._from_sequence(result) 

2525 else: 

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

2527 return result 

2528 

2529 def std( 

2530 self, 

2531 axis=None, 

2532 dtype=None, 

2533 out=None, 

2534 ddof: int = 1, 

2535 keepdims: bool = False, 

2536 skipna: bool = True, 

2537 ) -> Timedelta: 

2538 """ 

2539 Return sample standard deviation over requested axis. 

2540 

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

2542 

2543 Parameters 

2544 ---------- 

2545 axis : int, optional 

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

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

2548 dtype : dtype, optional, default None 

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

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

2551 it is the same as the array type. 

2552 out : ndarray, optional, default None 

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

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

2555 calculated values) will be cast if necessary. 

2556 ddof : int, default 1 

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

2558 where `N` represents the number of elements. 

2559 keepdims : bool, optional 

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

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

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

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

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

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

2566 exceptions will be raised. 

2567 skipna : bool, default True 

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

2569 will be ``NA``. 

2570 

2571 Returns 

2572 ------- 

2573 Timedelta 

2574 Standard deviation over requested axis. 

2575 

2576 See Also 

2577 -------- 

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

2579 along given axis. 

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

2581 

2582 Examples 

2583 -------- 

2584 For :class:`pandas.DatetimeIndex`: 

2585 

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

2587 >>> idx 

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

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

2590 >>> idx.std() 

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

2592 """ 

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

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

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

2596 from pandas.core.arrays import TimedeltaArray 

2597 

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

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

2600 dtype = np.dtype(dtype_str) 

2601 

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

2603 

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

2605 

2606 

2607# ------------------------------------------------------------------- 

2608# Constructor Helpers 

2609 

2610 

2611def _sequence_to_dt64( 

2612 data: ArrayLike, 

2613 *, 

2614 copy: bool = False, 

2615 tz: tzinfo | None = None, 

2616 dayfirst: bool = False, 

2617 yearfirst: bool = False, 

2618 ambiguous: TimeAmbiguous = "raise", 

2619 out_unit: str | None = None, 

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

2621 """ 

2622 Parameters 

2623 ---------- 

2624 data : np.ndarray or ExtensionArray 

2625 dtl.ensure_arraylike_for_datetimelike has already been called. 

2626 copy : bool, default False 

2627 tz : tzinfo or None, default None 

2628 dayfirst : bool, default False 

2629 yearfirst : bool, default False 

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

2631 See pandas._libs.tslibs.tzconversion.tz_localize_to_utc. 

2632 out_unit : str or None, default None 

2633 Desired output resolution. 

2634 

2635 Returns 

2636 ------- 

2637 result : numpy.ndarray 

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

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

2640 tz : tzinfo or None 

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

2642 

2643 Raises 

2644 ------ 

2645 TypeError : PeriodDType data is passed 

2646 """ 

2647 

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

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

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

2651 

2652 out_dtype = DT64NS_DTYPE 

2653 if out_unit is not None: 

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

2655 

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

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

2658 # also complex or categorical or other extension 

2659 data = cast("np.ndarray", data) 

2660 copy = False 

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

2662 # Much more performant than going through array_to_datetime 

2663 data = data.astype(np.int64) 

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

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

2666 result = tslib.array_to_datetime_with_tz( 

2667 obj_data, 

2668 tz=tz, 

2669 dayfirst=dayfirst, 

2670 yearfirst=yearfirst, 

2671 creso=abbrev_to_npy_unit(out_unit), 

2672 ) 

2673 return result, tz 

2674 else: 

2675 converted, inferred_tz = objects_to_datetime64( 

2676 data, 

2677 dayfirst=dayfirst, 

2678 yearfirst=yearfirst, 

2679 out_unit=out_unit, 

2680 ) 

2681 copy = False 

2682 if tz and inferred_tz: 

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

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

2685 result = converted 

2686 

2687 elif inferred_tz: 

2688 tz = inferred_tz 

2689 result = converted 

2690 

2691 else: 

2692 result, _ = _construct_from_dt64_naive( 

2693 converted, tz=tz, copy=copy, ambiguous=ambiguous 

2694 ) 

2695 return result, tz 

2696 

2697 data_dtype = data.dtype 

2698 

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

2700 # so we need to handle these types. 

2701 if isinstance(data_dtype, DatetimeTZDtype): 

2702 # DatetimeArray -> ndarray 

2703 data = cast("DatetimeArray", data) 

2704 tz = _maybe_infer_tz(tz, data.tz) 

2705 result = data._ndarray 

2706 

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

2708 # tz-naive DatetimeArray or ndarray[datetime64] 

2709 if isinstance(data, DatetimeArray): 

2710 data = data._ndarray 

2711 

2712 data = cast("np.ndarray", data) 

2713 result, copy = _construct_from_dt64_naive( 

2714 data, tz=tz, copy=copy, ambiguous=ambiguous 

2715 ) 

2716 

2717 else: 

2718 # must be integer dtype otherwise 

2719 # assume this data are epoch timestamps 

2720 if data.dtype != INT64_DTYPE: 

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

2722 copy = False 

2723 data = cast("np.ndarray", data) 

2724 result = data.view(out_dtype) 

2725 

2726 if copy: 

2727 result = result.copy() 

2728 

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

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

2731 assert result.dtype != "M8" 

2732 assert is_supported_dtype(result.dtype) 

2733 return result, tz 

2734 

2735 

2736def _construct_from_dt64_naive( 

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

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

2739 """ 

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

2741 """ 

2742 # Caller is responsible for ensuring 

2743 # lib.is_np_dtype(data.dtype) 

2744 

2745 new_dtype = data.dtype 

2746 if not is_supported_dtype(new_dtype): 

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

2748 new_dtype = get_supported_dtype(new_dtype) 

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

2750 copy = False 

2751 

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

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

2754 # without this, test_constructor_datetime64_bigendian fails 

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

2756 new_dtype = data.dtype 

2757 copy = False 

2758 

2759 if tz is not None: 

2760 # Convert tz-naive to UTC 

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

2762 # copy? tz_localize_to_utc always makes one. 

2763 shape = data.shape 

2764 if data.ndim > 1: 

2765 data = data.ravel() 

2766 

2767 data_unit = get_unit_from_dtype(new_dtype) 

2768 data = tzconversion.tz_localize_to_utc( 

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

2770 ) 

2771 data = data.view(new_dtype) 

2772 data = data.reshape(shape) 

2773 

2774 assert data.dtype == new_dtype, data.dtype 

2775 result = data 

2776 

2777 return result, copy 

2778 

2779 

2780def objects_to_datetime64( 

2781 data: np.ndarray, 

2782 dayfirst, 

2783 yearfirst, 

2784 utc: bool = False, 

2785 errors: DateTimeErrorChoices = "raise", 

2786 out_unit: str | None = None, 

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

2788 """ 

2789 Convert data to array of timestamps. 

2790 

2791 Parameters 

2792 ---------- 

2793 data : np.ndarray[object] 

2794 dayfirst : bool 

2795 yearfirst : bool 

2796 utc : bool, default False 

2797 Whether to convert/localize timestamps to UTC. 

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

2799 out_unit : str or None, default None 

2800 None indicates we should do resolution inference. 

2801 

2802 Returns 

2803 ------- 

2804 result : ndarray 

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

2806 timestamps. 

2807 inferred_tz : tzinfo or None 

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

2809 

2810 Raises 

2811 ------ 

2812 ValueError : if data cannot be converted to datetimes 

2813 TypeError : When a type cannot be converted to datetime 

2814 """ 

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

2816 

2817 # if str-dtype, convert 

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

2819 

2820 result, tz_parsed = tslib.array_to_datetime( 

2821 data, 

2822 errors=errors, 

2823 utc=utc, 

2824 dayfirst=dayfirst, 

2825 yearfirst=yearfirst, 

2826 creso=abbrev_to_npy_unit(out_unit), 

2827 ) 

2828 

2829 if tz_parsed is not None: 

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

2831 # is in UTC 

2832 return result, tz_parsed 

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

2834 return result, tz_parsed 

2835 else: # pragma: no cover 

2836 # this TypeError should never be hit 

2837 raise TypeError(result) 

2838 

2839 

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

2841 """ 

2842 Convert data based on dtype conventions, issuing 

2843 errors where appropriate. 

2844 

2845 Parameters 

2846 ---------- 

2847 data : np.ndarray or pd.Index 

2848 copy : bool 

2849 tz : tzinfo or None, default None 

2850 

2851 Returns 

2852 ------- 

2853 data : np.ndarray or pd.Index 

2854 copy : bool 

2855 

2856 Raises 

2857 ------ 

2858 TypeError : PeriodDType data is passed 

2859 """ 

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

2861 # e.g. collections.deque 

2862 return data, copy 

2863 

2864 if is_float_dtype(data.dtype): 

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

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

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

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

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

2870 copy = False 

2871 

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

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

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

2875 elif isinstance(data.dtype, PeriodDtype): 

2876 # Note: without explicitly raising here, PeriodIndex 

2877 # test_setops.test_join_does_not_recur fails 

2878 raise TypeError( 

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

2880 ) 

2881 

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

2883 data.dtype, DatetimeTZDtype 

2884 ): 

2885 # TODO: We have no tests for these 

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

2887 copy = False 

2888 

2889 return data, copy 

2890 

2891 

2892# ------------------------------------------------------------------- 

2893# Validation and Inference 

2894 

2895 

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

2897 """ 

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

2899 the user-provided timezone, if any. 

2900 

2901 Parameters 

2902 ---------- 

2903 tz : tzinfo or None 

2904 inferred_tz : tzinfo or None 

2905 

2906 Returns 

2907 ------- 

2908 tz : tzinfo or None 

2909 

2910 Raises 

2911 ------ 

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

2913 """ 

2914 if tz is None: 

2915 tz = inferred_tz 

2916 elif inferred_tz is None: 

2917 pass 

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

2919 raise TypeError( 

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

2921 ) 

2922 return tz 

2923 

2924 

2925def _validate_dt64_dtype(dtype): 

2926 """ 

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

2928 dtype or a pandas DatetimeTZDtype. 

2929 

2930 Parameters 

2931 ---------- 

2932 dtype : object 

2933 

2934 Returns 

2935 ------- 

2936 dtype : None, numpy.dtype, or DatetimeTZDtype 

2937 

2938 Raises 

2939 ------ 

2940 ValueError : invalid dtype 

2941 

2942 Notes 

2943 ----- 

2944 Unlike _validate_tz_from_dtype, this does _not_ allow non-existent 

2945 tz errors to go through 

2946 """ 

2947 if dtype is not None: 

2948 dtype = pandas_dtype(dtype) 

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

2950 # no precision, disallowed GH#24806 

2951 msg = ( 

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

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

2954 ) 

2955 raise ValueError(msg) 

2956 

2957 if ( 

2958 isinstance(dtype, np.dtype) 

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

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

2961 raise ValueError( 

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

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

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

2965 ) 

2966 

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

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

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

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

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

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

2973 dtype = cast("DatetimeTZDtype", dtype) 

2974 dtype = DatetimeTZDtype( 

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

2976 ) 

2977 

2978 return dtype 

2979 

2980 

2981def _validate_tz_from_dtype( 

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

2983) -> tzinfo | None: 

2984 """ 

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

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

2987 tz. 

2988 

2989 Parameters 

2990 ---------- 

2991 dtype : dtype, str 

2992 tz : None, tzinfo 

2993 explicit_tz_none : bool, default False 

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

2995 

2996 Returns 

2997 ------- 

2998 tz : consensus tzinfo 

2999 

3000 Raises 

3001 ------ 

3002 ValueError : on tzinfo mismatch 

3003 """ 

3004 if dtype is not None: 

3005 if isinstance(dtype, str): 

3006 try: 

3007 dtype = DatetimeTZDtype.construct_from_string(dtype) 

3008 except TypeError: 

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

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

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

3012 # go through 

3013 pass 

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

3015 if dtz is not None: 

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

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

3018 if explicit_tz_none: 

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

3020 tz = dtz 

3021 

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

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

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

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

3026 raise ValueError( 

3027 "cannot supply both a tz and a " 

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

3029 ) 

3030 

3031 return tz 

3032 

3033 

3034def _infer_tz_from_endpoints( 

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

3036) -> tzinfo | None: 

3037 """ 

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

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

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

3041 

3042 Parameters 

3043 ---------- 

3044 start : Timestamp 

3045 end : Timestamp 

3046 tz : tzinfo or None 

3047 

3048 Returns 

3049 ------- 

3050 tz : tzinfo or None 

3051 

3052 Raises 

3053 ------ 

3054 TypeError : if start and end timezones do not agree 

3055 """ 

3056 try: 

3057 inferred_tz = timezones.infer_tzinfo(start, end) 

3058 except AssertionError as err: 

3059 # infer_tzinfo raises AssertionError if passed mismatched timezones 

3060 raise TypeError( 

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

3062 ) from err 

3063 

3064 inferred_tz = timezones.maybe_get_tz(inferred_tz) 

3065 tz = timezones.maybe_get_tz(tz) 

3066 

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

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

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

3070 

3071 elif inferred_tz is not None: 

3072 tz = inferred_tz 

3073 

3074 return tz 

3075 

3076 

3077def _maybe_normalize_endpoints( 

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

3079) -> tuple[_TimestampNoneT1, _TimestampNoneT2]: 

3080 if normalize: 

3081 if start is not None: 

3082 start = start.normalize() 

3083 

3084 if end is not None: 

3085 end = end.normalize() 

3086 

3087 return start, end 

3088 

3089 

3090def _maybe_localize_point( 

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

3092) -> Timestamp | None: 

3093 """ 

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

3095 start or end Timestamp 

3096 

3097 Parameters 

3098 ---------- 

3099 ts : start or end Timestamp to potentially localize 

3100 freq : Tick, DateOffset, or None 

3101 tz : str, timezone object or None 

3102 ambiguous: str, localization behavior for ambiguous times 

3103 nonexistent: str, localization behavior for nonexistent times 

3104 

3105 Returns 

3106 ------- 

3107 ts : Timestamp 

3108 """ 

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

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

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

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

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

3114 # we have historically defaulted ambiguous=False 

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

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

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

3118 localize_args["tz"] = tz 

3119 ts = ts.tz_localize(**localize_args) 

3120 return ts 

3121 

3122 

3123def _generate_range( 

3124 start: Timestamp | None, 

3125 end: Timestamp | None, 

3126 periods: int | None, 

3127 offset: BaseOffset, 

3128 *, 

3129 unit: TimeUnit, 

3130) -> Generator[Timestamp]: 

3131 """ 

3132 Generates a sequence of dates corresponding to the specified time 

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

3134 objects to represent time increments. 

3135 

3136 Parameters 

3137 ---------- 

3138 start : Timestamp or None 

3139 end : Timestamp or None 

3140 periods : int or None 

3141 offset : DateOffset 

3142 unit : str 

3143 

3144 Notes 

3145 ----- 

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

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

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

3149 satisfy start <= date <= end. 

3150 

3151 Returns 

3152 ------- 

3153 dates : generator object 

3154 """ 

3155 offset = to_offset(offset) 

3156 

3157 start = Timestamp(start) 

3158 # error: Non-overlapping identity check (left operand type: "Timestamp", 

3159 # right operand type: "NaTType") 

3160 if start is not NaT: # type: ignore[comparison-overlap] 

3161 start = start.as_unit(unit) 

3162 else: 

3163 start = None 

3164 

3165 end = Timestamp(end) 

3166 # error: Non-overlapping identity check (left operand type: "Timestamp", 

3167 # right operand type: "NaTType") 

3168 if end is not NaT: # type: ignore[comparison-overlap] 

3169 end = end.as_unit(unit) 

3170 else: 

3171 end = None 

3172 

3173 # GH#64834/GH#65011 When deriving start from (end, periods), roll end onto 

3174 # the offset first so we don't lose a period. For B/bh this is handled by 

3175 # the vectorized path, but anchored offsets (W, ME, MS, QS, ...) reach here. 

3176 # Without this, periods=1 also leaves freq pinned to an off-grid end. 

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

3178 if offset.n >= 0: 

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

3180 else: 

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

3182 

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

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

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

3186 

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

3188 if offset.n >= 0: 

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

3190 else: 

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

3192 

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

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

3195 end = None 

3196 periods = 0 

3197 

3198 if end is None: 

3199 # GH#41563 avoid 0 * offset for offsets where n=0 is not allowed 

3200 if periods == 1: 

3201 end = start 

3202 else: 

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

3204 # argument type "None" 

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

3206 

3207 if start is None: 

3208 # GH#41563 avoid 0 * offset for offsets where n=0 is not allowed 

3209 if periods == 1: 

3210 start = end 

3211 else: 

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

3213 # argument type "None" 

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

3215 

3216 start = cast("Timestamp", start) 

3217 end = cast("Timestamp", end) 

3218 

3219 cur = start 

3220 if offset.n >= 0: 

3221 while cur <= end: 

3222 yield cur 

3223 

3224 if cur == end: 

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

3226 # in offset.apply unless we have to 

3227 break 

3228 

3229 # faster than cur + offset 

3230 next_date = offset._add_datetime(cur) 

3231 next_date = next_date.as_unit(unit) 

3232 if next_date <= cur: 

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

3234 cur = next_date 

3235 else: 

3236 while cur >= end: 

3237 yield cur 

3238 

3239 if cur == end: 

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

3241 # in offset.apply unless we have to 

3242 break 

3243 

3244 # faster than cur + offset 

3245 next_date = offset._add_datetime(cur) 

3246 next_date = next_date.as_unit(unit) 

3247 if next_date >= cur: 

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

3249 cur = next_date