datetimes.py 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595
  1. from __future__ import annotations
  2. from datetime import (
  3. datetime,
  4. time,
  5. timedelta,
  6. tzinfo,
  7. )
  8. from typing import (
  9. TYPE_CHECKING,
  10. Iterator,
  11. cast,
  12. )
  13. import warnings
  14. import numpy as np
  15. from pandas._libs import (
  16. lib,
  17. tslib,
  18. )
  19. from pandas._libs.tslibs import (
  20. BaseOffset,
  21. NaT,
  22. NaTType,
  23. Resolution,
  24. Timestamp,
  25. astype_overflowsafe,
  26. fields,
  27. get_resolution,
  28. get_supported_reso,
  29. get_unit_from_dtype,
  30. ints_to_pydatetime,
  31. is_date_array_normalized,
  32. is_supported_unit,
  33. is_unitless,
  34. normalize_i8_timestamps,
  35. npy_unit_to_abbrev,
  36. timezones,
  37. to_offset,
  38. tz_convert_from_utc,
  39. tzconversion,
  40. )
  41. from pandas._libs.tslibs.dtypes import abbrev_to_npy_unit
  42. from pandas._typing import (
  43. DateTimeErrorChoices,
  44. IntervalClosedType,
  45. TimeAmbiguous,
  46. TimeNonexistent,
  47. npt,
  48. )
  49. from pandas.errors import PerformanceWarning
  50. from pandas.util._exceptions import find_stack_level
  51. from pandas.util._validators import validate_inclusive
  52. from pandas.core.dtypes.common import (
  53. DT64NS_DTYPE,
  54. INT64_DTYPE,
  55. is_bool_dtype,
  56. is_datetime64_any_dtype,
  57. is_datetime64_dtype,
  58. is_datetime64tz_dtype,
  59. is_dtype_equal,
  60. is_extension_array_dtype,
  61. is_float_dtype,
  62. is_object_dtype,
  63. is_period_dtype,
  64. is_sparse,
  65. is_string_dtype,
  66. is_timedelta64_dtype,
  67. pandas_dtype,
  68. )
  69. from pandas.core.dtypes.dtypes import (
  70. DatetimeTZDtype,
  71. ExtensionDtype,
  72. )
  73. from pandas.core.dtypes.missing import isna
  74. from pandas.core.arrays import datetimelike as dtl
  75. from pandas.core.arrays._ranges import generate_regular_range
  76. import pandas.core.common as com
  77. from pandas.tseries.frequencies import get_period_alias
  78. from pandas.tseries.offsets import (
  79. Day,
  80. Tick,
  81. )
  82. if TYPE_CHECKING:
  83. from pandas import DataFrame
  84. from pandas.core.arrays import PeriodArray
  85. _midnight = time(0, 0)
  86. def tz_to_dtype(tz: tzinfo | None, unit: str = "ns"):
  87. """
  88. Return a datetime64[ns] dtype appropriate for the given timezone.
  89. Parameters
  90. ----------
  91. tz : tzinfo or None
  92. unit : str, default "ns"
  93. Returns
  94. -------
  95. np.dtype or Datetime64TZDType
  96. """
  97. if tz is None:
  98. return np.dtype(f"M8[{unit}]")
  99. else:
  100. return DatetimeTZDtype(tz=tz, unit=unit)
  101. def _field_accessor(name: str, field: str, docstring=None):
  102. def f(self):
  103. values = self._local_timestamps()
  104. if field in self._bool_ops:
  105. result: np.ndarray
  106. if field.endswith(("start", "end")):
  107. freq = self.freq
  108. month_kw = 12
  109. if freq:
  110. kwds = freq.kwds
  111. month_kw = kwds.get("startingMonth", kwds.get("month", 12))
  112. result = fields.get_start_end_field(
  113. values, field, self.freqstr, month_kw, reso=self._creso
  114. )
  115. else:
  116. result = fields.get_date_field(values, field, reso=self._creso)
  117. # these return a boolean by-definition
  118. return result
  119. if field in self._object_ops:
  120. result = fields.get_date_name_field(values, field, reso=self._creso)
  121. result = self._maybe_mask_results(result, fill_value=None)
  122. else:
  123. result = fields.get_date_field(values, field, reso=self._creso)
  124. result = self._maybe_mask_results(
  125. result, fill_value=None, convert="float64"
  126. )
  127. return result
  128. f.__name__ = name
  129. f.__doc__ = docstring
  130. return property(f)
  131. class DatetimeArray(dtl.TimelikeOps, dtl.DatelikeOps):
  132. """
  133. Pandas ExtensionArray for tz-naive or tz-aware datetime data.
  134. .. warning::
  135. DatetimeArray is currently experimental, and its API may change
  136. without warning. In particular, :attr:`DatetimeArray.dtype` is
  137. expected to change to always be an instance of an ``ExtensionDtype``
  138. subclass.
  139. Parameters
  140. ----------
  141. values : Series, Index, DatetimeArray, ndarray
  142. The datetime data.
  143. For DatetimeArray `values` (or a Series or Index boxing one),
  144. `dtype` and `freq` will be extracted from `values`.
  145. dtype : numpy.dtype or DatetimeTZDtype
  146. Note that the only NumPy dtype allowed is 'datetime64[ns]'.
  147. freq : str or Offset, optional
  148. The frequency.
  149. copy : bool, default False
  150. Whether to copy the underlying array of values.
  151. Attributes
  152. ----------
  153. None
  154. Methods
  155. -------
  156. None
  157. """
  158. _typ = "datetimearray"
  159. _internal_fill_value = np.datetime64("NaT", "ns")
  160. _recognized_scalars = (datetime, np.datetime64)
  161. _is_recognized_dtype = is_datetime64_any_dtype
  162. _infer_matches = ("datetime", "datetime64", "date")
  163. @property
  164. def _scalar_type(self) -> type[Timestamp]:
  165. return Timestamp
  166. # define my properties & methods for delegation
  167. _bool_ops: list[str] = [
  168. "is_month_start",
  169. "is_month_end",
  170. "is_quarter_start",
  171. "is_quarter_end",
  172. "is_year_start",
  173. "is_year_end",
  174. "is_leap_year",
  175. ]
  176. _object_ops: list[str] = ["freq", "tz"]
  177. _field_ops: list[str] = [
  178. "year",
  179. "month",
  180. "day",
  181. "hour",
  182. "minute",
  183. "second",
  184. "weekday",
  185. "dayofweek",
  186. "day_of_week",
  187. "dayofyear",
  188. "day_of_year",
  189. "quarter",
  190. "days_in_month",
  191. "daysinmonth",
  192. "microsecond",
  193. "nanosecond",
  194. ]
  195. _other_ops: list[str] = ["date", "time", "timetz"]
  196. _datetimelike_ops: list[str] = (
  197. _field_ops + _object_ops + _bool_ops + _other_ops + ["unit"]
  198. )
  199. _datetimelike_methods: list[str] = [
  200. "to_period",
  201. "tz_localize",
  202. "tz_convert",
  203. "normalize",
  204. "strftime",
  205. "round",
  206. "floor",
  207. "ceil",
  208. "month_name",
  209. "day_name",
  210. "as_unit",
  211. ]
  212. # ndim is inherited from ExtensionArray, must exist to ensure
  213. # Timestamp.__richcmp__(DateTimeArray) operates pointwise
  214. # ensure that operations with numpy arrays defer to our implementation
  215. __array_priority__ = 1000
  216. # -----------------------------------------------------------------
  217. # Constructors
  218. _dtype: np.dtype | DatetimeTZDtype
  219. _freq: BaseOffset | None = None
  220. _default_dtype = DT64NS_DTYPE # used in TimeLikeOps.__init__
  221. @classmethod
  222. def _validate_dtype(cls, values, dtype):
  223. # used in TimeLikeOps.__init__
  224. _validate_dt64_dtype(values.dtype)
  225. dtype = _validate_dt64_dtype(dtype)
  226. return dtype
  227. # error: Signature of "_simple_new" incompatible with supertype "NDArrayBacked"
  228. @classmethod
  229. def _simple_new( # type: ignore[override]
  230. cls,
  231. values: np.ndarray,
  232. freq: BaseOffset | None = None,
  233. dtype=DT64NS_DTYPE,
  234. ) -> DatetimeArray:
  235. assert isinstance(values, np.ndarray)
  236. assert dtype.kind == "M"
  237. if isinstance(dtype, np.dtype):
  238. assert dtype == values.dtype
  239. assert not is_unitless(dtype)
  240. else:
  241. # DatetimeTZDtype. If we have e.g. DatetimeTZDtype[us, UTC],
  242. # then values.dtype should be M8[us].
  243. assert dtype._creso == get_unit_from_dtype(values.dtype)
  244. result = super()._simple_new(values, dtype)
  245. result._freq = freq
  246. return result
  247. @classmethod
  248. def _from_sequence(cls, scalars, *, dtype=None, copy: bool = False):
  249. return cls._from_sequence_not_strict(scalars, dtype=dtype, copy=copy)
  250. @classmethod
  251. def _from_sequence_not_strict(
  252. cls,
  253. data,
  254. *,
  255. dtype=None,
  256. copy: bool = False,
  257. tz=lib.no_default,
  258. freq: str | BaseOffset | lib.NoDefault | None = lib.no_default,
  259. dayfirst: bool = False,
  260. yearfirst: bool = False,
  261. ambiguous: TimeAmbiguous = "raise",
  262. ):
  263. """
  264. A non-strict version of _from_sequence, called from DatetimeIndex.__new__.
  265. """
  266. explicit_none = freq is None
  267. freq = freq if freq is not lib.no_default else None
  268. freq, freq_infer = dtl.maybe_infer_freq(freq)
  269. # if the user either explicitly passes tz=None or a tz-naive dtype, we
  270. # disallows inferring a tz.
  271. explicit_tz_none = tz is None
  272. if tz is lib.no_default:
  273. tz = None
  274. else:
  275. tz = timezones.maybe_get_tz(tz)
  276. dtype = _validate_dt64_dtype(dtype)
  277. # if dtype has an embedded tz, capture it
  278. tz = _validate_tz_from_dtype(dtype, tz, explicit_tz_none)
  279. unit = None
  280. if dtype is not None:
  281. if isinstance(dtype, np.dtype):
  282. unit = np.datetime_data(dtype)[0]
  283. else:
  284. # DatetimeTZDtype
  285. unit = dtype.unit
  286. subarr, tz, inferred_freq = _sequence_to_dt64ns(
  287. data,
  288. copy=copy,
  289. tz=tz,
  290. dayfirst=dayfirst,
  291. yearfirst=yearfirst,
  292. ambiguous=ambiguous,
  293. out_unit=unit,
  294. )
  295. # We have to call this again after possibly inferring a tz above
  296. _validate_tz_from_dtype(dtype, tz, explicit_tz_none)
  297. if tz is not None and explicit_tz_none:
  298. raise ValueError(
  299. "Passed data is timezone-aware, incompatible with 'tz=None'. "
  300. "Use obj.tz_localize(None) instead."
  301. )
  302. freq, freq_infer = dtl.validate_inferred_freq(freq, inferred_freq, freq_infer)
  303. if explicit_none:
  304. freq = None
  305. data_unit = np.datetime_data(subarr.dtype)[0]
  306. data_dtype = tz_to_dtype(tz, data_unit)
  307. result = cls._simple_new(subarr, freq=freq, dtype=data_dtype)
  308. if unit is not None and unit != result.unit:
  309. # If unit was specified in user-passed dtype, cast to it here
  310. result = result.as_unit(unit)
  311. if inferred_freq is None and freq is not None:
  312. # this condition precludes `freq_infer`
  313. cls._validate_frequency(result, freq, ambiguous=ambiguous)
  314. elif freq_infer:
  315. # Set _freq directly to bypass duplicative _validate_frequency
  316. # check.
  317. result._freq = to_offset(result.inferred_freq)
  318. return result
  319. # error: Signature of "_generate_range" incompatible with supertype
  320. # "DatetimeLikeArrayMixin"
  321. @classmethod
  322. def _generate_range( # type: ignore[override]
  323. cls,
  324. start,
  325. end,
  326. periods,
  327. freq,
  328. tz=None,
  329. normalize: bool = False,
  330. ambiguous: TimeAmbiguous = "raise",
  331. nonexistent: TimeNonexistent = "raise",
  332. inclusive: IntervalClosedType = "both",
  333. *,
  334. unit: str | None = None,
  335. ) -> DatetimeArray:
  336. periods = dtl.validate_periods(periods)
  337. if freq is None and any(x is None for x in [periods, start, end]):
  338. raise ValueError("Must provide freq argument if no data is supplied")
  339. if com.count_not_none(start, end, periods, freq) != 3:
  340. raise ValueError(
  341. "Of the four parameters: start, end, periods, "
  342. "and freq, exactly three must be specified"
  343. )
  344. freq = to_offset(freq)
  345. if start is not None:
  346. start = Timestamp(start)
  347. if end is not None:
  348. end = Timestamp(end)
  349. if start is NaT or end is NaT:
  350. raise ValueError("Neither `start` nor `end` can be NaT")
  351. if unit is not None:
  352. if unit not in ["s", "ms", "us", "ns"]:
  353. raise ValueError("'unit' must be one of 's', 'ms', 'us', 'ns'")
  354. else:
  355. unit = "ns"
  356. if start is not None and unit is not None:
  357. start = start.as_unit(unit, round_ok=False)
  358. if end is not None and unit is not None:
  359. end = end.as_unit(unit, round_ok=False)
  360. left_inclusive, right_inclusive = validate_inclusive(inclusive)
  361. start, end = _maybe_normalize_endpoints(start, end, normalize)
  362. tz = _infer_tz_from_endpoints(start, end, tz)
  363. if tz is not None:
  364. # Localize the start and end arguments
  365. start_tz = None if start is None else start.tz
  366. end_tz = None if end is None else end.tz
  367. start = _maybe_localize_point(
  368. start, start_tz, start, freq, tz, ambiguous, nonexistent
  369. )
  370. end = _maybe_localize_point(
  371. end, end_tz, end, freq, tz, ambiguous, nonexistent
  372. )
  373. if freq is not None:
  374. # We break Day arithmetic (fixed 24 hour) here and opt for
  375. # Day to mean calendar day (23/24/25 hour). Therefore, strip
  376. # tz info from start and day to avoid DST arithmetic
  377. if isinstance(freq, Day):
  378. if start is not None:
  379. start = start.tz_localize(None)
  380. if end is not None:
  381. end = end.tz_localize(None)
  382. if isinstance(freq, Tick):
  383. i8values = generate_regular_range(start, end, periods, freq, unit=unit)
  384. else:
  385. xdr = _generate_range(
  386. start=start, end=end, periods=periods, offset=freq, unit=unit
  387. )
  388. i8values = np.array([x._value for x in xdr], dtype=np.int64)
  389. endpoint_tz = start.tz if start is not None else end.tz
  390. if tz is not None and endpoint_tz is None:
  391. if not timezones.is_utc(tz):
  392. # short-circuit tz_localize_to_utc which would make
  393. # an unnecessary copy with UTC but be a no-op.
  394. creso = abbrev_to_npy_unit(unit)
  395. i8values = tzconversion.tz_localize_to_utc(
  396. i8values,
  397. tz,
  398. ambiguous=ambiguous,
  399. nonexistent=nonexistent,
  400. creso=creso,
  401. )
  402. # i8values is localized datetime64 array -> have to convert
  403. # start/end as well to compare
  404. if start is not None:
  405. start = start.tz_localize(tz, ambiguous, nonexistent)
  406. if end is not None:
  407. end = end.tz_localize(tz, ambiguous, nonexistent)
  408. else:
  409. # Create a linearly spaced date_range in local time
  410. # Nanosecond-granularity timestamps aren't always correctly
  411. # representable with doubles, so we limit the range that we
  412. # pass to np.linspace as much as possible
  413. i8values = (
  414. np.linspace(0, end._value - start._value, periods, dtype="int64")
  415. + start._value
  416. )
  417. if i8values.dtype != "i8":
  418. # 2022-01-09 I (brock) am not sure if it is possible for this
  419. # to overflow and cast to e.g. f8, but if it does we need to cast
  420. i8values = i8values.astype("i8")
  421. if start == end:
  422. if not left_inclusive and not right_inclusive:
  423. i8values = i8values[1:-1]
  424. else:
  425. start_i8 = Timestamp(start)._value
  426. end_i8 = Timestamp(end)._value
  427. if not left_inclusive or not right_inclusive:
  428. if not left_inclusive and len(i8values) and i8values[0] == start_i8:
  429. i8values = i8values[1:]
  430. if not right_inclusive and len(i8values) and i8values[-1] == end_i8:
  431. i8values = i8values[:-1]
  432. dt64_values = i8values.view(f"datetime64[{unit}]")
  433. dtype = tz_to_dtype(tz, unit=unit)
  434. return cls._simple_new(dt64_values, freq=freq, dtype=dtype)
  435. # -----------------------------------------------------------------
  436. # DatetimeLike Interface
  437. def _unbox_scalar(self, value) -> np.datetime64:
  438. if not isinstance(value, self._scalar_type) and value is not NaT:
  439. raise ValueError("'value' should be a Timestamp.")
  440. self._check_compatible_with(value)
  441. if value is NaT:
  442. return np.datetime64(value._value, self.unit)
  443. else:
  444. return value.as_unit(self.unit).asm8
  445. def _scalar_from_string(self, value) -> Timestamp | NaTType:
  446. return Timestamp(value, tz=self.tz)
  447. def _check_compatible_with(self, other) -> None:
  448. if other is NaT:
  449. return
  450. self._assert_tzawareness_compat(other)
  451. # -----------------------------------------------------------------
  452. # Descriptive Properties
  453. def _box_func(self, x: np.datetime64) -> Timestamp | NaTType:
  454. # GH#42228
  455. value = x.view("i8")
  456. ts = Timestamp._from_value_and_reso(value, reso=self._creso, tz=self.tz)
  457. return ts
  458. @property
  459. # error: Return type "Union[dtype, DatetimeTZDtype]" of "dtype"
  460. # incompatible with return type "ExtensionDtype" in supertype
  461. # "ExtensionArray"
  462. def dtype(self) -> np.dtype | DatetimeTZDtype: # type: ignore[override]
  463. """
  464. The dtype for the DatetimeArray.
  465. .. warning::
  466. A future version of pandas will change dtype to never be a
  467. ``numpy.dtype``. Instead, :attr:`DatetimeArray.dtype` will
  468. always be an instance of an ``ExtensionDtype`` subclass.
  469. Returns
  470. -------
  471. numpy.dtype or DatetimeTZDtype
  472. If the values are tz-naive, then ``np.dtype('datetime64[ns]')``
  473. is returned.
  474. If the values are tz-aware, then the ``DatetimeTZDtype``
  475. is returned.
  476. """
  477. return self._dtype
  478. @property
  479. def tz(self) -> tzinfo | None:
  480. """
  481. Return the timezone.
  482. Returns
  483. -------
  484. datetime.tzinfo, pytz.tzinfo.BaseTZInfo, dateutil.tz.tz.tzfile, or None
  485. Returns None when the array is tz-naive.
  486. """
  487. # GH 18595
  488. return getattr(self.dtype, "tz", None)
  489. @tz.setter
  490. def tz(self, value):
  491. # GH 3746: Prevent localizing or converting the index by setting tz
  492. raise AttributeError(
  493. "Cannot directly set timezone. Use tz_localize() "
  494. "or tz_convert() as appropriate"
  495. )
  496. @property
  497. def tzinfo(self) -> tzinfo | None:
  498. """
  499. Alias for tz attribute
  500. """
  501. return self.tz
  502. @property # NB: override with cache_readonly in immutable subclasses
  503. def is_normalized(self) -> bool:
  504. """
  505. Returns True if all of the dates are at midnight ("no time")
  506. """
  507. return is_date_array_normalized(self.asi8, self.tz, reso=self._creso)
  508. @property # NB: override with cache_readonly in immutable subclasses
  509. def _resolution_obj(self) -> Resolution:
  510. return get_resolution(self.asi8, self.tz, reso=self._creso)
  511. # ----------------------------------------------------------------
  512. # Array-Like / EA-Interface Methods
  513. def __array__(self, dtype=None) -> np.ndarray:
  514. if dtype is None and self.tz:
  515. # The default for tz-aware is object, to preserve tz info
  516. dtype = object
  517. return super().__array__(dtype=dtype)
  518. def __iter__(self) -> Iterator:
  519. """
  520. Return an iterator over the boxed values
  521. Yields
  522. ------
  523. tstamp : Timestamp
  524. """
  525. if self.ndim > 1:
  526. for i in range(len(self)):
  527. yield self[i]
  528. else:
  529. # convert in chunks of 10k for efficiency
  530. data = self.asi8
  531. length = len(self)
  532. chunksize = 10000
  533. chunks = (length // chunksize) + 1
  534. for i in range(chunks):
  535. start_i = i * chunksize
  536. end_i = min((i + 1) * chunksize, length)
  537. converted = ints_to_pydatetime(
  538. data[start_i:end_i],
  539. tz=self.tz,
  540. box="timestamp",
  541. reso=self._creso,
  542. )
  543. yield from converted
  544. def astype(self, dtype, copy: bool = True):
  545. # We handle
  546. # --> datetime
  547. # --> period
  548. # DatetimeLikeArrayMixin Super handles the rest.
  549. dtype = pandas_dtype(dtype)
  550. if is_dtype_equal(dtype, self.dtype):
  551. if copy:
  552. return self.copy()
  553. return self
  554. elif isinstance(dtype, ExtensionDtype):
  555. if not isinstance(dtype, DatetimeTZDtype):
  556. # e.g. Sparse[datetime64[ns]]
  557. return super().astype(dtype, copy=copy)
  558. elif self.tz is None:
  559. # pre-2.0 this did self.tz_localize(dtype.tz), which did not match
  560. # the Series behavior which did
  561. # values.tz_localize("UTC").tz_convert(dtype.tz)
  562. raise TypeError(
  563. "Cannot use .astype to convert from timezone-naive dtype to "
  564. "timezone-aware dtype. Use obj.tz_localize instead or "
  565. "series.dt.tz_localize instead"
  566. )
  567. else:
  568. # tzaware unit conversion e.g. datetime64[s, UTC]
  569. np_dtype = np.dtype(dtype.str)
  570. res_values = astype_overflowsafe(self._ndarray, np_dtype, copy=copy)
  571. return type(self)._simple_new(res_values, dtype=dtype, freq=self.freq)
  572. elif (
  573. self.tz is None
  574. and is_datetime64_dtype(dtype)
  575. and not is_unitless(dtype)
  576. and is_supported_unit(get_unit_from_dtype(dtype))
  577. ):
  578. # unit conversion e.g. datetime64[s]
  579. res_values = astype_overflowsafe(self._ndarray, dtype, copy=True)
  580. return type(self)._simple_new(res_values, dtype=res_values.dtype)
  581. # TODO: preserve freq?
  582. elif self.tz is not None and is_datetime64_dtype(dtype):
  583. # pre-2.0 behavior for DTA/DTI was
  584. # values.tz_convert("UTC").tz_localize(None), which did not match
  585. # the Series behavior
  586. raise TypeError(
  587. "Cannot use .astype to convert from timezone-aware dtype to "
  588. "timezone-naive dtype. Use obj.tz_localize(None) or "
  589. "obj.tz_convert('UTC').tz_localize(None) instead."
  590. )
  591. elif (
  592. self.tz is None
  593. and is_datetime64_dtype(dtype)
  594. and dtype != self.dtype
  595. and is_unitless(dtype)
  596. ):
  597. raise TypeError(
  598. "Casting to unit-less dtype 'datetime64' is not supported. "
  599. "Pass e.g. 'datetime64[ns]' instead."
  600. )
  601. elif is_period_dtype(dtype):
  602. return self.to_period(freq=dtype.freq)
  603. return dtl.DatetimeLikeArrayMixin.astype(self, dtype, copy)
  604. # -----------------------------------------------------------------
  605. # Rendering Methods
  606. def _format_native_types(
  607. self, *, na_rep: str | float = "NaT", date_format=None, **kwargs
  608. ) -> npt.NDArray[np.object_]:
  609. from pandas.io.formats.format import get_format_datetime64_from_values
  610. fmt = get_format_datetime64_from_values(self, date_format)
  611. return tslib.format_array_from_datetime(
  612. self.asi8, tz=self.tz, format=fmt, na_rep=na_rep, reso=self._creso
  613. )
  614. # -----------------------------------------------------------------
  615. # Comparison Methods
  616. def _has_same_tz(self, other) -> bool:
  617. # vzone shouldn't be None if value is non-datetime like
  618. if isinstance(other, np.datetime64):
  619. # convert to Timestamp as np.datetime64 doesn't have tz attr
  620. other = Timestamp(other)
  621. if not hasattr(other, "tzinfo"):
  622. return False
  623. other_tz = other.tzinfo
  624. return timezones.tz_compare(self.tzinfo, other_tz)
  625. def _assert_tzawareness_compat(self, other) -> None:
  626. # adapted from _Timestamp._assert_tzawareness_compat
  627. other_tz = getattr(other, "tzinfo", None)
  628. other_dtype = getattr(other, "dtype", None)
  629. if is_datetime64tz_dtype(other_dtype):
  630. # Get tzinfo from Series dtype
  631. other_tz = other.dtype.tz
  632. if other is NaT:
  633. # pd.NaT quacks both aware and naive
  634. pass
  635. elif self.tz is None:
  636. if other_tz is not None:
  637. raise TypeError(
  638. "Cannot compare tz-naive and tz-aware datetime-like objects."
  639. )
  640. elif other_tz is None:
  641. raise TypeError(
  642. "Cannot compare tz-naive and tz-aware datetime-like objects"
  643. )
  644. # -----------------------------------------------------------------
  645. # Arithmetic Methods
  646. def _add_offset(self, offset) -> DatetimeArray:
  647. assert not isinstance(offset, Tick)
  648. if self.tz is not None:
  649. values = self.tz_localize(None)
  650. else:
  651. values = self
  652. try:
  653. result = offset._apply_array(values).view(values.dtype)
  654. except NotImplementedError:
  655. warnings.warn(
  656. "Non-vectorized DateOffset being applied to Series or DatetimeIndex.",
  657. PerformanceWarning,
  658. stacklevel=find_stack_level(),
  659. )
  660. result = self.astype("O") + offset
  661. result = type(self)._from_sequence(result).as_unit(self.unit)
  662. if not len(self):
  663. # GH#30336 _from_sequence won't be able to infer self.tz
  664. return result.tz_localize(self.tz)
  665. else:
  666. result = DatetimeArray._simple_new(result, dtype=result.dtype)
  667. if self.tz is not None:
  668. result = result.tz_localize(self.tz)
  669. return result
  670. # -----------------------------------------------------------------
  671. # Timezone Conversion and Localization Methods
  672. def _local_timestamps(self) -> npt.NDArray[np.int64]:
  673. """
  674. Convert to an i8 (unix-like nanosecond timestamp) representation
  675. while keeping the local timezone and not using UTC.
  676. This is used to calculate time-of-day information as if the timestamps
  677. were timezone-naive.
  678. """
  679. if self.tz is None or timezones.is_utc(self.tz):
  680. # Avoid the copy that would be made in tzconversion
  681. return self.asi8
  682. return tz_convert_from_utc(self.asi8, self.tz, reso=self._creso)
  683. def tz_convert(self, tz) -> DatetimeArray:
  684. """
  685. Convert tz-aware Datetime Array/Index from one time zone to another.
  686. Parameters
  687. ----------
  688. tz : str, pytz.timezone, dateutil.tz.tzfile, datetime.tzinfo or None
  689. Time zone for time. Corresponding timestamps would be converted
  690. to this time zone of the Datetime Array/Index. A `tz` of None will
  691. convert to UTC and remove the timezone information.
  692. Returns
  693. -------
  694. Array or Index
  695. Raises
  696. ------
  697. TypeError
  698. If Datetime Array/Index is tz-naive.
  699. See Also
  700. --------
  701. DatetimeIndex.tz : A timezone that has a variable offset from UTC.
  702. DatetimeIndex.tz_localize : Localize tz-naive DatetimeIndex to a
  703. given time zone, or remove timezone from a tz-aware DatetimeIndex.
  704. Examples
  705. --------
  706. With the `tz` parameter, we can change the DatetimeIndex
  707. to other time zones:
  708. >>> dti = pd.date_range(start='2014-08-01 09:00',
  709. ... freq='H', periods=3, tz='Europe/Berlin')
  710. >>> dti
  711. DatetimeIndex(['2014-08-01 09:00:00+02:00',
  712. '2014-08-01 10:00:00+02:00',
  713. '2014-08-01 11:00:00+02:00'],
  714. dtype='datetime64[ns, Europe/Berlin]', freq='H')
  715. >>> dti.tz_convert('US/Central')
  716. DatetimeIndex(['2014-08-01 02:00:00-05:00',
  717. '2014-08-01 03:00:00-05:00',
  718. '2014-08-01 04:00:00-05:00'],
  719. dtype='datetime64[ns, US/Central]', freq='H')
  720. With the ``tz=None``, we can remove the timezone (after converting
  721. to UTC if necessary):
  722. >>> dti = pd.date_range(start='2014-08-01 09:00', freq='H',
  723. ... periods=3, tz='Europe/Berlin')
  724. >>> dti
  725. DatetimeIndex(['2014-08-01 09:00:00+02:00',
  726. '2014-08-01 10:00:00+02:00',
  727. '2014-08-01 11:00:00+02:00'],
  728. dtype='datetime64[ns, Europe/Berlin]', freq='H')
  729. >>> dti.tz_convert(None)
  730. DatetimeIndex(['2014-08-01 07:00:00',
  731. '2014-08-01 08:00:00',
  732. '2014-08-01 09:00:00'],
  733. dtype='datetime64[ns]', freq='H')
  734. """
  735. tz = timezones.maybe_get_tz(tz)
  736. if self.tz is None:
  737. # tz naive, use tz_localize
  738. raise TypeError(
  739. "Cannot convert tz-naive timestamps, use tz_localize to localize"
  740. )
  741. # No conversion since timestamps are all UTC to begin with
  742. dtype = tz_to_dtype(tz, unit=self.unit)
  743. return self._simple_new(self._ndarray, dtype=dtype, freq=self.freq)
  744. @dtl.ravel_compat
  745. def tz_localize(
  746. self,
  747. tz,
  748. ambiguous: TimeAmbiguous = "raise",
  749. nonexistent: TimeNonexistent = "raise",
  750. ) -> DatetimeArray:
  751. """
  752. Localize tz-naive Datetime Array/Index to tz-aware Datetime Array/Index.
  753. This method takes a time zone (tz) naive Datetime Array/Index object
  754. and makes this time zone aware. It does not move the time to another
  755. time zone.
  756. This method can also be used to do the inverse -- to create a time
  757. zone unaware object from an aware object. To that end, pass `tz=None`.
  758. Parameters
  759. ----------
  760. tz : str, pytz.timezone, dateutil.tz.tzfile, datetime.tzinfo or None
  761. Time zone to convert timestamps to. Passing ``None`` will
  762. remove the time zone information preserving local time.
  763. ambiguous : 'infer', 'NaT', bool array, default 'raise'
  764. When clocks moved backward due to DST, ambiguous times may arise.
  765. For example in Central European Time (UTC+01), when going from
  766. 03:00 DST to 02:00 non-DST, 02:30:00 local time occurs both at
  767. 00:30:00 UTC and at 01:30:00 UTC. In such a situation, the
  768. `ambiguous` parameter dictates how ambiguous times should be
  769. handled.
  770. - 'infer' will attempt to infer fall dst-transition hours based on
  771. order
  772. - bool-ndarray where True signifies a DST time, False signifies a
  773. non-DST time (note that this flag is only applicable for
  774. ambiguous times)
  775. - 'NaT' will return NaT where there are ambiguous times
  776. - 'raise' will raise an AmbiguousTimeError if there are ambiguous
  777. times.
  778. nonexistent : 'shift_forward', 'shift_backward, 'NaT', timedelta, \
  779. default 'raise'
  780. A nonexistent time does not exist in a particular timezone
  781. where clocks moved forward due to DST.
  782. - 'shift_forward' will shift the nonexistent time forward to the
  783. closest existing time
  784. - 'shift_backward' will shift the nonexistent time backward to the
  785. closest existing time
  786. - 'NaT' will return NaT where there are nonexistent times
  787. - timedelta objects will shift nonexistent times by the timedelta
  788. - 'raise' will raise an NonExistentTimeError if there are
  789. nonexistent times.
  790. Returns
  791. -------
  792. Same type as self
  793. Array/Index converted to the specified time zone.
  794. Raises
  795. ------
  796. TypeError
  797. If the Datetime Array/Index is tz-aware and tz is not None.
  798. See Also
  799. --------
  800. DatetimeIndex.tz_convert : Convert tz-aware DatetimeIndex from
  801. one time zone to another.
  802. Examples
  803. --------
  804. >>> tz_naive = pd.date_range('2018-03-01 09:00', periods=3)
  805. >>> tz_naive
  806. DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00',
  807. '2018-03-03 09:00:00'],
  808. dtype='datetime64[ns]', freq='D')
  809. Localize DatetimeIndex in US/Eastern time zone:
  810. >>> tz_aware = tz_naive.tz_localize(tz='US/Eastern')
  811. >>> tz_aware
  812. DatetimeIndex(['2018-03-01 09:00:00-05:00',
  813. '2018-03-02 09:00:00-05:00',
  814. '2018-03-03 09:00:00-05:00'],
  815. dtype='datetime64[ns, US/Eastern]', freq=None)
  816. With the ``tz=None``, we can remove the time zone information
  817. while keeping the local time (not converted to UTC):
  818. >>> tz_aware.tz_localize(None)
  819. DatetimeIndex(['2018-03-01 09:00:00', '2018-03-02 09:00:00',
  820. '2018-03-03 09:00:00'],
  821. dtype='datetime64[ns]', freq=None)
  822. Be careful with DST changes. When there is sequential data, pandas can
  823. infer the DST time:
  824. >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:30:00',
  825. ... '2018-10-28 02:00:00',
  826. ... '2018-10-28 02:30:00',
  827. ... '2018-10-28 02:00:00',
  828. ... '2018-10-28 02:30:00',
  829. ... '2018-10-28 03:00:00',
  830. ... '2018-10-28 03:30:00']))
  831. >>> s.dt.tz_localize('CET', ambiguous='infer')
  832. 0 2018-10-28 01:30:00+02:00
  833. 1 2018-10-28 02:00:00+02:00
  834. 2 2018-10-28 02:30:00+02:00
  835. 3 2018-10-28 02:00:00+01:00
  836. 4 2018-10-28 02:30:00+01:00
  837. 5 2018-10-28 03:00:00+01:00
  838. 6 2018-10-28 03:30:00+01:00
  839. dtype: datetime64[ns, CET]
  840. In some cases, inferring the DST is impossible. In such cases, you can
  841. pass an ndarray to the ambiguous parameter to set the DST explicitly
  842. >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:20:00',
  843. ... '2018-10-28 02:36:00',
  844. ... '2018-10-28 03:46:00']))
  845. >>> s.dt.tz_localize('CET', ambiguous=np.array([True, True, False]))
  846. 0 2018-10-28 01:20:00+02:00
  847. 1 2018-10-28 02:36:00+02:00
  848. 2 2018-10-28 03:46:00+01:00
  849. dtype: datetime64[ns, CET]
  850. If the DST transition causes nonexistent times, you can shift these
  851. dates forward or backwards with a timedelta object or `'shift_forward'`
  852. or `'shift_backwards'`.
  853. >>> s = pd.to_datetime(pd.Series(['2015-03-29 02:30:00',
  854. ... '2015-03-29 03:30:00']))
  855. >>> s.dt.tz_localize('Europe/Warsaw', nonexistent='shift_forward')
  856. 0 2015-03-29 03:00:00+02:00
  857. 1 2015-03-29 03:30:00+02:00
  858. dtype: datetime64[ns, Europe/Warsaw]
  859. >>> s.dt.tz_localize('Europe/Warsaw', nonexistent='shift_backward')
  860. 0 2015-03-29 01:59:59.999999999+01:00
  861. 1 2015-03-29 03:30:00+02:00
  862. dtype: datetime64[ns, Europe/Warsaw]
  863. >>> s.dt.tz_localize('Europe/Warsaw', nonexistent=pd.Timedelta('1H'))
  864. 0 2015-03-29 03:30:00+02:00
  865. 1 2015-03-29 03:30:00+02:00
  866. dtype: datetime64[ns, Europe/Warsaw]
  867. """
  868. nonexistent_options = ("raise", "NaT", "shift_forward", "shift_backward")
  869. if nonexistent not in nonexistent_options and not isinstance(
  870. nonexistent, timedelta
  871. ):
  872. raise ValueError(
  873. "The nonexistent argument must be one of 'raise', "
  874. "'NaT', 'shift_forward', 'shift_backward' or "
  875. "a timedelta object"
  876. )
  877. if self.tz is not None:
  878. if tz is None:
  879. new_dates = tz_convert_from_utc(self.asi8, self.tz, reso=self._creso)
  880. else:
  881. raise TypeError("Already tz-aware, use tz_convert to convert.")
  882. else:
  883. tz = timezones.maybe_get_tz(tz)
  884. # Convert to UTC
  885. new_dates = tzconversion.tz_localize_to_utc(
  886. self.asi8,
  887. tz,
  888. ambiguous=ambiguous,
  889. nonexistent=nonexistent,
  890. creso=self._creso,
  891. )
  892. new_dates = new_dates.view(f"M8[{self.unit}]")
  893. dtype = tz_to_dtype(tz, unit=self.unit)
  894. freq = None
  895. if timezones.is_utc(tz) or (len(self) == 1 and not isna(new_dates[0])):
  896. # we can preserve freq
  897. # TODO: Also for fixed-offsets
  898. freq = self.freq
  899. elif tz is None and self.tz is None:
  900. # no-op
  901. freq = self.freq
  902. return self._simple_new(new_dates, dtype=dtype, freq=freq)
  903. # ----------------------------------------------------------------
  904. # Conversion Methods - Vectorized analogues of Timestamp methods
  905. def to_pydatetime(self) -> npt.NDArray[np.object_]:
  906. """
  907. Return an ndarray of datetime.datetime objects.
  908. Returns
  909. -------
  910. numpy.ndarray
  911. """
  912. return ints_to_pydatetime(self.asi8, tz=self.tz, reso=self._creso)
  913. def normalize(self) -> DatetimeArray:
  914. """
  915. Convert times to midnight.
  916. The time component of the date-time is converted to midnight i.e.
  917. 00:00:00. This is useful in cases, when the time does not matter.
  918. Length is unaltered. The timezones are unaffected.
  919. This method is available on Series with datetime values under
  920. the ``.dt`` accessor, and directly on Datetime Array/Index.
  921. Returns
  922. -------
  923. DatetimeArray, DatetimeIndex or Series
  924. The same type as the original data. Series will have the same
  925. name and index. DatetimeIndex will have the same name.
  926. See Also
  927. --------
  928. floor : Floor the datetimes to the specified freq.
  929. ceil : Ceil the datetimes to the specified freq.
  930. round : Round the datetimes to the specified freq.
  931. Examples
  932. --------
  933. >>> idx = pd.date_range(start='2014-08-01 10:00', freq='H',
  934. ... periods=3, tz='Asia/Calcutta')
  935. >>> idx
  936. DatetimeIndex(['2014-08-01 10:00:00+05:30',
  937. '2014-08-01 11:00:00+05:30',
  938. '2014-08-01 12:00:00+05:30'],
  939. dtype='datetime64[ns, Asia/Calcutta]', freq='H')
  940. >>> idx.normalize()
  941. DatetimeIndex(['2014-08-01 00:00:00+05:30',
  942. '2014-08-01 00:00:00+05:30',
  943. '2014-08-01 00:00:00+05:30'],
  944. dtype='datetime64[ns, Asia/Calcutta]', freq=None)
  945. """
  946. new_values = normalize_i8_timestamps(self.asi8, self.tz, reso=self._creso)
  947. dt64_values = new_values.view(self._ndarray.dtype)
  948. dta = type(self)._simple_new(dt64_values, dtype=dt64_values.dtype)
  949. dta = dta._with_freq("infer")
  950. if self.tz is not None:
  951. dta = dta.tz_localize(self.tz)
  952. return dta
  953. def to_period(self, freq=None) -> PeriodArray:
  954. """
  955. Cast to PeriodArray/Index at a particular frequency.
  956. Converts DatetimeArray/Index to PeriodArray/Index.
  957. Parameters
  958. ----------
  959. freq : str or Offset, optional
  960. One of pandas' :ref:`offset strings <timeseries.offset_aliases>`
  961. or an Offset object. Will be inferred by default.
  962. Returns
  963. -------
  964. PeriodArray/Index
  965. Raises
  966. ------
  967. ValueError
  968. When converting a DatetimeArray/Index with non-regular values,
  969. so that a frequency cannot be inferred.
  970. See Also
  971. --------
  972. PeriodIndex: Immutable ndarray holding ordinal values.
  973. DatetimeIndex.to_pydatetime: Return DatetimeIndex as object.
  974. Examples
  975. --------
  976. >>> df = pd.DataFrame({"y": [1, 2, 3]},
  977. ... index=pd.to_datetime(["2000-03-31 00:00:00",
  978. ... "2000-05-31 00:00:00",
  979. ... "2000-08-31 00:00:00"]))
  980. >>> df.index.to_period("M")
  981. PeriodIndex(['2000-03', '2000-05', '2000-08'],
  982. dtype='period[M]')
  983. Infer the daily frequency
  984. >>> idx = pd.date_range("2017-01-01", periods=2)
  985. >>> idx.to_period()
  986. PeriodIndex(['2017-01-01', '2017-01-02'],
  987. dtype='period[D]')
  988. """
  989. from pandas.core.arrays import PeriodArray
  990. if self.tz is not None:
  991. warnings.warn(
  992. "Converting to PeriodArray/Index representation "
  993. "will drop timezone information.",
  994. UserWarning,
  995. stacklevel=find_stack_level(),
  996. )
  997. if freq is None:
  998. freq = self.freqstr or self.inferred_freq
  999. if freq is None:
  1000. raise ValueError(
  1001. "You must pass a freq argument as current index has none."
  1002. )
  1003. res = get_period_alias(freq)
  1004. # https://github.com/pandas-dev/pandas/issues/33358
  1005. if res is None:
  1006. res = freq
  1007. freq = res
  1008. return PeriodArray._from_datetime64(self._ndarray, freq, tz=self.tz)
  1009. # -----------------------------------------------------------------
  1010. # Properties - Vectorized Timestamp Properties/Methods
  1011. def month_name(self, locale=None) -> npt.NDArray[np.object_]:
  1012. """
  1013. Return the month names with specified locale.
  1014. Parameters
  1015. ----------
  1016. locale : str, optional
  1017. Locale determining the language in which to return the month name.
  1018. Default is English locale (``'en_US.utf8'``). Use the command
  1019. ``locale -a`` on your terminal on Unix systems to find your locale
  1020. language code.
  1021. Returns
  1022. -------
  1023. Series or Index
  1024. Series or Index of month names.
  1025. Examples
  1026. --------
  1027. >>> s = pd.Series(pd.date_range(start='2018-01', freq='M', periods=3))
  1028. >>> s
  1029. 0 2018-01-31
  1030. 1 2018-02-28
  1031. 2 2018-03-31
  1032. dtype: datetime64[ns]
  1033. >>> s.dt.month_name()
  1034. 0 January
  1035. 1 February
  1036. 2 March
  1037. dtype: object
  1038. >>> idx = pd.date_range(start='2018-01', freq='M', periods=3)
  1039. >>> idx
  1040. DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31'],
  1041. dtype='datetime64[ns]', freq='M')
  1042. >>> idx.month_name()
  1043. Index(['January', 'February', 'March'], dtype='object')
  1044. Using the ``locale`` parameter you can set a different locale language,
  1045. for example: ``idx.month_name(locale='pt_BR.utf8')`` will return month
  1046. names in Brazilian Portuguese language.
  1047. >>> idx = pd.date_range(start='2018-01', freq='M', periods=3)
  1048. >>> idx
  1049. DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31'],
  1050. dtype='datetime64[ns]', freq='M')
  1051. >>> idx.month_name(locale='pt_BR.utf8') # doctest: +SKIP
  1052. Index(['Janeiro', 'Fevereiro', 'Março'], dtype='object')
  1053. """
  1054. values = self._local_timestamps()
  1055. result = fields.get_date_name_field(
  1056. values, "month_name", locale=locale, reso=self._creso
  1057. )
  1058. result = self._maybe_mask_results(result, fill_value=None)
  1059. return result
  1060. def day_name(self, locale=None) -> npt.NDArray[np.object_]:
  1061. """
  1062. Return the day names with specified locale.
  1063. Parameters
  1064. ----------
  1065. locale : str, optional
  1066. Locale determining the language in which to return the day name.
  1067. Default is English locale (``'en_US.utf8'``). Use the command
  1068. ``locale -a`` on your terminal on Unix systems to find your locale
  1069. language code.
  1070. Returns
  1071. -------
  1072. Series or Index
  1073. Series or Index of day names.
  1074. Examples
  1075. --------
  1076. >>> s = pd.Series(pd.date_range(start='2018-01-01', freq='D', periods=3))
  1077. >>> s
  1078. 0 2018-01-01
  1079. 1 2018-01-02
  1080. 2 2018-01-03
  1081. dtype: datetime64[ns]
  1082. >>> s.dt.day_name()
  1083. 0 Monday
  1084. 1 Tuesday
  1085. 2 Wednesday
  1086. dtype: object
  1087. >>> idx = pd.date_range(start='2018-01-01', freq='D', periods=3)
  1088. >>> idx
  1089. DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03'],
  1090. dtype='datetime64[ns]', freq='D')
  1091. >>> idx.day_name()
  1092. Index(['Monday', 'Tuesday', 'Wednesday'], dtype='object')
  1093. Using the ``locale`` parameter you can set a different locale language,
  1094. for example: ``idx.day_name(locale='pt_BR.utf8')`` will return day
  1095. names in Brazilian Portuguese language.
  1096. >>> idx = pd.date_range(start='2018-01-01', freq='D', periods=3)
  1097. >>> idx
  1098. DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03'],
  1099. dtype='datetime64[ns]', freq='D')
  1100. >>> idx.day_name(locale='pt_BR.utf8') # doctest: +SKIP
  1101. Index(['Segunda', 'Terça', 'Quarta'], dtype='object')
  1102. """
  1103. values = self._local_timestamps()
  1104. result = fields.get_date_name_field(
  1105. values, "day_name", locale=locale, reso=self._creso
  1106. )
  1107. result = self._maybe_mask_results(result, fill_value=None)
  1108. return result
  1109. @property
  1110. def time(self) -> npt.NDArray[np.object_]:
  1111. """
  1112. Returns numpy array of :class:`datetime.time` objects.
  1113. The time part of the Timestamps.
  1114. """
  1115. # If the Timestamps have a timezone that is not UTC,
  1116. # convert them into their i8 representation while
  1117. # keeping their timezone and not using UTC
  1118. timestamps = self._local_timestamps()
  1119. return ints_to_pydatetime(timestamps, box="time", reso=self._creso)
  1120. @property
  1121. def timetz(self) -> npt.NDArray[np.object_]:
  1122. """
  1123. Returns numpy array of :class:`datetime.time` objects with timezones.
  1124. The time part of the Timestamps.
  1125. """
  1126. return ints_to_pydatetime(self.asi8, self.tz, box="time", reso=self._creso)
  1127. @property
  1128. def date(self) -> npt.NDArray[np.object_]:
  1129. """
  1130. Returns numpy array of python :class:`datetime.date` objects.
  1131. Namely, the date part of Timestamps without time and
  1132. timezone information.
  1133. """
  1134. # If the Timestamps have a timezone that is not UTC,
  1135. # convert them into their i8 representation while
  1136. # keeping their timezone and not using UTC
  1137. timestamps = self._local_timestamps()
  1138. return ints_to_pydatetime(timestamps, box="date", reso=self._creso)
  1139. def isocalendar(self) -> DataFrame:
  1140. """
  1141. Calculate year, week, and day according to the ISO 8601 standard.
  1142. .. versionadded:: 1.1.0
  1143. Returns
  1144. -------
  1145. DataFrame
  1146. With columns year, week and day.
  1147. See Also
  1148. --------
  1149. Timestamp.isocalendar : Function return a 3-tuple containing ISO year,
  1150. week number, and weekday for the given Timestamp object.
  1151. datetime.date.isocalendar : Return a named tuple object with
  1152. three components: year, week and weekday.
  1153. Examples
  1154. --------
  1155. >>> idx = pd.date_range(start='2019-12-29', freq='D', periods=4)
  1156. >>> idx.isocalendar()
  1157. year week day
  1158. 2019-12-29 2019 52 7
  1159. 2019-12-30 2020 1 1
  1160. 2019-12-31 2020 1 2
  1161. 2020-01-01 2020 1 3
  1162. >>> idx.isocalendar().week
  1163. 2019-12-29 52
  1164. 2019-12-30 1
  1165. 2019-12-31 1
  1166. 2020-01-01 1
  1167. Freq: D, Name: week, dtype: UInt32
  1168. """
  1169. from pandas import DataFrame
  1170. values = self._local_timestamps()
  1171. sarray = fields.build_isocalendar_sarray(values, reso=self._creso)
  1172. iso_calendar_df = DataFrame(
  1173. sarray, columns=["year", "week", "day"], dtype="UInt32"
  1174. )
  1175. if self._hasna:
  1176. iso_calendar_df.iloc[self._isnan] = None
  1177. return iso_calendar_df
  1178. year = _field_accessor(
  1179. "year",
  1180. "Y",
  1181. """
  1182. The year of the datetime.
  1183. Examples
  1184. --------
  1185. >>> datetime_series = pd.Series(
  1186. ... pd.date_range("2000-01-01", periods=3, freq="Y")
  1187. ... )
  1188. >>> datetime_series
  1189. 0 2000-12-31
  1190. 1 2001-12-31
  1191. 2 2002-12-31
  1192. dtype: datetime64[ns]
  1193. >>> datetime_series.dt.year
  1194. 0 2000
  1195. 1 2001
  1196. 2 2002
  1197. dtype: int32
  1198. """,
  1199. )
  1200. month = _field_accessor(
  1201. "month",
  1202. "M",
  1203. """
  1204. The month as January=1, December=12.
  1205. Examples
  1206. --------
  1207. >>> datetime_series = pd.Series(
  1208. ... pd.date_range("2000-01-01", periods=3, freq="M")
  1209. ... )
  1210. >>> datetime_series
  1211. 0 2000-01-31
  1212. 1 2000-02-29
  1213. 2 2000-03-31
  1214. dtype: datetime64[ns]
  1215. >>> datetime_series.dt.month
  1216. 0 1
  1217. 1 2
  1218. 2 3
  1219. dtype: int32
  1220. """,
  1221. )
  1222. day = _field_accessor(
  1223. "day",
  1224. "D",
  1225. """
  1226. The day of the datetime.
  1227. Examples
  1228. --------
  1229. >>> datetime_series = pd.Series(
  1230. ... pd.date_range("2000-01-01", periods=3, freq="D")
  1231. ... )
  1232. >>> datetime_series
  1233. 0 2000-01-01
  1234. 1 2000-01-02
  1235. 2 2000-01-03
  1236. dtype: datetime64[ns]
  1237. >>> datetime_series.dt.day
  1238. 0 1
  1239. 1 2
  1240. 2 3
  1241. dtype: int32
  1242. """,
  1243. )
  1244. hour = _field_accessor(
  1245. "hour",
  1246. "h",
  1247. """
  1248. The hours of the datetime.
  1249. Examples
  1250. --------
  1251. >>> datetime_series = pd.Series(
  1252. ... pd.date_range("2000-01-01", periods=3, freq="h")
  1253. ... )
  1254. >>> datetime_series
  1255. 0 2000-01-01 00:00:00
  1256. 1 2000-01-01 01:00:00
  1257. 2 2000-01-01 02:00:00
  1258. dtype: datetime64[ns]
  1259. >>> datetime_series.dt.hour
  1260. 0 0
  1261. 1 1
  1262. 2 2
  1263. dtype: int32
  1264. """,
  1265. )
  1266. minute = _field_accessor(
  1267. "minute",
  1268. "m",
  1269. """
  1270. The minutes of the datetime.
  1271. Examples
  1272. --------
  1273. >>> datetime_series = pd.Series(
  1274. ... pd.date_range("2000-01-01", periods=3, freq="T")
  1275. ... )
  1276. >>> datetime_series
  1277. 0 2000-01-01 00:00:00
  1278. 1 2000-01-01 00:01:00
  1279. 2 2000-01-01 00:02:00
  1280. dtype: datetime64[ns]
  1281. >>> datetime_series.dt.minute
  1282. 0 0
  1283. 1 1
  1284. 2 2
  1285. dtype: int32
  1286. """,
  1287. )
  1288. second = _field_accessor(
  1289. "second",
  1290. "s",
  1291. """
  1292. The seconds of the datetime.
  1293. Examples
  1294. --------
  1295. >>> datetime_series = pd.Series(
  1296. ... pd.date_range("2000-01-01", periods=3, freq="s")
  1297. ... )
  1298. >>> datetime_series
  1299. 0 2000-01-01 00:00:00
  1300. 1 2000-01-01 00:00:01
  1301. 2 2000-01-01 00:00:02
  1302. dtype: datetime64[ns]
  1303. >>> datetime_series.dt.second
  1304. 0 0
  1305. 1 1
  1306. 2 2
  1307. dtype: int32
  1308. """,
  1309. )
  1310. microsecond = _field_accessor(
  1311. "microsecond",
  1312. "us",
  1313. """
  1314. The microseconds of the datetime.
  1315. Examples
  1316. --------
  1317. >>> datetime_series = pd.Series(
  1318. ... pd.date_range("2000-01-01", periods=3, freq="us")
  1319. ... )
  1320. >>> datetime_series
  1321. 0 2000-01-01 00:00:00.000000
  1322. 1 2000-01-01 00:00:00.000001
  1323. 2 2000-01-01 00:00:00.000002
  1324. dtype: datetime64[ns]
  1325. >>> datetime_series.dt.microsecond
  1326. 0 0
  1327. 1 1
  1328. 2 2
  1329. dtype: int32
  1330. """,
  1331. )
  1332. nanosecond = _field_accessor(
  1333. "nanosecond",
  1334. "ns",
  1335. """
  1336. The nanoseconds of the datetime.
  1337. Examples
  1338. --------
  1339. >>> datetime_series = pd.Series(
  1340. ... pd.date_range("2000-01-01", periods=3, freq="ns")
  1341. ... )
  1342. >>> datetime_series
  1343. 0 2000-01-01 00:00:00.000000000
  1344. 1 2000-01-01 00:00:00.000000001
  1345. 2 2000-01-01 00:00:00.000000002
  1346. dtype: datetime64[ns]
  1347. >>> datetime_series.dt.nanosecond
  1348. 0 0
  1349. 1 1
  1350. 2 2
  1351. dtype: int32
  1352. """,
  1353. )
  1354. _dayofweek_doc = """
  1355. The day of the week with Monday=0, Sunday=6.
  1356. Return the day of the week. It is assumed the week starts on
  1357. Monday, which is denoted by 0 and ends on Sunday which is denoted
  1358. by 6. This method is available on both Series with datetime
  1359. values (using the `dt` accessor) or DatetimeIndex.
  1360. Returns
  1361. -------
  1362. Series or Index
  1363. Containing integers indicating the day number.
  1364. See Also
  1365. --------
  1366. Series.dt.dayofweek : Alias.
  1367. Series.dt.weekday : Alias.
  1368. Series.dt.day_name : Returns the name of the day of the week.
  1369. Examples
  1370. --------
  1371. >>> s = pd.date_range('2016-12-31', '2017-01-08', freq='D').to_series()
  1372. >>> s.dt.dayofweek
  1373. 2016-12-31 5
  1374. 2017-01-01 6
  1375. 2017-01-02 0
  1376. 2017-01-03 1
  1377. 2017-01-04 2
  1378. 2017-01-05 3
  1379. 2017-01-06 4
  1380. 2017-01-07 5
  1381. 2017-01-08 6
  1382. Freq: D, dtype: int32
  1383. """
  1384. day_of_week = _field_accessor("day_of_week", "dow", _dayofweek_doc)
  1385. dayofweek = day_of_week
  1386. weekday = day_of_week
  1387. day_of_year = _field_accessor(
  1388. "dayofyear",
  1389. "doy",
  1390. """
  1391. The ordinal day of the year.
  1392. """,
  1393. )
  1394. dayofyear = day_of_year
  1395. quarter = _field_accessor(
  1396. "quarter",
  1397. "q",
  1398. """
  1399. The quarter of the date.
  1400. """,
  1401. )
  1402. days_in_month = _field_accessor(
  1403. "days_in_month",
  1404. "dim",
  1405. """
  1406. The number of days in the month.
  1407. """,
  1408. )
  1409. daysinmonth = days_in_month
  1410. _is_month_doc = """
  1411. Indicates whether the date is the {first_or_last} day of the month.
  1412. Returns
  1413. -------
  1414. Series or array
  1415. For Series, returns a Series with boolean values.
  1416. For DatetimeIndex, returns a boolean array.
  1417. See Also
  1418. --------
  1419. is_month_start : Return a boolean indicating whether the date
  1420. is the first day of the month.
  1421. is_month_end : Return a boolean indicating whether the date
  1422. is the last day of the month.
  1423. Examples
  1424. --------
  1425. This method is available on Series with datetime values under
  1426. the ``.dt`` accessor, and directly on DatetimeIndex.
  1427. >>> s = pd.Series(pd.date_range("2018-02-27", periods=3))
  1428. >>> s
  1429. 0 2018-02-27
  1430. 1 2018-02-28
  1431. 2 2018-03-01
  1432. dtype: datetime64[ns]
  1433. >>> s.dt.is_month_start
  1434. 0 False
  1435. 1 False
  1436. 2 True
  1437. dtype: bool
  1438. >>> s.dt.is_month_end
  1439. 0 False
  1440. 1 True
  1441. 2 False
  1442. dtype: bool
  1443. >>> idx = pd.date_range("2018-02-27", periods=3)
  1444. >>> idx.is_month_start
  1445. array([False, False, True])
  1446. >>> idx.is_month_end
  1447. array([False, True, False])
  1448. """
  1449. is_month_start = _field_accessor(
  1450. "is_month_start", "is_month_start", _is_month_doc.format(first_or_last="first")
  1451. )
  1452. is_month_end = _field_accessor(
  1453. "is_month_end", "is_month_end", _is_month_doc.format(first_or_last="last")
  1454. )
  1455. is_quarter_start = _field_accessor(
  1456. "is_quarter_start",
  1457. "is_quarter_start",
  1458. """
  1459. Indicator for whether the date is the first day of a quarter.
  1460. Returns
  1461. -------
  1462. is_quarter_start : Series or DatetimeIndex
  1463. The same type as the original data with boolean values. Series will
  1464. have the same name and index. DatetimeIndex will have the same
  1465. name.
  1466. See Also
  1467. --------
  1468. quarter : Return the quarter of the date.
  1469. is_quarter_end : Similar property for indicating the quarter end.
  1470. Examples
  1471. --------
  1472. This method is available on Series with datetime values under
  1473. the ``.dt`` accessor, and directly on DatetimeIndex.
  1474. >>> df = pd.DataFrame({'dates': pd.date_range("2017-03-30",
  1475. ... periods=4)})
  1476. >>> df.assign(quarter=df.dates.dt.quarter,
  1477. ... is_quarter_start=df.dates.dt.is_quarter_start)
  1478. dates quarter is_quarter_start
  1479. 0 2017-03-30 1 False
  1480. 1 2017-03-31 1 False
  1481. 2 2017-04-01 2 True
  1482. 3 2017-04-02 2 False
  1483. >>> idx = pd.date_range('2017-03-30', periods=4)
  1484. >>> idx
  1485. DatetimeIndex(['2017-03-30', '2017-03-31', '2017-04-01', '2017-04-02'],
  1486. dtype='datetime64[ns]', freq='D')
  1487. >>> idx.is_quarter_start
  1488. array([False, False, True, False])
  1489. """,
  1490. )
  1491. is_quarter_end = _field_accessor(
  1492. "is_quarter_end",
  1493. "is_quarter_end",
  1494. """
  1495. Indicator for whether the date is the last day of a quarter.
  1496. Returns
  1497. -------
  1498. is_quarter_end : Series or DatetimeIndex
  1499. The same type as the original data with boolean values. Series will
  1500. have the same name and index. DatetimeIndex will have the same
  1501. name.
  1502. See Also
  1503. --------
  1504. quarter : Return the quarter of the date.
  1505. is_quarter_start : Similar property indicating the quarter start.
  1506. Examples
  1507. --------
  1508. This method is available on Series with datetime values under
  1509. the ``.dt`` accessor, and directly on DatetimeIndex.
  1510. >>> df = pd.DataFrame({'dates': pd.date_range("2017-03-30",
  1511. ... periods=4)})
  1512. >>> df.assign(quarter=df.dates.dt.quarter,
  1513. ... is_quarter_end=df.dates.dt.is_quarter_end)
  1514. dates quarter is_quarter_end
  1515. 0 2017-03-30 1 False
  1516. 1 2017-03-31 1 True
  1517. 2 2017-04-01 2 False
  1518. 3 2017-04-02 2 False
  1519. >>> idx = pd.date_range('2017-03-30', periods=4)
  1520. >>> idx
  1521. DatetimeIndex(['2017-03-30', '2017-03-31', '2017-04-01', '2017-04-02'],
  1522. dtype='datetime64[ns]', freq='D')
  1523. >>> idx.is_quarter_end
  1524. array([False, True, False, False])
  1525. """,
  1526. )
  1527. is_year_start = _field_accessor(
  1528. "is_year_start",
  1529. "is_year_start",
  1530. """
  1531. Indicate whether the date is the first day of a year.
  1532. Returns
  1533. -------
  1534. Series or DatetimeIndex
  1535. The same type as the original data with boolean values. Series will
  1536. have the same name and index. DatetimeIndex will have the same
  1537. name.
  1538. See Also
  1539. --------
  1540. is_year_end : Similar property indicating the last day of the year.
  1541. Examples
  1542. --------
  1543. This method is available on Series with datetime values under
  1544. the ``.dt`` accessor, and directly on DatetimeIndex.
  1545. >>> dates = pd.Series(pd.date_range("2017-12-30", periods=3))
  1546. >>> dates
  1547. 0 2017-12-30
  1548. 1 2017-12-31
  1549. 2 2018-01-01
  1550. dtype: datetime64[ns]
  1551. >>> dates.dt.is_year_start
  1552. 0 False
  1553. 1 False
  1554. 2 True
  1555. dtype: bool
  1556. >>> idx = pd.date_range("2017-12-30", periods=3)
  1557. >>> idx
  1558. DatetimeIndex(['2017-12-30', '2017-12-31', '2018-01-01'],
  1559. dtype='datetime64[ns]', freq='D')
  1560. >>> idx.is_year_start
  1561. array([False, False, True])
  1562. """,
  1563. )
  1564. is_year_end = _field_accessor(
  1565. "is_year_end",
  1566. "is_year_end",
  1567. """
  1568. Indicate whether the date is the last day of the year.
  1569. Returns
  1570. -------
  1571. Series or DatetimeIndex
  1572. The same type as the original data with boolean values. Series will
  1573. have the same name and index. DatetimeIndex will have the same
  1574. name.
  1575. See Also
  1576. --------
  1577. is_year_start : Similar property indicating the start of the year.
  1578. Examples
  1579. --------
  1580. This method is available on Series with datetime values under
  1581. the ``.dt`` accessor, and directly on DatetimeIndex.
  1582. >>> dates = pd.Series(pd.date_range("2017-12-30", periods=3))
  1583. >>> dates
  1584. 0 2017-12-30
  1585. 1 2017-12-31
  1586. 2 2018-01-01
  1587. dtype: datetime64[ns]
  1588. >>> dates.dt.is_year_end
  1589. 0 False
  1590. 1 True
  1591. 2 False
  1592. dtype: bool
  1593. >>> idx = pd.date_range("2017-12-30", periods=3)
  1594. >>> idx
  1595. DatetimeIndex(['2017-12-30', '2017-12-31', '2018-01-01'],
  1596. dtype='datetime64[ns]', freq='D')
  1597. >>> idx.is_year_end
  1598. array([False, True, False])
  1599. """,
  1600. )
  1601. is_leap_year = _field_accessor(
  1602. "is_leap_year",
  1603. "is_leap_year",
  1604. """
  1605. Boolean indicator if the date belongs to a leap year.
  1606. A leap year is a year, which has 366 days (instead of 365) including
  1607. 29th of February as an intercalary day.
  1608. Leap years are years which are multiples of four with the exception
  1609. of years divisible by 100 but not by 400.
  1610. Returns
  1611. -------
  1612. Series or ndarray
  1613. Booleans indicating if dates belong to a leap year.
  1614. Examples
  1615. --------
  1616. This method is available on Series with datetime values under
  1617. the ``.dt`` accessor, and directly on DatetimeIndex.
  1618. >>> idx = pd.date_range("2012-01-01", "2015-01-01", freq="Y")
  1619. >>> idx
  1620. DatetimeIndex(['2012-12-31', '2013-12-31', '2014-12-31'],
  1621. dtype='datetime64[ns]', freq='A-DEC')
  1622. >>> idx.is_leap_year
  1623. array([ True, False, False])
  1624. >>> dates_series = pd.Series(idx)
  1625. >>> dates_series
  1626. 0 2012-12-31
  1627. 1 2013-12-31
  1628. 2 2014-12-31
  1629. dtype: datetime64[ns]
  1630. >>> dates_series.dt.is_leap_year
  1631. 0 True
  1632. 1 False
  1633. 2 False
  1634. dtype: bool
  1635. """,
  1636. )
  1637. def to_julian_date(self) -> npt.NDArray[np.float64]:
  1638. """
  1639. Convert Datetime Array to float64 ndarray of Julian Dates.
  1640. 0 Julian date is noon January 1, 4713 BC.
  1641. https://en.wikipedia.org/wiki/Julian_day
  1642. """
  1643. # http://mysite.verizon.net/aesir_research/date/jdalg2.htm
  1644. year = np.asarray(self.year)
  1645. month = np.asarray(self.month)
  1646. day = np.asarray(self.day)
  1647. testarr = month < 3
  1648. year[testarr] -= 1
  1649. month[testarr] += 12
  1650. return (
  1651. day
  1652. + np.fix((153 * month - 457) / 5)
  1653. + 365 * year
  1654. + np.floor(year / 4)
  1655. - np.floor(year / 100)
  1656. + np.floor(year / 400)
  1657. + 1_721_118.5
  1658. + (
  1659. self.hour
  1660. + self.minute / 60
  1661. + self.second / 3600
  1662. + self.microsecond / 3600 / 10**6
  1663. + self.nanosecond / 3600 / 10**9
  1664. )
  1665. / 24
  1666. )
  1667. # -----------------------------------------------------------------
  1668. # Reductions
  1669. def std(
  1670. self,
  1671. axis=None,
  1672. dtype=None,
  1673. out=None,
  1674. ddof: int = 1,
  1675. keepdims: bool = False,
  1676. skipna: bool = True,
  1677. ):
  1678. """
  1679. Return sample standard deviation over requested axis.
  1680. Normalized by N-1 by default. This can be changed using the ddof argument
  1681. Parameters
  1682. ----------
  1683. axis : int optional, default None
  1684. Axis for the function to be applied on.
  1685. For `Series` this parameter is unused and defaults to `None`.
  1686. ddof : int, default 1
  1687. Degrees of Freedom. The divisor used in calculations is N - ddof,
  1688. where N represents the number of elements.
  1689. skipna : bool, default True
  1690. Exclude NA/null values. If an entire row/column is NA, the result will be
  1691. NA.
  1692. Returns
  1693. -------
  1694. Timedelta
  1695. """
  1696. # Because std is translation-invariant, we can get self.std
  1697. # by calculating (self - Timestamp(0)).std, and we can do it
  1698. # without creating a copy by using a view on self._ndarray
  1699. from pandas.core.arrays import TimedeltaArray
  1700. # Find the td64 dtype with the same resolution as our dt64 dtype
  1701. dtype_str = self._ndarray.dtype.name.replace("datetime64", "timedelta64")
  1702. dtype = np.dtype(dtype_str)
  1703. tda = TimedeltaArray._simple_new(self._ndarray.view(dtype), dtype=dtype)
  1704. return tda.std(axis=axis, out=out, ddof=ddof, keepdims=keepdims, skipna=skipna)
  1705. # -------------------------------------------------------------------
  1706. # Constructor Helpers
  1707. def _sequence_to_dt64ns(
  1708. data,
  1709. *,
  1710. copy: bool = False,
  1711. tz: tzinfo | None = None,
  1712. dayfirst: bool = False,
  1713. yearfirst: bool = False,
  1714. ambiguous: TimeAmbiguous = "raise",
  1715. out_unit: str | None = None,
  1716. ):
  1717. """
  1718. Parameters
  1719. ----------
  1720. data : list-like
  1721. copy : bool, default False
  1722. tz : tzinfo or None, default None
  1723. dayfirst : bool, default False
  1724. yearfirst : bool, default False
  1725. ambiguous : str, bool, or arraylike, default 'raise'
  1726. See pandas._libs.tslibs.tzconversion.tz_localize_to_utc.
  1727. out_unit : str or None, default None
  1728. Desired output resolution.
  1729. Returns
  1730. -------
  1731. result : numpy.ndarray
  1732. The sequence converted to a numpy array with dtype ``datetime64[ns]``.
  1733. tz : tzinfo or None
  1734. Either the user-provided tzinfo or one inferred from the data.
  1735. inferred_freq : Tick or None
  1736. The inferred frequency of the sequence.
  1737. Raises
  1738. ------
  1739. TypeError : PeriodDType data is passed
  1740. """
  1741. inferred_freq = None
  1742. data, copy = dtl.ensure_arraylike_for_datetimelike(
  1743. data, copy, cls_name="DatetimeArray"
  1744. )
  1745. if isinstance(data, DatetimeArray):
  1746. inferred_freq = data.freq
  1747. # By this point we are assured to have either a numpy array or Index
  1748. data, copy = maybe_convert_dtype(data, copy, tz=tz)
  1749. data_dtype = getattr(data, "dtype", None)
  1750. out_dtype = DT64NS_DTYPE
  1751. if out_unit is not None:
  1752. out_dtype = np.dtype(f"M8[{out_unit}]")
  1753. if (
  1754. is_object_dtype(data_dtype)
  1755. or is_string_dtype(data_dtype)
  1756. or is_sparse(data_dtype)
  1757. ):
  1758. # TODO: We do not have tests specific to string-dtypes,
  1759. # also complex or categorical or other extension
  1760. copy = False
  1761. if lib.infer_dtype(data, skipna=False) == "integer":
  1762. data = data.astype(np.int64)
  1763. elif tz is not None and ambiguous == "raise":
  1764. # TODO: yearfirst/dayfirst/etc?
  1765. obj_data = np.asarray(data, dtype=object)
  1766. i8data = tslib.array_to_datetime_with_tz(obj_data, tz)
  1767. return i8data.view(DT64NS_DTYPE), tz, None
  1768. else:
  1769. # data comes back here as either i8 to denote UTC timestamps
  1770. # or M8[ns] to denote wall times
  1771. data, inferred_tz = objects_to_datetime64ns(
  1772. data,
  1773. dayfirst=dayfirst,
  1774. yearfirst=yearfirst,
  1775. allow_object=False,
  1776. )
  1777. if tz and inferred_tz:
  1778. # two timezones: convert to intended from base UTC repr
  1779. assert data.dtype == "i8"
  1780. # GH#42505
  1781. # by convention, these are _already_ UTC, e.g
  1782. return data.view(DT64NS_DTYPE), tz, None
  1783. elif inferred_tz:
  1784. tz = inferred_tz
  1785. data_dtype = data.dtype
  1786. # `data` may have originally been a Categorical[datetime64[ns, tz]],
  1787. # so we need to handle these types.
  1788. if is_datetime64tz_dtype(data_dtype):
  1789. # DatetimeArray -> ndarray
  1790. tz = _maybe_infer_tz(tz, data.tz)
  1791. result = data._ndarray
  1792. elif is_datetime64_dtype(data_dtype):
  1793. # tz-naive DatetimeArray or ndarray[datetime64]
  1794. data = getattr(data, "_ndarray", data)
  1795. new_dtype = data.dtype
  1796. data_unit = get_unit_from_dtype(new_dtype)
  1797. if not is_supported_unit(data_unit):
  1798. # Cast to the nearest supported unit, generally "s"
  1799. new_reso = get_supported_reso(data_unit)
  1800. new_unit = npy_unit_to_abbrev(new_reso)
  1801. new_dtype = np.dtype(f"M8[{new_unit}]")
  1802. data = astype_overflowsafe(data, dtype=new_dtype, copy=False)
  1803. data_unit = get_unit_from_dtype(new_dtype)
  1804. copy = False
  1805. if data.dtype.byteorder == ">":
  1806. # TODO: better way to handle this? non-copying alternative?
  1807. # without this, test_constructor_datetime64_bigendian fails
  1808. data = data.astype(data.dtype.newbyteorder("<"))
  1809. new_dtype = data.dtype
  1810. copy = False
  1811. if tz is not None:
  1812. # Convert tz-naive to UTC
  1813. # TODO: if tz is UTC, are there situations where we *don't* want a
  1814. # copy? tz_localize_to_utc always makes one.
  1815. shape = data.shape
  1816. if data.ndim > 1:
  1817. data = data.ravel()
  1818. data = tzconversion.tz_localize_to_utc(
  1819. data.view("i8"), tz, ambiguous=ambiguous, creso=data_unit
  1820. )
  1821. data = data.view(new_dtype)
  1822. data = data.reshape(shape)
  1823. assert data.dtype == new_dtype, data.dtype
  1824. result = data
  1825. else:
  1826. # must be integer dtype otherwise
  1827. # assume this data are epoch timestamps
  1828. if data.dtype != INT64_DTYPE:
  1829. data = data.astype(np.int64, copy=False)
  1830. result = data.view(out_dtype)
  1831. if copy:
  1832. result = result.copy()
  1833. assert isinstance(result, np.ndarray), type(result)
  1834. assert result.dtype.kind == "M"
  1835. assert result.dtype != "M8"
  1836. assert is_supported_unit(get_unit_from_dtype(result.dtype))
  1837. return result, tz, inferred_freq
  1838. def objects_to_datetime64ns(
  1839. data: np.ndarray,
  1840. dayfirst,
  1841. yearfirst,
  1842. utc: bool = False,
  1843. errors: DateTimeErrorChoices = "raise",
  1844. allow_object: bool = False,
  1845. ):
  1846. """
  1847. Convert data to array of timestamps.
  1848. Parameters
  1849. ----------
  1850. data : np.ndarray[object]
  1851. dayfirst : bool
  1852. yearfirst : bool
  1853. utc : bool, default False
  1854. Whether to convert/localize timestamps to UTC.
  1855. errors : {'raise', 'ignore', 'coerce'}
  1856. allow_object : bool
  1857. Whether to return an object-dtype ndarray instead of raising if the
  1858. data contains more than one timezone.
  1859. Returns
  1860. -------
  1861. result : ndarray
  1862. np.int64 dtype if returned values represent UTC timestamps
  1863. np.datetime64[ns] if returned values represent wall times
  1864. object if mixed timezones
  1865. inferred_tz : tzinfo or None
  1866. Raises
  1867. ------
  1868. ValueError : if data cannot be converted to datetimes
  1869. """
  1870. assert errors in ["raise", "ignore", "coerce"]
  1871. # if str-dtype, convert
  1872. data = np.array(data, copy=False, dtype=np.object_)
  1873. result, tz_parsed = tslib.array_to_datetime(
  1874. data,
  1875. errors=errors,
  1876. utc=utc,
  1877. dayfirst=dayfirst,
  1878. yearfirst=yearfirst,
  1879. )
  1880. if tz_parsed is not None:
  1881. # We can take a shortcut since the datetime64 numpy array
  1882. # is in UTC
  1883. # Return i8 values to denote unix timestamps
  1884. return result.view("i8"), tz_parsed
  1885. elif is_datetime64_dtype(result):
  1886. # returning M8[ns] denotes wall-times; since tz is None
  1887. # the distinction is a thin one
  1888. return result, tz_parsed
  1889. elif is_object_dtype(result):
  1890. # GH#23675 when called via `pd.to_datetime`, returning an object-dtype
  1891. # array is allowed. When called via `pd.DatetimeIndex`, we can
  1892. # only accept datetime64 dtype, so raise TypeError if object-dtype
  1893. # is returned, as that indicates the values can be recognized as
  1894. # datetimes but they have conflicting timezones/awareness
  1895. if allow_object:
  1896. return result, tz_parsed
  1897. raise TypeError(result)
  1898. else: # pragma: no cover
  1899. # GH#23675 this TypeError should never be hit, whereas the TypeError
  1900. # in the object-dtype branch above is reachable.
  1901. raise TypeError(result)
  1902. def maybe_convert_dtype(data, copy: bool, tz: tzinfo | None = None):
  1903. """
  1904. Convert data based on dtype conventions, issuing
  1905. errors where appropriate.
  1906. Parameters
  1907. ----------
  1908. data : np.ndarray or pd.Index
  1909. copy : bool
  1910. tz : tzinfo or None, default None
  1911. Returns
  1912. -------
  1913. data : np.ndarray or pd.Index
  1914. copy : bool
  1915. Raises
  1916. ------
  1917. TypeError : PeriodDType data is passed
  1918. """
  1919. if not hasattr(data, "dtype"):
  1920. # e.g. collections.deque
  1921. return data, copy
  1922. if is_float_dtype(data.dtype):
  1923. # pre-2.0 we treated these as wall-times, inconsistent with ints
  1924. # GH#23675, GH#45573 deprecated to treat symmetrically with integer dtypes.
  1925. # Note: data.astype(np.int64) fails ARM tests, see
  1926. # https://github.com/pandas-dev/pandas/issues/49468.
  1927. data = data.astype(DT64NS_DTYPE).view("i8")
  1928. copy = False
  1929. elif is_timedelta64_dtype(data.dtype) or is_bool_dtype(data.dtype):
  1930. # GH#29794 enforcing deprecation introduced in GH#23539
  1931. raise TypeError(f"dtype {data.dtype} cannot be converted to datetime64[ns]")
  1932. elif is_period_dtype(data.dtype):
  1933. # Note: without explicitly raising here, PeriodIndex
  1934. # test_setops.test_join_does_not_recur fails
  1935. raise TypeError(
  1936. "Passing PeriodDtype data is invalid. Use `data.to_timestamp()` instead"
  1937. )
  1938. elif is_extension_array_dtype(data.dtype) and not is_datetime64tz_dtype(data.dtype):
  1939. # TODO: We have no tests for these
  1940. data = np.array(data, dtype=np.object_)
  1941. copy = False
  1942. return data, copy
  1943. # -------------------------------------------------------------------
  1944. # Validation and Inference
  1945. def _maybe_infer_tz(tz: tzinfo | None, inferred_tz: tzinfo | None) -> tzinfo | None:
  1946. """
  1947. If a timezone is inferred from data, check that it is compatible with
  1948. the user-provided timezone, if any.
  1949. Parameters
  1950. ----------
  1951. tz : tzinfo or None
  1952. inferred_tz : tzinfo or None
  1953. Returns
  1954. -------
  1955. tz : tzinfo or None
  1956. Raises
  1957. ------
  1958. TypeError : if both timezones are present but do not match
  1959. """
  1960. if tz is None:
  1961. tz = inferred_tz
  1962. elif inferred_tz is None:
  1963. pass
  1964. elif not timezones.tz_compare(tz, inferred_tz):
  1965. raise TypeError(
  1966. f"data is already tz-aware {inferred_tz}, unable to "
  1967. f"set specified tz: {tz}"
  1968. )
  1969. return tz
  1970. def _validate_dt64_dtype(dtype):
  1971. """
  1972. Check that a dtype, if passed, represents either a numpy datetime64[ns]
  1973. dtype or a pandas DatetimeTZDtype.
  1974. Parameters
  1975. ----------
  1976. dtype : object
  1977. Returns
  1978. -------
  1979. dtype : None, numpy.dtype, or DatetimeTZDtype
  1980. Raises
  1981. ------
  1982. ValueError : invalid dtype
  1983. Notes
  1984. -----
  1985. Unlike _validate_tz_from_dtype, this does _not_ allow non-existent
  1986. tz errors to go through
  1987. """
  1988. if dtype is not None:
  1989. dtype = pandas_dtype(dtype)
  1990. if is_dtype_equal(dtype, np.dtype("M8")):
  1991. # no precision, disallowed GH#24806
  1992. msg = (
  1993. "Passing in 'datetime64' dtype with no precision is not allowed. "
  1994. "Please pass in 'datetime64[ns]' instead."
  1995. )
  1996. raise ValueError(msg)
  1997. if (
  1998. isinstance(dtype, np.dtype)
  1999. and (dtype.kind != "M" or not is_supported_unit(get_unit_from_dtype(dtype)))
  2000. ) or not isinstance(dtype, (np.dtype, DatetimeTZDtype)):
  2001. raise ValueError(
  2002. f"Unexpected value for 'dtype': '{dtype}'. "
  2003. "Must be 'datetime64[s]', 'datetime64[ms]', 'datetime64[us]', "
  2004. "'datetime64[ns]' or DatetimeTZDtype'."
  2005. )
  2006. if getattr(dtype, "tz", None):
  2007. # https://github.com/pandas-dev/pandas/issues/18595
  2008. # Ensure that we have a standard timezone for pytz objects.
  2009. # Without this, things like adding an array of timedeltas and
  2010. # a tz-aware Timestamp (with a tz specific to its datetime) will
  2011. # be incorrect(ish?) for the array as a whole
  2012. dtype = cast(DatetimeTZDtype, dtype)
  2013. dtype = DatetimeTZDtype(tz=timezones.tz_standardize(dtype.tz))
  2014. return dtype
  2015. def _validate_tz_from_dtype(
  2016. dtype, tz: tzinfo | None, explicit_tz_none: bool = False
  2017. ) -> tzinfo | None:
  2018. """
  2019. If the given dtype is a DatetimeTZDtype, extract the implied
  2020. tzinfo object from it and check that it does not conflict with the given
  2021. tz.
  2022. Parameters
  2023. ----------
  2024. dtype : dtype, str
  2025. tz : None, tzinfo
  2026. explicit_tz_none : bool, default False
  2027. Whether tz=None was passed explicitly, as opposed to lib.no_default.
  2028. Returns
  2029. -------
  2030. tz : consensus tzinfo
  2031. Raises
  2032. ------
  2033. ValueError : on tzinfo mismatch
  2034. """
  2035. if dtype is not None:
  2036. if isinstance(dtype, str):
  2037. try:
  2038. dtype = DatetimeTZDtype.construct_from_string(dtype)
  2039. except TypeError:
  2040. # Things like `datetime64[ns]`, which is OK for the
  2041. # constructors, but also nonsense, which should be validated
  2042. # but not by us. We *do* allow non-existent tz errors to
  2043. # go through
  2044. pass
  2045. dtz = getattr(dtype, "tz", None)
  2046. if dtz is not None:
  2047. if tz is not None and not timezones.tz_compare(tz, dtz):
  2048. raise ValueError("cannot supply both a tz and a dtype with a tz")
  2049. if explicit_tz_none:
  2050. raise ValueError("Cannot pass both a timezone-aware dtype and tz=None")
  2051. tz = dtz
  2052. if tz is not None and is_datetime64_dtype(dtype):
  2053. # We also need to check for the case where the user passed a
  2054. # tz-naive dtype (i.e. datetime64[ns])
  2055. if tz is not None and not timezones.tz_compare(tz, dtz):
  2056. raise ValueError(
  2057. "cannot supply both a tz and a "
  2058. "timezone-naive dtype (i.e. datetime64[ns])"
  2059. )
  2060. return tz
  2061. def _infer_tz_from_endpoints(
  2062. start: Timestamp, end: Timestamp, tz: tzinfo | None
  2063. ) -> tzinfo | None:
  2064. """
  2065. If a timezone is not explicitly given via `tz`, see if one can
  2066. be inferred from the `start` and `end` endpoints. If more than one
  2067. of these inputs provides a timezone, require that they all agree.
  2068. Parameters
  2069. ----------
  2070. start : Timestamp
  2071. end : Timestamp
  2072. tz : tzinfo or None
  2073. Returns
  2074. -------
  2075. tz : tzinfo or None
  2076. Raises
  2077. ------
  2078. TypeError : if start and end timezones do not agree
  2079. """
  2080. try:
  2081. inferred_tz = timezones.infer_tzinfo(start, end)
  2082. except AssertionError as err:
  2083. # infer_tzinfo raises AssertionError if passed mismatched timezones
  2084. raise TypeError(
  2085. "Start and end cannot both be tz-aware with different timezones"
  2086. ) from err
  2087. inferred_tz = timezones.maybe_get_tz(inferred_tz)
  2088. tz = timezones.maybe_get_tz(tz)
  2089. if tz is not None and inferred_tz is not None:
  2090. if not timezones.tz_compare(inferred_tz, tz):
  2091. raise AssertionError("Inferred time zone not equal to passed time zone")
  2092. elif inferred_tz is not None:
  2093. tz = inferred_tz
  2094. return tz
  2095. def _maybe_normalize_endpoints(
  2096. start: Timestamp | None, end: Timestamp | None, normalize: bool
  2097. ):
  2098. if normalize:
  2099. if start is not None:
  2100. start = start.normalize()
  2101. if end is not None:
  2102. end = end.normalize()
  2103. return start, end
  2104. def _maybe_localize_point(ts, is_none, is_not_none, freq, tz, ambiguous, nonexistent):
  2105. """
  2106. Localize a start or end Timestamp to the timezone of the corresponding
  2107. start or end Timestamp
  2108. Parameters
  2109. ----------
  2110. ts : start or end Timestamp to potentially localize
  2111. is_none : argument that should be None
  2112. is_not_none : argument that should not be None
  2113. freq : Tick, DateOffset, or None
  2114. tz : str, timezone object or None
  2115. ambiguous: str, localization behavior for ambiguous times
  2116. nonexistent: str, localization behavior for nonexistent times
  2117. Returns
  2118. -------
  2119. ts : Timestamp
  2120. """
  2121. # Make sure start and end are timezone localized if:
  2122. # 1) freq = a Timedelta-like frequency (Tick)
  2123. # 2) freq = None i.e. generating a linspaced range
  2124. if is_none is None and is_not_none is not None:
  2125. # Note: We can't ambiguous='infer' a singular ambiguous time; however,
  2126. # we have historically defaulted ambiguous=False
  2127. ambiguous = ambiguous if ambiguous != "infer" else False
  2128. localize_args = {"ambiguous": ambiguous, "nonexistent": nonexistent, "tz": None}
  2129. if isinstance(freq, Tick) or freq is None:
  2130. localize_args["tz"] = tz
  2131. ts = ts.tz_localize(**localize_args)
  2132. return ts
  2133. def _generate_range(
  2134. start: Timestamp | None,
  2135. end: Timestamp | None,
  2136. periods: int | None,
  2137. offset: BaseOffset,
  2138. *,
  2139. unit: str,
  2140. ):
  2141. """
  2142. Generates a sequence of dates corresponding to the specified time
  2143. offset. Similar to dateutil.rrule except uses pandas DateOffset
  2144. objects to represent time increments.
  2145. Parameters
  2146. ----------
  2147. start : Timestamp or None
  2148. end : Timestamp or None
  2149. periods : int or None
  2150. offset : DateOffset
  2151. unit : str
  2152. Notes
  2153. -----
  2154. * This method is faster for generating weekdays than dateutil.rrule
  2155. * At least two of (start, end, periods) must be specified.
  2156. * If both start and end are specified, the returned dates will
  2157. satisfy start <= date <= end.
  2158. Returns
  2159. -------
  2160. dates : generator object
  2161. """
  2162. offset = to_offset(offset)
  2163. # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]";
  2164. # expected "Union[integer[Any], float, str, date, datetime64]"
  2165. start = Timestamp(start) # type: ignore[arg-type]
  2166. if start is not NaT:
  2167. start = start.as_unit(unit)
  2168. else:
  2169. start = None
  2170. # Argument 1 to "Timestamp" has incompatible type "Optional[Timestamp]";
  2171. # expected "Union[integer[Any], float, str, date, datetime64]"
  2172. end = Timestamp(end) # type: ignore[arg-type]
  2173. if end is not NaT:
  2174. end = end.as_unit(unit)
  2175. else:
  2176. end = None
  2177. if start and not offset.is_on_offset(start):
  2178. # Incompatible types in assignment (expression has type "datetime",
  2179. # variable has type "Optional[Timestamp]")
  2180. start = offset.rollforward(start) # type: ignore[assignment]
  2181. elif end and not offset.is_on_offset(end):
  2182. # Incompatible types in assignment (expression has type "datetime",
  2183. # variable has type "Optional[Timestamp]")
  2184. end = offset.rollback(end) # type: ignore[assignment]
  2185. # Unsupported operand types for < ("Timestamp" and "None")
  2186. if periods is None and end < start and offset.n >= 0: # type: ignore[operator]
  2187. end = None
  2188. periods = 0
  2189. if end is None:
  2190. # error: No overload variant of "__radd__" of "BaseOffset" matches
  2191. # argument type "None"
  2192. end = start + (periods - 1) * offset # type: ignore[operator]
  2193. if start is None:
  2194. # error: No overload variant of "__radd__" of "BaseOffset" matches
  2195. # argument type "None"
  2196. start = end - (periods - 1) * offset # type: ignore[operator]
  2197. start = cast(Timestamp, start)
  2198. end = cast(Timestamp, end)
  2199. cur = start
  2200. if offset.n >= 0:
  2201. while cur <= end:
  2202. yield cur
  2203. if cur == end:
  2204. # GH#24252 avoid overflows by not performing the addition
  2205. # in offset.apply unless we have to
  2206. break
  2207. # faster than cur + offset
  2208. next_date = offset._apply(cur).as_unit(unit)
  2209. if next_date <= cur:
  2210. raise ValueError(f"Offset {offset} did not increment date")
  2211. cur = next_date
  2212. else:
  2213. while cur >= end:
  2214. yield cur
  2215. if cur == end:
  2216. # GH#24252 avoid overflows by not performing the addition
  2217. # in offset.apply unless we have to
  2218. break
  2219. # faster than cur + offset
  2220. next_date = offset._apply(cur).as_unit(unit)
  2221. if next_date >= cur:
  2222. raise ValueError(f"Offset {offset} did not decrement date")
  2223. cur = next_date