test_timestamp.py 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. """ test the scalar Timestamp """
  2. import calendar
  3. from datetime import (
  4. datetime,
  5. timedelta,
  6. timezone,
  7. )
  8. import locale
  9. import time
  10. import unicodedata
  11. from dateutil.tz import (
  12. tzlocal,
  13. tzutc,
  14. )
  15. from hypothesis import (
  16. given,
  17. strategies as st,
  18. )
  19. import numpy as np
  20. import pytest
  21. import pytz
  22. from pytz import utc
  23. from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
  24. from pandas._libs.tslibs.timezones import (
  25. dateutil_gettz as gettz,
  26. get_timezone,
  27. maybe_get_tz,
  28. tz_compare,
  29. )
  30. from pandas.compat import IS64
  31. from pandas.errors import OutOfBoundsDatetime
  32. import pandas.util._test_decorators as td
  33. from pandas import (
  34. NaT,
  35. Timedelta,
  36. Timestamp,
  37. )
  38. import pandas._testing as tm
  39. from pandas.tseries import offsets
  40. from pandas.tseries.frequencies import to_offset
  41. class TestTimestampProperties:
  42. def test_properties_business(self):
  43. freq = to_offset("B")
  44. ts = Timestamp("2017-10-01")
  45. assert ts.dayofweek == 6
  46. assert ts.day_of_week == 6
  47. assert ts.is_month_start # not a weekday
  48. assert not freq.is_month_start(ts)
  49. assert freq.is_month_start(ts + Timedelta(days=1))
  50. assert not freq.is_quarter_start(ts)
  51. assert freq.is_quarter_start(ts + Timedelta(days=1))
  52. ts = Timestamp("2017-09-30")
  53. assert ts.dayofweek == 5
  54. assert ts.day_of_week == 5
  55. assert ts.is_month_end
  56. assert not freq.is_month_end(ts)
  57. assert freq.is_month_end(ts - Timedelta(days=1))
  58. assert ts.is_quarter_end
  59. assert not freq.is_quarter_end(ts)
  60. assert freq.is_quarter_end(ts - Timedelta(days=1))
  61. @pytest.mark.parametrize(
  62. "attr, expected",
  63. [
  64. ["year", 2014],
  65. ["month", 12],
  66. ["day", 31],
  67. ["hour", 23],
  68. ["minute", 59],
  69. ["second", 0],
  70. ["microsecond", 0],
  71. ["nanosecond", 0],
  72. ["dayofweek", 2],
  73. ["day_of_week", 2],
  74. ["quarter", 4],
  75. ["dayofyear", 365],
  76. ["day_of_year", 365],
  77. ["week", 1],
  78. ["daysinmonth", 31],
  79. ],
  80. )
  81. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  82. def test_fields(self, attr, expected, tz):
  83. # GH 10050
  84. # GH 13303
  85. ts = Timestamp("2014-12-31 23:59:00", tz=tz)
  86. result = getattr(ts, attr)
  87. # that we are int like
  88. assert isinstance(result, int)
  89. assert result == expected
  90. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  91. def test_millisecond_raises(self, tz):
  92. ts = Timestamp("2014-12-31 23:59:00", tz=tz)
  93. msg = "'Timestamp' object has no attribute 'millisecond'"
  94. with pytest.raises(AttributeError, match=msg):
  95. ts.millisecond
  96. @pytest.mark.parametrize(
  97. "start", ["is_month_start", "is_quarter_start", "is_year_start"]
  98. )
  99. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  100. def test_is_start(self, start, tz):
  101. ts = Timestamp("2014-01-01 00:00:00", tz=tz)
  102. assert getattr(ts, start)
  103. @pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"])
  104. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  105. def test_is_end(self, end, tz):
  106. ts = Timestamp("2014-12-31 23:59:59", tz=tz)
  107. assert getattr(ts, end)
  108. # GH 12806
  109. @pytest.mark.parametrize(
  110. "data",
  111. [Timestamp("2017-08-28 23:00:00"), Timestamp("2017-08-28 23:00:00", tz="EST")],
  112. )
  113. # error: Unsupported operand types for + ("List[None]" and "List[str]")
  114. @pytest.mark.parametrize(
  115. "time_locale", [None] + tm.get_locales() # type: ignore[operator]
  116. )
  117. def test_names(self, data, time_locale):
  118. # GH 17354
  119. # Test .day_name(), .month_name
  120. if time_locale is None:
  121. expected_day = "Monday"
  122. expected_month = "August"
  123. else:
  124. with tm.set_locale(time_locale, locale.LC_TIME):
  125. expected_day = calendar.day_name[0].capitalize()
  126. expected_month = calendar.month_name[8].capitalize()
  127. result_day = data.day_name(time_locale)
  128. result_month = data.month_name(time_locale)
  129. # Work around https://github.com/pandas-dev/pandas/issues/22342
  130. # different normalizations
  131. expected_day = unicodedata.normalize("NFD", expected_day)
  132. expected_month = unicodedata.normalize("NFD", expected_month)
  133. result_day = unicodedata.normalize("NFD", result_day)
  134. result_month = unicodedata.normalize("NFD", result_month)
  135. assert result_day == expected_day
  136. assert result_month == expected_month
  137. # Test NaT
  138. nan_ts = Timestamp(NaT)
  139. assert np.isnan(nan_ts.day_name(time_locale))
  140. assert np.isnan(nan_ts.month_name(time_locale))
  141. def test_is_leap_year(self, tz_naive_fixture):
  142. tz = tz_naive_fixture
  143. if not IS64 and tz == tzlocal():
  144. # https://github.com/dateutil/dateutil/issues/197
  145. pytest.skip(
  146. "tzlocal() on a 32 bit platform causes internal overflow errors"
  147. )
  148. # GH 13727
  149. dt = Timestamp("2000-01-01 00:00:00", tz=tz)
  150. assert dt.is_leap_year
  151. assert isinstance(dt.is_leap_year, bool)
  152. dt = Timestamp("1999-01-01 00:00:00", tz=tz)
  153. assert not dt.is_leap_year
  154. dt = Timestamp("2004-01-01 00:00:00", tz=tz)
  155. assert dt.is_leap_year
  156. dt = Timestamp("2100-01-01 00:00:00", tz=tz)
  157. assert not dt.is_leap_year
  158. def test_woy_boundary(self):
  159. # make sure weeks at year boundaries are correct
  160. d = datetime(2013, 12, 31)
  161. result = Timestamp(d).week
  162. expected = 1 # ISO standard
  163. assert result == expected
  164. d = datetime(2008, 12, 28)
  165. result = Timestamp(d).week
  166. expected = 52 # ISO standard
  167. assert result == expected
  168. d = datetime(2009, 12, 31)
  169. result = Timestamp(d).week
  170. expected = 53 # ISO standard
  171. assert result == expected
  172. d = datetime(2010, 1, 1)
  173. result = Timestamp(d).week
  174. expected = 53 # ISO standard
  175. assert result == expected
  176. d = datetime(2010, 1, 3)
  177. result = Timestamp(d).week
  178. expected = 53 # ISO standard
  179. assert result == expected
  180. result = np.array(
  181. [
  182. Timestamp(datetime(*args)).week
  183. for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)]
  184. ]
  185. )
  186. assert (result == [52, 52, 53, 53]).all()
  187. def test_resolution(self):
  188. # GH#21336, GH#21365
  189. dt = Timestamp("2100-01-01 00:00:00.000000000")
  190. assert dt.resolution == Timedelta(nanoseconds=1)
  191. # Check that the attribute is available on the class, mirroring
  192. # the stdlib datetime behavior
  193. assert Timestamp.resolution == Timedelta(nanoseconds=1)
  194. assert dt.as_unit("us").resolution == Timedelta(microseconds=1)
  195. assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
  196. assert dt.as_unit("s").resolution == Timedelta(seconds=1)
  197. @pytest.mark.parametrize(
  198. "date_string, expected",
  199. [
  200. ("0000-2-29", 1),
  201. ("0000-3-1", 2),
  202. ("1582-10-14", 3),
  203. ("-0040-1-1", 4),
  204. ("2023-06-18", 6),
  205. ],
  206. )
  207. def test_dow_historic(self, date_string, expected):
  208. # GH 53738
  209. ts = Timestamp(date_string)
  210. dow = ts.weekday()
  211. assert dow == expected
  212. @given(
  213. ts=st.datetimes(),
  214. sign=st.sampled_from(["-", ""]),
  215. )
  216. def test_dow_parametric(self, ts, sign):
  217. # GH 53738
  218. ts = (
  219. f"{sign}{str(ts.year).zfill(4)}"
  220. f"-{str(ts.month).zfill(2)}"
  221. f"-{str(ts.day).zfill(2)}"
  222. )
  223. result = Timestamp(ts).weekday()
  224. expected = (
  225. (np.datetime64(ts) - np.datetime64("1970-01-01")).astype("int64") - 4
  226. ) % 7
  227. assert result == expected
  228. class TestTimestamp:
  229. def test_default_to_stdlib_utc(self):
  230. assert Timestamp.utcnow().tz is timezone.utc
  231. assert Timestamp.now("UTC").tz is timezone.utc
  232. assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc
  233. def test_tz(self):
  234. tstr = "2014-02-01 09:00"
  235. ts = Timestamp(tstr)
  236. local = ts.tz_localize("Asia/Tokyo")
  237. assert local.hour == 9
  238. assert local == Timestamp(tstr, tz="Asia/Tokyo")
  239. conv = local.tz_convert("US/Eastern")
  240. assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern")
  241. assert conv.hour == 19
  242. # preserves nanosecond
  243. ts = Timestamp(tstr) + offsets.Nano(5)
  244. local = ts.tz_localize("Asia/Tokyo")
  245. assert local.hour == 9
  246. assert local.nanosecond == 5
  247. conv = local.tz_convert("US/Eastern")
  248. assert conv.nanosecond == 5
  249. assert conv.hour == 19
  250. def test_utc_z_designator(self):
  251. assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is timezone.utc
  252. def test_asm8(self):
  253. np.random.seed(7_960_929)
  254. ns = [Timestamp.min._value, Timestamp.max._value, 1000]
  255. for n in ns:
  256. assert (
  257. Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n
  258. )
  259. assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8")
  260. def test_class_ops_pytz(self):
  261. def compare(x, y):
  262. assert int((Timestamp(x)._value - Timestamp(y)._value) / 1e9) == 0
  263. compare(Timestamp.now(), datetime.now())
  264. compare(Timestamp.now("UTC"), datetime.now(pytz.timezone("UTC")))
  265. compare(Timestamp.utcnow(), datetime.utcnow())
  266. compare(Timestamp.today(), datetime.today())
  267. current_time = calendar.timegm(datetime.now().utctimetuple())
  268. ts_utc = Timestamp.utcfromtimestamp(current_time)
  269. assert ts_utc.timestamp() == current_time
  270. compare(
  271. Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
  272. )
  273. compare(
  274. # Support tz kwarg in Timestamp.fromtimestamp
  275. Timestamp.fromtimestamp(current_time, "UTC"),
  276. datetime.fromtimestamp(current_time, utc),
  277. )
  278. compare(
  279. # Support tz kwarg in Timestamp.fromtimestamp
  280. Timestamp.fromtimestamp(current_time, tz="UTC"),
  281. datetime.fromtimestamp(current_time, utc),
  282. )
  283. date_component = datetime.utcnow()
  284. time_component = (date_component + timedelta(minutes=10)).time()
  285. compare(
  286. Timestamp.combine(date_component, time_component),
  287. datetime.combine(date_component, time_component),
  288. )
  289. def test_class_ops_dateutil(self):
  290. def compare(x, y):
  291. assert (
  292. int(
  293. np.round(Timestamp(x)._value / 1e9)
  294. - np.round(Timestamp(y)._value / 1e9)
  295. )
  296. == 0
  297. )
  298. compare(Timestamp.now(), datetime.now())
  299. compare(Timestamp.now("UTC"), datetime.now(tzutc()))
  300. compare(Timestamp.utcnow(), datetime.utcnow())
  301. compare(Timestamp.today(), datetime.today())
  302. current_time = calendar.timegm(datetime.now().utctimetuple())
  303. ts_utc = Timestamp.utcfromtimestamp(current_time)
  304. assert ts_utc.timestamp() == current_time
  305. compare(
  306. Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
  307. )
  308. date_component = datetime.utcnow()
  309. time_component = (date_component + timedelta(minutes=10)).time()
  310. compare(
  311. Timestamp.combine(date_component, time_component),
  312. datetime.combine(date_component, time_component),
  313. )
  314. def test_basics_nanos(self):
  315. val = np.int64(946_684_800_000_000_000).view("M8[ns]")
  316. stamp = Timestamp(val.view("i8") + 500)
  317. assert stamp.year == 2000
  318. assert stamp.month == 1
  319. assert stamp.microsecond == 0
  320. assert stamp.nanosecond == 500
  321. # GH 14415
  322. val = np.iinfo(np.int64).min + 80_000_000_000_000
  323. stamp = Timestamp(val)
  324. assert stamp.year == 1677
  325. assert stamp.month == 9
  326. assert stamp.day == 21
  327. assert stamp.microsecond == 145224
  328. assert stamp.nanosecond == 192
  329. @pytest.mark.parametrize(
  330. "value, check_kwargs",
  331. [
  332. [946688461000000000, {}],
  333. [946688461000000000 / 1000, {"unit": "us"}],
  334. [946688461000000000 / 1_000_000, {"unit": "ms"}],
  335. [946688461000000000 / 1_000_000_000, {"unit": "s"}],
  336. [10957, {"unit": "D", "h": 0}],
  337. [
  338. (946688461000000000 + 500000) / 1000000000,
  339. {"unit": "s", "us": 499, "ns": 964},
  340. ],
  341. [
  342. (946688461000000000 + 500000000) / 1000000000,
  343. {"unit": "s", "us": 500000},
  344. ],
  345. [(946688461000000000 + 500000) / 1000000, {"unit": "ms", "us": 500}],
  346. [(946688461000000000 + 500000) / 1000, {"unit": "us", "us": 500}],
  347. [(946688461000000000 + 500000000) / 1000000, {"unit": "ms", "us": 500000}],
  348. [946688461000000000 / 1000.0 + 5, {"unit": "us", "us": 5}],
  349. [946688461000000000 / 1000.0 + 5000, {"unit": "us", "us": 5000}],
  350. [946688461000000000 / 1000000.0 + 0.5, {"unit": "ms", "us": 500}],
  351. [946688461000000000 / 1000000.0 + 0.005, {"unit": "ms", "us": 5, "ns": 5}],
  352. [946688461000000000 / 1000000000.0 + 0.5, {"unit": "s", "us": 500000}],
  353. [10957 + 0.5, {"unit": "D", "h": 12}],
  354. ],
  355. )
  356. def test_unit(self, value, check_kwargs):
  357. def check(value, unit=None, h=1, s=1, us=0, ns=0):
  358. stamp = Timestamp(value, unit=unit)
  359. assert stamp.year == 2000
  360. assert stamp.month == 1
  361. assert stamp.day == 1
  362. assert stamp.hour == h
  363. if unit != "D":
  364. assert stamp.minute == 1
  365. assert stamp.second == s
  366. assert stamp.microsecond == us
  367. else:
  368. assert stamp.minute == 0
  369. assert stamp.second == 0
  370. assert stamp.microsecond == 0
  371. assert stamp.nanosecond == ns
  372. check(value, **check_kwargs)
  373. def test_roundtrip(self):
  374. # test value to string and back conversions
  375. # further test accessors
  376. base = Timestamp("20140101 00:00:00").as_unit("ns")
  377. result = Timestamp(base._value + Timedelta("5ms")._value)
  378. assert result == Timestamp(f"{base}.005000")
  379. assert result.microsecond == 5000
  380. result = Timestamp(base._value + Timedelta("5us")._value)
  381. assert result == Timestamp(f"{base}.000005")
  382. assert result.microsecond == 5
  383. result = Timestamp(base._value + Timedelta("5ns")._value)
  384. assert result == Timestamp(f"{base}.000000005")
  385. assert result.nanosecond == 5
  386. assert result.microsecond == 0
  387. result = Timestamp(base._value + Timedelta("6ms 5us")._value)
  388. assert result == Timestamp(f"{base}.006005")
  389. assert result.microsecond == 5 + 6 * 1000
  390. result = Timestamp(base._value + Timedelta("200ms 5us")._value)
  391. assert result == Timestamp(f"{base}.200005")
  392. assert result.microsecond == 5 + 200 * 1000
  393. def test_hash_equivalent(self):
  394. d = {datetime(2011, 1, 1): 5}
  395. stamp = Timestamp(datetime(2011, 1, 1))
  396. assert d[stamp] == 5
  397. @pytest.mark.parametrize(
  398. "timezone, year, month, day, hour",
  399. [["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
  400. )
  401. def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour):
  402. # see gh-33931
  403. test_timezone = gettz(timezone)
  404. transition_1 = Timestamp(
  405. year=year,
  406. month=month,
  407. day=day,
  408. hour=hour,
  409. minute=0,
  410. fold=0,
  411. tzinfo=test_timezone,
  412. )
  413. transition_2 = Timestamp(
  414. year=year,
  415. month=month,
  416. day=day,
  417. hour=hour,
  418. minute=0,
  419. fold=1,
  420. tzinfo=test_timezone,
  421. )
  422. assert hash(transition_1) == hash(transition_2)
  423. class TestTimestampNsOperations:
  424. def test_nanosecond_string_parsing(self):
  425. ts = Timestamp("2013-05-01 07:15:45.123456789")
  426. # GH 7878
  427. expected_repr = "2013-05-01 07:15:45.123456789"
  428. expected_value = 1_367_392_545_123_456_789
  429. assert ts._value == expected_value
  430. assert expected_repr in repr(ts)
  431. ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo")
  432. assert ts._value == expected_value - 9 * 3600 * 1_000_000_000
  433. assert expected_repr in repr(ts)
  434. ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC")
  435. assert ts._value == expected_value
  436. assert expected_repr in repr(ts)
  437. ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern")
  438. assert ts._value == expected_value + 4 * 3600 * 1_000_000_000
  439. assert expected_repr in repr(ts)
  440. # GH 10041
  441. ts = Timestamp("20130501T071545.123456789")
  442. assert ts._value == expected_value
  443. assert expected_repr in repr(ts)
  444. def test_nanosecond_timestamp(self):
  445. # GH 7610
  446. expected = 1_293_840_000_000_000_005
  447. t = Timestamp("2011-01-01") + offsets.Nano(5)
  448. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  449. assert t._value == expected
  450. assert t.nanosecond == 5
  451. t = Timestamp(t)
  452. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  453. assert t._value == expected
  454. assert t.nanosecond == 5
  455. t = Timestamp("2011-01-01 00:00:00.000000005")
  456. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  457. assert t._value == expected
  458. assert t.nanosecond == 5
  459. expected = 1_293_840_000_000_000_010
  460. t = t + offsets.Nano(5)
  461. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  462. assert t._value == expected
  463. assert t.nanosecond == 10
  464. t = Timestamp(t)
  465. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  466. assert t._value == expected
  467. assert t.nanosecond == 10
  468. t = Timestamp("2011-01-01 00:00:00.000000010")
  469. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  470. assert t._value == expected
  471. assert t.nanosecond == 10
  472. class TestTimestampToJulianDate:
  473. def test_compare_1700(self):
  474. r = Timestamp("1700-06-23").to_julian_date()
  475. assert r == 2_342_145.5
  476. def test_compare_2000(self):
  477. r = Timestamp("2000-04-12").to_julian_date()
  478. assert r == 2_451_646.5
  479. def test_compare_2100(self):
  480. r = Timestamp("2100-08-12").to_julian_date()
  481. assert r == 2_488_292.5
  482. def test_compare_hour01(self):
  483. r = Timestamp("2000-08-12T01:00:00").to_julian_date()
  484. assert r == 2_451_768.5416666666666666
  485. def test_compare_hour13(self):
  486. r = Timestamp("2000-08-12T13:00:00").to_julian_date()
  487. assert r == 2_451_769.0416666666666666
  488. class TestTimestampConversion:
  489. def test_conversion(self):
  490. # GH#9255
  491. ts = Timestamp("2000-01-01").as_unit("ns")
  492. result = ts.to_pydatetime()
  493. expected = datetime(2000, 1, 1)
  494. assert result == expected
  495. assert type(result) == type(expected)
  496. result = ts.to_datetime64()
  497. expected = np.datetime64(ts._value, "ns")
  498. assert result == expected
  499. assert type(result) == type(expected)
  500. assert result.dtype == expected.dtype
  501. def test_to_pydatetime_fold(self):
  502. # GH#45087
  503. tzstr = "dateutil/usr/share/zoneinfo/America/Chicago"
  504. ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, tz=tzstr)
  505. dt = ts.to_pydatetime()
  506. assert dt.fold == 1
  507. def test_to_pydatetime_nonzero_nano(self):
  508. ts = Timestamp("2011-01-01 9:00:00.123456789")
  509. # Warn the user of data loss (nanoseconds).
  510. with tm.assert_produces_warning(UserWarning):
  511. expected = datetime(2011, 1, 1, 9, 0, 0, 123456)
  512. result = ts.to_pydatetime()
  513. assert result == expected
  514. def test_timestamp_to_datetime(self):
  515. stamp = Timestamp("20090415", tz="US/Eastern")
  516. dtval = stamp.to_pydatetime()
  517. assert stamp == dtval
  518. assert stamp.tzinfo == dtval.tzinfo
  519. def test_timestamp_to_datetime_dateutil(self):
  520. stamp = Timestamp("20090415", tz="dateutil/US/Eastern")
  521. dtval = stamp.to_pydatetime()
  522. assert stamp == dtval
  523. assert stamp.tzinfo == dtval.tzinfo
  524. def test_timestamp_to_datetime_explicit_pytz(self):
  525. stamp = Timestamp("20090415", tz=pytz.timezone("US/Eastern"))
  526. dtval = stamp.to_pydatetime()
  527. assert stamp == dtval
  528. assert stamp.tzinfo == dtval.tzinfo
  529. @td.skip_if_windows
  530. def test_timestamp_to_datetime_explicit_dateutil(self):
  531. stamp = Timestamp("20090415", tz=gettz("US/Eastern"))
  532. dtval = stamp.to_pydatetime()
  533. assert stamp == dtval
  534. assert stamp.tzinfo == dtval.tzinfo
  535. def test_to_datetime_bijective(self):
  536. # Ensure that converting to datetime and back only loses precision
  537. # by going from nanoseconds to microseconds.
  538. exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning
  539. with tm.assert_produces_warning(exp_warning):
  540. pydt_max = Timestamp.max.to_pydatetime()
  541. assert (
  542. Timestamp(pydt_max).as_unit("ns")._value / 1000
  543. == Timestamp.max._value / 1000
  544. )
  545. exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning
  546. with tm.assert_produces_warning(exp_warning):
  547. pydt_min = Timestamp.min.to_pydatetime()
  548. # The next assertion can be enabled once GH#39221 is merged
  549. # assert pydt_min < Timestamp.min # this is bc nanos are dropped
  550. tdus = timedelta(microseconds=1)
  551. assert pydt_min + tdus > Timestamp.min
  552. assert (
  553. Timestamp(pydt_min + tdus).as_unit("ns")._value / 1000
  554. == Timestamp.min._value / 1000
  555. )
  556. def test_to_period_tz_warning(self):
  557. # GH#21333 make sure a warning is issued when timezone
  558. # info is lost
  559. ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern")
  560. with tm.assert_produces_warning(UserWarning):
  561. # warning that timezone info will be lost
  562. ts.to_period("D")
  563. def test_to_numpy_alias(self):
  564. # GH 24653: alias .to_numpy() for scalars
  565. ts = Timestamp(datetime.now())
  566. assert ts.to_datetime64() == ts.to_numpy()
  567. # GH#44460
  568. msg = "dtype and copy arguments are ignored"
  569. with pytest.raises(ValueError, match=msg):
  570. ts.to_numpy("M8[s]")
  571. with pytest.raises(ValueError, match=msg):
  572. ts.to_numpy(copy=True)
  573. class SubDatetime(datetime):
  574. pass
  575. @pytest.mark.parametrize(
  576. "lh,rh",
  577. [
  578. (SubDatetime(2000, 1, 1), Timedelta(hours=1)),
  579. (Timedelta(hours=1), SubDatetime(2000, 1, 1)),
  580. ],
  581. )
  582. def test_dt_subclass_add_timedelta(lh, rh):
  583. # GH#25851
  584. # ensure that subclassed datetime works for
  585. # Timedelta operations
  586. result = lh + rh
  587. expected = SubDatetime(2000, 1, 1, 1)
  588. assert result == expected
  589. class TestNonNano:
  590. @pytest.fixture(params=["s", "ms", "us"])
  591. def reso(self, request):
  592. return request.param
  593. @pytest.fixture
  594. def dt64(self, reso):
  595. # cases that are in-bounds for nanosecond, so we can compare against
  596. # the existing implementation.
  597. return np.datetime64("2016-01-01", reso)
  598. @pytest.fixture
  599. def ts(self, dt64):
  600. return Timestamp._from_dt64(dt64)
  601. @pytest.fixture
  602. def ts_tz(self, ts, tz_aware_fixture):
  603. tz = maybe_get_tz(tz_aware_fixture)
  604. return Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
  605. def test_non_nano_construction(self, dt64, ts, reso):
  606. assert ts._value == dt64.view("i8")
  607. if reso == "s":
  608. assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
  609. elif reso == "ms":
  610. assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value
  611. elif reso == "us":
  612. assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value
  613. def test_non_nano_fields(self, dt64, ts):
  614. alt = Timestamp(dt64)
  615. assert ts.year == alt.year
  616. assert ts.month == alt.month
  617. assert ts.day == alt.day
  618. assert ts.hour == ts.minute == ts.second == ts.microsecond == 0
  619. assert ts.nanosecond == 0
  620. assert ts.to_julian_date() == alt.to_julian_date()
  621. assert ts.weekday() == alt.weekday()
  622. assert ts.isoweekday() == alt.isoweekday()
  623. def test_start_end_fields(self, ts):
  624. assert ts.is_year_start
  625. assert ts.is_quarter_start
  626. assert ts.is_month_start
  627. assert not ts.is_year_end
  628. assert not ts.is_month_end
  629. assert not ts.is_month_end
  630. # 2016-01-01 is a Friday, so is year/quarter/month start with this freq
  631. assert ts.is_year_start
  632. assert ts.is_quarter_start
  633. assert ts.is_month_start
  634. assert not ts.is_year_end
  635. assert not ts.is_month_end
  636. assert not ts.is_month_end
  637. def test_day_name(self, dt64, ts):
  638. alt = Timestamp(dt64)
  639. assert ts.day_name() == alt.day_name()
  640. def test_month_name(self, dt64, ts):
  641. alt = Timestamp(dt64)
  642. assert ts.month_name() == alt.month_name()
  643. def test_tz_convert(self, ts):
  644. ts = Timestamp._from_value_and_reso(ts._value, ts._creso, utc)
  645. tz = pytz.timezone("US/Pacific")
  646. result = ts.tz_convert(tz)
  647. assert isinstance(result, Timestamp)
  648. assert result._creso == ts._creso
  649. assert tz_compare(result.tz, tz)
  650. def test_repr(self, dt64, ts):
  651. alt = Timestamp(dt64)
  652. assert str(ts) == str(alt)
  653. assert repr(ts) == repr(alt)
  654. def test_comparison(self, dt64, ts):
  655. alt = Timestamp(dt64)
  656. assert ts == dt64
  657. assert dt64 == ts
  658. assert ts == alt
  659. assert alt == ts
  660. assert not ts != dt64
  661. assert not dt64 != ts
  662. assert not ts != alt
  663. assert not alt != ts
  664. assert not ts < dt64
  665. assert not dt64 < ts
  666. assert not ts < alt
  667. assert not alt < ts
  668. assert not ts > dt64
  669. assert not dt64 > ts
  670. assert not ts > alt
  671. assert not alt > ts
  672. assert ts >= dt64
  673. assert dt64 >= ts
  674. assert ts >= alt
  675. assert alt >= ts
  676. assert ts <= dt64
  677. assert dt64 <= ts
  678. assert ts <= alt
  679. assert alt <= ts
  680. def test_cmp_cross_reso(self):
  681. # numpy gets this wrong because of silent overflow
  682. dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns]
  683. ts = Timestamp._from_dt64(dt64)
  684. # subtracting 3600*24 gives a datetime64 that _can_ fit inside the
  685. # nanosecond implementation bounds.
  686. other = Timestamp(dt64 - 3600 * 24).as_unit("ns")
  687. assert other < ts
  688. assert other.asm8 > ts.asm8 # <- numpy gets this wrong
  689. assert ts > other
  690. assert ts.asm8 < other.asm8 # <- numpy gets this wrong
  691. assert not other == ts
  692. assert ts != other
  693. @pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong")
  694. def test_cmp_cross_reso_reversed_dt64(self):
  695. dt64 = np.datetime64(106752, "D") # won't fit in M8[ns]
  696. ts = Timestamp._from_dt64(dt64)
  697. other = Timestamp(dt64 - 1)
  698. assert other.asm8 < ts
  699. def test_pickle(self, ts, tz_aware_fixture):
  700. tz = tz_aware_fixture
  701. tz = maybe_get_tz(tz)
  702. ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
  703. rt = tm.round_trip_pickle(ts)
  704. assert rt._creso == ts._creso
  705. assert rt == ts
  706. def test_normalize(self, dt64, ts):
  707. alt = Timestamp(dt64)
  708. result = ts.normalize()
  709. assert result._creso == ts._creso
  710. assert result == alt.normalize()
  711. def test_asm8(self, dt64, ts):
  712. rt = ts.asm8
  713. assert rt == dt64
  714. assert rt.dtype == dt64.dtype
  715. def test_to_numpy(self, dt64, ts):
  716. res = ts.to_numpy()
  717. assert res == dt64
  718. assert res.dtype == dt64.dtype
  719. def test_to_datetime64(self, dt64, ts):
  720. res = ts.to_datetime64()
  721. assert res == dt64
  722. assert res.dtype == dt64.dtype
  723. def test_timestamp(self, dt64, ts):
  724. alt = Timestamp(dt64)
  725. assert ts.timestamp() == alt.timestamp()
  726. def test_to_period(self, dt64, ts):
  727. alt = Timestamp(dt64)
  728. assert ts.to_period("D") == alt.to_period("D")
  729. @pytest.mark.parametrize(
  730. "td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")]
  731. )
  732. def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
  733. exp_reso = max(ts._creso, Timedelta(td)._creso)
  734. result = ts - td
  735. expected = Timestamp(dt64) - td
  736. assert isinstance(result, Timestamp)
  737. assert result._creso == exp_reso
  738. assert result == expected
  739. result = ts + td
  740. expected = Timestamp(dt64) + td
  741. assert isinstance(result, Timestamp)
  742. assert result._creso == exp_reso
  743. assert result == expected
  744. result = td + ts
  745. expected = td + Timestamp(dt64)
  746. assert isinstance(result, Timestamp)
  747. assert result._creso == exp_reso
  748. assert result == expected
  749. def test_addsub_offset(self, ts_tz):
  750. # specifically non-Tick offset
  751. off = offsets.YearEnd(1)
  752. result = ts_tz + off
  753. assert isinstance(result, Timestamp)
  754. assert result._creso == ts_tz._creso
  755. if ts_tz.month == 12 and ts_tz.day == 31:
  756. assert result.year == ts_tz.year + 1
  757. else:
  758. assert result.year == ts_tz.year
  759. assert result.day == 31
  760. assert result.month == 12
  761. assert tz_compare(result.tz, ts_tz.tz)
  762. result = ts_tz - off
  763. assert isinstance(result, Timestamp)
  764. assert result._creso == ts_tz._creso
  765. assert result.year == ts_tz.year - 1
  766. assert result.day == 31
  767. assert result.month == 12
  768. assert tz_compare(result.tz, ts_tz.tz)
  769. def test_sub_datetimelike_mismatched_reso(self, ts_tz):
  770. # case with non-lossy rounding
  771. ts = ts_tz
  772. # choose a unit for `other` that doesn't match ts_tz's;
  773. # this construction ensures we get cases with other._creso < ts._creso
  774. # and cases with other._creso > ts._creso
  775. unit = {
  776. NpyDatetimeUnit.NPY_FR_us.value: "ms",
  777. NpyDatetimeUnit.NPY_FR_ms.value: "s",
  778. NpyDatetimeUnit.NPY_FR_s.value: "us",
  779. }[ts._creso]
  780. other = ts.as_unit(unit)
  781. assert other._creso != ts._creso
  782. result = ts - other
  783. assert isinstance(result, Timedelta)
  784. assert result._value == 0
  785. assert result._creso == max(ts._creso, other._creso)
  786. result = other - ts
  787. assert isinstance(result, Timedelta)
  788. assert result._value == 0
  789. assert result._creso == max(ts._creso, other._creso)
  790. if ts._creso < other._creso:
  791. # Case where rounding is lossy
  792. other2 = other + Timedelta._from_value_and_reso(1, other._creso)
  793. exp = ts.as_unit(other.unit) - other2
  794. res = ts - other2
  795. assert res == exp
  796. assert res._creso == max(ts._creso, other._creso)
  797. res = other2 - ts
  798. assert res == -exp
  799. assert res._creso == max(ts._creso, other._creso)
  800. else:
  801. ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
  802. exp = ts2 - other.as_unit(ts2.unit)
  803. res = ts2 - other
  804. assert res == exp
  805. assert res._creso == max(ts._creso, other._creso)
  806. res = other - ts2
  807. assert res == -exp
  808. assert res._creso == max(ts._creso, other._creso)
  809. def test_sub_timedeltalike_mismatched_reso(self, ts_tz):
  810. # case with non-lossy rounding
  811. ts = ts_tz
  812. # choose a unit for `other` that doesn't match ts_tz's;
  813. # this construction ensures we get cases with other._creso < ts._creso
  814. # and cases with other._creso > ts._creso
  815. unit = {
  816. NpyDatetimeUnit.NPY_FR_us.value: "ms",
  817. NpyDatetimeUnit.NPY_FR_ms.value: "s",
  818. NpyDatetimeUnit.NPY_FR_s.value: "us",
  819. }[ts._creso]
  820. other = Timedelta(0).as_unit(unit)
  821. assert other._creso != ts._creso
  822. result = ts + other
  823. assert isinstance(result, Timestamp)
  824. assert result == ts
  825. assert result._creso == max(ts._creso, other._creso)
  826. result = other + ts
  827. assert isinstance(result, Timestamp)
  828. assert result == ts
  829. assert result._creso == max(ts._creso, other._creso)
  830. if ts._creso < other._creso:
  831. # Case where rounding is lossy
  832. other2 = other + Timedelta._from_value_and_reso(1, other._creso)
  833. exp = ts.as_unit(other.unit) + other2
  834. res = ts + other2
  835. assert res == exp
  836. assert res._creso == max(ts._creso, other._creso)
  837. res = other2 + ts
  838. assert res == exp
  839. assert res._creso == max(ts._creso, other._creso)
  840. else:
  841. ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
  842. exp = ts2 + other.as_unit(ts2.unit)
  843. res = ts2 + other
  844. assert res == exp
  845. assert res._creso == max(ts._creso, other._creso)
  846. res = other + ts2
  847. assert res == exp
  848. assert res._creso == max(ts._creso, other._creso)
  849. def test_addition_doesnt_downcast_reso(self):
  850. # https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413
  851. ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us")
  852. td = Timedelta(microseconds=1).as_unit("us")
  853. res = ts + td
  854. assert res._creso == ts._creso
  855. def test_sub_timedelta64_mismatched_reso(self, ts_tz):
  856. ts = ts_tz
  857. res = ts + np.timedelta64(1, "ns")
  858. exp = ts.as_unit("ns") + np.timedelta64(1, "ns")
  859. assert exp == res
  860. assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value
  861. def test_min(self, ts):
  862. assert ts.min <= ts
  863. assert ts.min._creso == ts._creso
  864. assert ts.min._value == NaT._value + 1
  865. def test_max(self, ts):
  866. assert ts.max >= ts
  867. assert ts.max._creso == ts._creso
  868. assert ts.max._value == np.iinfo(np.int64).max
  869. def test_resolution(self, ts):
  870. expected = Timedelta._from_value_and_reso(1, ts._creso)
  871. result = ts.resolution
  872. assert result == expected
  873. assert result._creso == expected._creso
  874. def test_out_of_ns_bounds(self):
  875. # https://github.com/pandas-dev/pandas/issues/51060
  876. result = Timestamp(-52700112000, unit="s")
  877. assert result == Timestamp("0300-01-01")
  878. assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s")
  879. def test_timestamp_class_min_max_resolution():
  880. # when accessed on the class (as opposed to an instance), we default
  881. # to nanoseconds
  882. assert Timestamp.min == Timestamp(NaT._value + 1)
  883. assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
  884. assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
  885. assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
  886. assert Timestamp.resolution == Timedelta(1)
  887. assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
  888. class TestAsUnit:
  889. def test_as_unit(self):
  890. ts = Timestamp("1970-01-01").as_unit("ns")
  891. assert ts.unit == "ns"
  892. assert ts.as_unit("ns") is ts
  893. res = ts.as_unit("us")
  894. assert res._value == ts._value // 1000
  895. assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
  896. rt = res.as_unit("ns")
  897. assert rt._value == ts._value
  898. assert rt._creso == ts._creso
  899. res = ts.as_unit("ms")
  900. assert res._value == ts._value // 1_000_000
  901. assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
  902. rt = res.as_unit("ns")
  903. assert rt._value == ts._value
  904. assert rt._creso == ts._creso
  905. res = ts.as_unit("s")
  906. assert res._value == ts._value // 1_000_000_000
  907. assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
  908. rt = res.as_unit("ns")
  909. assert rt._value == ts._value
  910. assert rt._creso == ts._creso
  911. def test_as_unit_overflows(self):
  912. # microsecond that would be just out of bounds for nano
  913. us = 9223372800000000
  914. ts = Timestamp._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value, None)
  915. msg = "Cannot cast 2262-04-12 00:00:00 to unit='ns' without overflow"
  916. with pytest.raises(OutOfBoundsDatetime, match=msg):
  917. ts.as_unit("ns")
  918. res = ts.as_unit("ms")
  919. assert res._value == us // 1000
  920. assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
  921. def test_as_unit_rounding(self):
  922. ts = Timestamp(1_500_000) # i.e. 1500 microseconds
  923. res = ts.as_unit("ms")
  924. expected = Timestamp(1_000_000) # i.e. 1 millisecond
  925. assert res == expected
  926. assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
  927. assert res._value == 1
  928. with pytest.raises(ValueError, match="Cannot losslessly convert units"):
  929. ts.as_unit("ms", round_ok=False)
  930. def test_as_unit_non_nano(self):
  931. # case where we are going neither to nor from nano
  932. ts = Timestamp("1970-01-02").as_unit("ms")
  933. assert ts.year == 1970
  934. assert ts.month == 1
  935. assert ts.day == 2
  936. assert ts.hour == ts.minute == ts.second == ts.microsecond == ts.nanosecond == 0
  937. res = ts.as_unit("s")
  938. assert res._value == 24 * 3600
  939. assert res.year == 1970
  940. assert res.month == 1
  941. assert res.day == 2
  942. assert (
  943. res.hour
  944. == res.minute
  945. == res.second
  946. == res.microsecond
  947. == res.nanosecond
  948. == 0
  949. )
  950. def test_delimited_date():
  951. # https://github.com/pandas-dev/pandas/issues/50231
  952. with tm.assert_produces_warning(None):
  953. result = Timestamp("13-01-2000")
  954. expected = Timestamp(2000, 1, 13)
  955. assert result == expected
  956. def test_utctimetuple():
  957. # GH 32174
  958. ts = Timestamp("2000-01-01", tz="UTC")
  959. result = ts.utctimetuple()
  960. expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0))
  961. assert result == expected
  962. def test_negative_dates():
  963. # https://github.com/pandas-dev/pandas/issues/50787
  964. ts = Timestamp("-2000-01-01")
  965. msg = (
  966. "^strftime not yet supported on Timestamps which are outside the range of "
  967. "Python's standard library. For now, please call the components you need "
  968. r"\(such as `.year` and `.month`\) and construct your string from there.$"
  969. )
  970. with pytest.raises(NotImplementedError, match=msg):
  971. ts.strftime("%Y")