test_timestamp.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  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 import (
  32. NaT,
  33. Timedelta,
  34. Timestamp,
  35. )
  36. import pandas._testing as tm
  37. from pandas.tseries import offsets
  38. from pandas.tseries.frequencies import to_offset
  39. class TestTimestampProperties:
  40. def test_properties_business(self):
  41. freq = to_offset("B")
  42. ts = Timestamp("2017-10-01")
  43. assert ts.dayofweek == 6
  44. assert ts.day_of_week == 6
  45. assert ts.is_month_start # not a weekday
  46. assert not freq.is_month_start(ts)
  47. assert freq.is_month_start(ts + Timedelta(days=1))
  48. assert not freq.is_quarter_start(ts)
  49. assert freq.is_quarter_start(ts + Timedelta(days=1))
  50. ts = Timestamp("2017-09-30")
  51. assert ts.dayofweek == 5
  52. assert ts.day_of_week == 5
  53. assert ts.is_month_end
  54. assert not freq.is_month_end(ts)
  55. assert freq.is_month_end(ts - Timedelta(days=1))
  56. assert ts.is_quarter_end
  57. assert not freq.is_quarter_end(ts)
  58. assert freq.is_quarter_end(ts - Timedelta(days=1))
  59. @pytest.mark.parametrize(
  60. "attr, expected",
  61. [
  62. ["year", 2014],
  63. ["month", 12],
  64. ["day", 31],
  65. ["hour", 23],
  66. ["minute", 59],
  67. ["second", 0],
  68. ["microsecond", 0],
  69. ["nanosecond", 0],
  70. ["dayofweek", 2],
  71. ["day_of_week", 2],
  72. ["quarter", 4],
  73. ["dayofyear", 365],
  74. ["day_of_year", 365],
  75. ["week", 1],
  76. ["daysinmonth", 31],
  77. ],
  78. )
  79. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  80. def test_fields(self, attr, expected, tz):
  81. # GH 10050
  82. # GH 13303
  83. ts = Timestamp("2014-12-31 23:59:00", tz=tz)
  84. result = getattr(ts, attr)
  85. # that we are int like
  86. assert isinstance(result, int)
  87. assert result == expected
  88. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  89. def test_millisecond_raises(self, tz):
  90. ts = Timestamp("2014-12-31 23:59:00", tz=tz)
  91. msg = "'Timestamp' object has no attribute 'millisecond'"
  92. with pytest.raises(AttributeError, match=msg):
  93. ts.millisecond
  94. @pytest.mark.parametrize(
  95. "start", ["is_month_start", "is_quarter_start", "is_year_start"]
  96. )
  97. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  98. def test_is_start(self, start, tz):
  99. ts = Timestamp("2014-01-01 00:00:00", tz=tz)
  100. assert getattr(ts, start)
  101. @pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"])
  102. @pytest.mark.parametrize("tz", [None, "US/Eastern"])
  103. def test_is_end(self, end, tz):
  104. ts = Timestamp("2014-12-31 23:59:59", tz=tz)
  105. assert getattr(ts, end)
  106. # GH 12806
  107. @pytest.mark.parametrize(
  108. "data",
  109. [Timestamp("2017-08-28 23:00:00"), Timestamp("2017-08-28 23:00:00", tz="EST")],
  110. )
  111. # error: Unsupported operand types for + ("List[None]" and "List[str]")
  112. @pytest.mark.parametrize(
  113. "time_locale", [None] + tm.get_locales() # type: ignore[operator]
  114. )
  115. def test_names(self, data, time_locale):
  116. # GH 17354
  117. # Test .day_name(), .month_name
  118. if time_locale is None:
  119. expected_day = "Monday"
  120. expected_month = "August"
  121. else:
  122. with tm.set_locale(time_locale, locale.LC_TIME):
  123. expected_day = calendar.day_name[0].capitalize()
  124. expected_month = calendar.month_name[8].capitalize()
  125. result_day = data.day_name(time_locale)
  126. result_month = data.month_name(time_locale)
  127. # Work around https://github.com/pandas-dev/pandas/issues/22342
  128. # different normalizations
  129. expected_day = unicodedata.normalize("NFD", expected_day)
  130. expected_month = unicodedata.normalize("NFD", expected_month)
  131. result_day = unicodedata.normalize("NFD", result_day)
  132. result_month = unicodedata.normalize("NFD", result_month)
  133. assert result_day == expected_day
  134. assert result_month == expected_month
  135. # Test NaT
  136. nan_ts = Timestamp(NaT)
  137. assert np.isnan(nan_ts.day_name(time_locale))
  138. assert np.isnan(nan_ts.month_name(time_locale))
  139. def test_is_leap_year(self, tz_naive_fixture):
  140. tz = tz_naive_fixture
  141. if not IS64 and tz == tzlocal():
  142. # https://github.com/dateutil/dateutil/issues/197
  143. pytest.skip(
  144. "tzlocal() on a 32 bit platform causes internal overflow errors"
  145. )
  146. # GH 13727
  147. dt = Timestamp("2000-01-01 00:00:00", tz=tz)
  148. assert dt.is_leap_year
  149. assert isinstance(dt.is_leap_year, bool)
  150. dt = Timestamp("1999-01-01 00:00:00", tz=tz)
  151. assert not dt.is_leap_year
  152. dt = Timestamp("2004-01-01 00:00:00", tz=tz)
  153. assert dt.is_leap_year
  154. dt = Timestamp("2100-01-01 00:00:00", tz=tz)
  155. assert not dt.is_leap_year
  156. def test_woy_boundary(self):
  157. # make sure weeks at year boundaries are correct
  158. d = datetime(2013, 12, 31)
  159. result = Timestamp(d).week
  160. expected = 1 # ISO standard
  161. assert result == expected
  162. d = datetime(2008, 12, 28)
  163. result = Timestamp(d).week
  164. expected = 52 # ISO standard
  165. assert result == expected
  166. d = datetime(2009, 12, 31)
  167. result = Timestamp(d).week
  168. expected = 53 # ISO standard
  169. assert result == expected
  170. d = datetime(2010, 1, 1)
  171. result = Timestamp(d).week
  172. expected = 53 # ISO standard
  173. assert result == expected
  174. d = datetime(2010, 1, 3)
  175. result = Timestamp(d).week
  176. expected = 53 # ISO standard
  177. assert result == expected
  178. result = np.array(
  179. [
  180. Timestamp(datetime(*args)).week
  181. for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)]
  182. ]
  183. )
  184. assert (result == [52, 52, 53, 53]).all()
  185. def test_resolution(self):
  186. # GH#21336, GH#21365
  187. dt = Timestamp("2100-01-01 00:00:00.000000000")
  188. assert dt.resolution == Timedelta(nanoseconds=1)
  189. # Check that the attribute is available on the class, mirroring
  190. # the stdlib datetime behavior
  191. assert Timestamp.resolution == Timedelta(nanoseconds=1)
  192. assert dt.as_unit("us").resolution == Timedelta(microseconds=1)
  193. assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
  194. assert dt.as_unit("s").resolution == Timedelta(seconds=1)
  195. @pytest.mark.parametrize(
  196. "date_string, expected",
  197. [
  198. ("0000-2-29", 1),
  199. ("0000-3-1", 2),
  200. ("1582-10-14", 3),
  201. ("-0040-1-1", 4),
  202. ("2023-06-18", 6),
  203. ],
  204. )
  205. def test_dow_historic(self, date_string, expected):
  206. # GH 53738
  207. ts = Timestamp(date_string)
  208. dow = ts.weekday()
  209. assert dow == expected
  210. @given(
  211. ts=st.datetimes(),
  212. sign=st.sampled_from(["-", ""]),
  213. )
  214. def test_dow_parametric(self, ts, sign):
  215. # GH 53738
  216. ts = (
  217. f"{sign}{str(ts.year).zfill(4)}"
  218. f"-{str(ts.month).zfill(2)}"
  219. f"-{str(ts.day).zfill(2)}"
  220. )
  221. result = Timestamp(ts).weekday()
  222. expected = (
  223. (np.datetime64(ts) - np.datetime64("1970-01-01")).astype("int64") - 4
  224. ) % 7
  225. assert result == expected
  226. class TestTimestamp:
  227. @pytest.mark.parametrize("tz", [None, pytz.timezone("US/Pacific")])
  228. def test_disallow_setting_tz(self, tz):
  229. # GH#3746
  230. ts = Timestamp("2010")
  231. msg = "Cannot directly set timezone"
  232. with pytest.raises(AttributeError, match=msg):
  233. ts.tz = tz
  234. def test_default_to_stdlib_utc(self):
  235. assert Timestamp.utcnow().tz is timezone.utc
  236. assert Timestamp.now("UTC").tz is timezone.utc
  237. assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc
  238. def test_tz(self):
  239. tstr = "2014-02-01 09:00"
  240. ts = Timestamp(tstr)
  241. local = ts.tz_localize("Asia/Tokyo")
  242. assert local.hour == 9
  243. assert local == Timestamp(tstr, tz="Asia/Tokyo")
  244. conv = local.tz_convert("US/Eastern")
  245. assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern")
  246. assert conv.hour == 19
  247. # preserves nanosecond
  248. ts = Timestamp(tstr) + offsets.Nano(5)
  249. local = ts.tz_localize("Asia/Tokyo")
  250. assert local.hour == 9
  251. assert local.nanosecond == 5
  252. conv = local.tz_convert("US/Eastern")
  253. assert conv.nanosecond == 5
  254. assert conv.hour == 19
  255. def test_utc_z_designator(self):
  256. assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is timezone.utc
  257. def test_asm8(self):
  258. ns = [Timestamp.min._value, Timestamp.max._value, 1000]
  259. for n in ns:
  260. assert (
  261. Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n
  262. )
  263. assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8")
  264. def test_class_ops(self):
  265. def compare(x, y):
  266. assert int((Timestamp(x)._value - Timestamp(y)._value) / 1e9) == 0
  267. compare(Timestamp.now(), datetime.now())
  268. compare(Timestamp.now("UTC"), datetime.now(pytz.timezone("UTC")))
  269. compare(Timestamp.now("UTC"), datetime.now(tzutc()))
  270. compare(Timestamp.utcnow(), datetime.now(timezone.utc))
  271. compare(Timestamp.today(), datetime.today())
  272. current_time = calendar.timegm(datetime.now().utctimetuple())
  273. ts_utc = Timestamp.utcfromtimestamp(current_time)
  274. assert ts_utc.timestamp() == current_time
  275. compare(
  276. Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
  277. )
  278. compare(
  279. # Support tz kwarg in Timestamp.fromtimestamp
  280. Timestamp.fromtimestamp(current_time, "UTC"),
  281. datetime.fromtimestamp(current_time, utc),
  282. )
  283. compare(
  284. # Support tz kwarg in Timestamp.fromtimestamp
  285. Timestamp.fromtimestamp(current_time, tz="UTC"),
  286. datetime.fromtimestamp(current_time, utc),
  287. )
  288. date_component = datetime.now(timezone.utc)
  289. time_component = (date_component + timedelta(minutes=10)).time()
  290. compare(
  291. Timestamp.combine(date_component, time_component),
  292. datetime.combine(date_component, time_component),
  293. )
  294. def test_basics_nanos(self):
  295. val = np.int64(946_684_800_000_000_000).view("M8[ns]")
  296. stamp = Timestamp(val.view("i8") + 500)
  297. assert stamp.year == 2000
  298. assert stamp.month == 1
  299. assert stamp.microsecond == 0
  300. assert stamp.nanosecond == 500
  301. # GH 14415
  302. val = np.iinfo(np.int64).min + 80_000_000_000_000
  303. stamp = Timestamp(val)
  304. assert stamp.year == 1677
  305. assert stamp.month == 9
  306. assert stamp.day == 21
  307. assert stamp.microsecond == 145224
  308. assert stamp.nanosecond == 192
  309. def test_roundtrip(self):
  310. # test value to string and back conversions
  311. # further test accessors
  312. base = Timestamp("20140101 00:00:00").as_unit("ns")
  313. result = Timestamp(base._value + Timedelta("5ms")._value)
  314. assert result == Timestamp(f"{base}.005000")
  315. assert result.microsecond == 5000
  316. result = Timestamp(base._value + Timedelta("5us")._value)
  317. assert result == Timestamp(f"{base}.000005")
  318. assert result.microsecond == 5
  319. result = Timestamp(base._value + Timedelta("5ns")._value)
  320. assert result == Timestamp(f"{base}.000000005")
  321. assert result.nanosecond == 5
  322. assert result.microsecond == 0
  323. result = Timestamp(base._value + Timedelta("6ms 5us")._value)
  324. assert result == Timestamp(f"{base}.006005")
  325. assert result.microsecond == 5 + 6 * 1000
  326. result = Timestamp(base._value + Timedelta("200ms 5us")._value)
  327. assert result == Timestamp(f"{base}.200005")
  328. assert result.microsecond == 5 + 200 * 1000
  329. def test_hash_equivalent(self):
  330. d = {datetime(2011, 1, 1): 5}
  331. stamp = Timestamp(datetime(2011, 1, 1))
  332. assert d[stamp] == 5
  333. @pytest.mark.parametrize(
  334. "timezone, year, month, day, hour",
  335. [["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
  336. )
  337. def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour):
  338. # see gh-33931
  339. test_timezone = gettz(timezone)
  340. transition_1 = Timestamp(
  341. year=year,
  342. month=month,
  343. day=day,
  344. hour=hour,
  345. minute=0,
  346. fold=0,
  347. tzinfo=test_timezone,
  348. )
  349. transition_2 = Timestamp(
  350. year=year,
  351. month=month,
  352. day=day,
  353. hour=hour,
  354. minute=0,
  355. fold=1,
  356. tzinfo=test_timezone,
  357. )
  358. assert hash(transition_1) == hash(transition_2)
  359. class TestTimestampNsOperations:
  360. def test_nanosecond_string_parsing(self):
  361. ts = Timestamp("2013-05-01 07:15:45.123456789")
  362. # GH 7878
  363. expected_repr = "2013-05-01 07:15:45.123456789"
  364. expected_value = 1_367_392_545_123_456_789
  365. assert ts._value == expected_value
  366. assert expected_repr in repr(ts)
  367. ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo")
  368. assert ts._value == expected_value - 9 * 3600 * 1_000_000_000
  369. assert expected_repr in repr(ts)
  370. ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC")
  371. assert ts._value == expected_value
  372. assert expected_repr in repr(ts)
  373. ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern")
  374. assert ts._value == expected_value + 4 * 3600 * 1_000_000_000
  375. assert expected_repr in repr(ts)
  376. # GH 10041
  377. ts = Timestamp("20130501T071545.123456789")
  378. assert ts._value == expected_value
  379. assert expected_repr in repr(ts)
  380. def test_nanosecond_timestamp(self):
  381. # GH 7610
  382. expected = 1_293_840_000_000_000_005
  383. t = Timestamp("2011-01-01") + offsets.Nano(5)
  384. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  385. assert t._value == expected
  386. assert t.nanosecond == 5
  387. t = Timestamp(t)
  388. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  389. assert t._value == expected
  390. assert t.nanosecond == 5
  391. t = Timestamp("2011-01-01 00:00:00.000000005")
  392. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
  393. assert t._value == expected
  394. assert t.nanosecond == 5
  395. expected = 1_293_840_000_000_000_010
  396. t = t + offsets.Nano(5)
  397. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  398. assert t._value == expected
  399. assert t.nanosecond == 10
  400. t = Timestamp(t)
  401. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  402. assert t._value == expected
  403. assert t.nanosecond == 10
  404. t = Timestamp("2011-01-01 00:00:00.000000010")
  405. assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
  406. assert t._value == expected
  407. assert t.nanosecond == 10
  408. class TestTimestampConversion:
  409. def test_conversion(self):
  410. # GH#9255
  411. ts = Timestamp("2000-01-01").as_unit("ns")
  412. result = ts.to_pydatetime()
  413. expected = datetime(2000, 1, 1)
  414. assert result == expected
  415. assert type(result) == type(expected)
  416. result = ts.to_datetime64()
  417. expected = np.datetime64(ts._value, "ns")
  418. assert result == expected
  419. assert type(result) == type(expected)
  420. assert result.dtype == expected.dtype
  421. def test_to_period_tz_warning(self):
  422. # GH#21333 make sure a warning is issued when timezone
  423. # info is lost
  424. ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern")
  425. with tm.assert_produces_warning(UserWarning):
  426. # warning that timezone info will be lost
  427. ts.to_period("D")
  428. def test_to_numpy_alias(self):
  429. # GH 24653: alias .to_numpy() for scalars
  430. ts = Timestamp(datetime.now())
  431. assert ts.to_datetime64() == ts.to_numpy()
  432. # GH#44460
  433. msg = "dtype and copy arguments are ignored"
  434. with pytest.raises(ValueError, match=msg):
  435. ts.to_numpy("M8[s]")
  436. with pytest.raises(ValueError, match=msg):
  437. ts.to_numpy(copy=True)
  438. class TestNonNano:
  439. @pytest.fixture(params=["s", "ms", "us"])
  440. def reso(self, request):
  441. return request.param
  442. @pytest.fixture
  443. def dt64(self, reso):
  444. # cases that are in-bounds for nanosecond, so we can compare against
  445. # the existing implementation.
  446. return np.datetime64("2016-01-01", reso)
  447. @pytest.fixture
  448. def ts(self, dt64):
  449. return Timestamp._from_dt64(dt64)
  450. @pytest.fixture
  451. def ts_tz(self, ts, tz_aware_fixture):
  452. tz = maybe_get_tz(tz_aware_fixture)
  453. return Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
  454. def test_non_nano_construction(self, dt64, ts, reso):
  455. assert ts._value == dt64.view("i8")
  456. if reso == "s":
  457. assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
  458. elif reso == "ms":
  459. assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value
  460. elif reso == "us":
  461. assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value
  462. def test_non_nano_fields(self, dt64, ts):
  463. alt = Timestamp(dt64)
  464. assert ts.year == alt.year
  465. assert ts.month == alt.month
  466. assert ts.day == alt.day
  467. assert ts.hour == ts.minute == ts.second == ts.microsecond == 0
  468. assert ts.nanosecond == 0
  469. assert ts.to_julian_date() == alt.to_julian_date()
  470. assert ts.weekday() == alt.weekday()
  471. assert ts.isoweekday() == alt.isoweekday()
  472. def test_start_end_fields(self, ts):
  473. assert ts.is_year_start
  474. assert ts.is_quarter_start
  475. assert ts.is_month_start
  476. assert not ts.is_year_end
  477. assert not ts.is_month_end
  478. assert not ts.is_month_end
  479. # 2016-01-01 is a Friday, so is year/quarter/month start with this freq
  480. assert ts.is_year_start
  481. assert ts.is_quarter_start
  482. assert ts.is_month_start
  483. assert not ts.is_year_end
  484. assert not ts.is_month_end
  485. assert not ts.is_month_end
  486. def test_day_name(self, dt64, ts):
  487. alt = Timestamp(dt64)
  488. assert ts.day_name() == alt.day_name()
  489. def test_month_name(self, dt64, ts):
  490. alt = Timestamp(dt64)
  491. assert ts.month_name() == alt.month_name()
  492. def test_tz_convert(self, ts):
  493. ts = Timestamp._from_value_and_reso(ts._value, ts._creso, utc)
  494. tz = pytz.timezone("US/Pacific")
  495. result = ts.tz_convert(tz)
  496. assert isinstance(result, Timestamp)
  497. assert result._creso == ts._creso
  498. assert tz_compare(result.tz, tz)
  499. def test_repr(self, dt64, ts):
  500. alt = Timestamp(dt64)
  501. assert str(ts) == str(alt)
  502. assert repr(ts) == repr(alt)
  503. def test_comparison(self, dt64, ts):
  504. alt = Timestamp(dt64)
  505. assert ts == dt64
  506. assert dt64 == ts
  507. assert ts == alt
  508. assert alt == ts
  509. assert not ts != dt64
  510. assert not dt64 != ts
  511. assert not ts != alt
  512. assert not alt != ts
  513. assert not ts < dt64
  514. assert not dt64 < ts
  515. assert not ts < alt
  516. assert not alt < ts
  517. assert not ts > dt64
  518. assert not dt64 > ts
  519. assert not ts > alt
  520. assert not alt > ts
  521. assert ts >= dt64
  522. assert dt64 >= ts
  523. assert ts >= alt
  524. assert alt >= ts
  525. assert ts <= dt64
  526. assert dt64 <= ts
  527. assert ts <= alt
  528. assert alt <= ts
  529. def test_cmp_cross_reso(self):
  530. # numpy gets this wrong because of silent overflow
  531. dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns]
  532. ts = Timestamp._from_dt64(dt64)
  533. # subtracting 3600*24 gives a datetime64 that _can_ fit inside the
  534. # nanosecond implementation bounds.
  535. other = Timestamp(dt64 - 3600 * 24).as_unit("ns")
  536. assert other < ts
  537. assert other.asm8 > ts.asm8 # <- numpy gets this wrong
  538. assert ts > other
  539. assert ts.asm8 < other.asm8 # <- numpy gets this wrong
  540. assert not other == ts
  541. assert ts != other
  542. @pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong")
  543. def test_cmp_cross_reso_reversed_dt64(self):
  544. dt64 = np.datetime64(106752, "D") # won't fit in M8[ns]
  545. ts = Timestamp._from_dt64(dt64)
  546. other = Timestamp(dt64 - 1)
  547. assert other.asm8 < ts
  548. def test_pickle(self, ts, tz_aware_fixture):
  549. tz = tz_aware_fixture
  550. tz = maybe_get_tz(tz)
  551. ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
  552. rt = tm.round_trip_pickle(ts)
  553. assert rt._creso == ts._creso
  554. assert rt == ts
  555. def test_normalize(self, dt64, ts):
  556. alt = Timestamp(dt64)
  557. result = ts.normalize()
  558. assert result._creso == ts._creso
  559. assert result == alt.normalize()
  560. def test_asm8(self, dt64, ts):
  561. rt = ts.asm8
  562. assert rt == dt64
  563. assert rt.dtype == dt64.dtype
  564. def test_to_numpy(self, dt64, ts):
  565. res = ts.to_numpy()
  566. assert res == dt64
  567. assert res.dtype == dt64.dtype
  568. def test_to_datetime64(self, dt64, ts):
  569. res = ts.to_datetime64()
  570. assert res == dt64
  571. assert res.dtype == dt64.dtype
  572. def test_timestamp(self, dt64, ts):
  573. alt = Timestamp(dt64)
  574. assert ts.timestamp() == alt.timestamp()
  575. def test_to_period(self, dt64, ts):
  576. alt = Timestamp(dt64)
  577. assert ts.to_period("D") == alt.to_period("D")
  578. @pytest.mark.parametrize(
  579. "td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")]
  580. )
  581. def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
  582. exp_reso = max(ts._creso, Timedelta(td)._creso)
  583. result = ts - td
  584. expected = Timestamp(dt64) - td
  585. assert isinstance(result, Timestamp)
  586. assert result._creso == exp_reso
  587. assert result == expected
  588. result = ts + td
  589. expected = Timestamp(dt64) + td
  590. assert isinstance(result, Timestamp)
  591. assert result._creso == exp_reso
  592. assert result == expected
  593. result = td + ts
  594. expected = td + Timestamp(dt64)
  595. assert isinstance(result, Timestamp)
  596. assert result._creso == exp_reso
  597. assert result == expected
  598. def test_addsub_offset(self, ts_tz):
  599. # specifically non-Tick offset
  600. off = offsets.YearEnd(1)
  601. result = ts_tz + off
  602. assert isinstance(result, Timestamp)
  603. assert result._creso == ts_tz._creso
  604. if ts_tz.month == 12 and ts_tz.day == 31:
  605. assert result.year == ts_tz.year + 1
  606. else:
  607. assert result.year == ts_tz.year
  608. assert result.day == 31
  609. assert result.month == 12
  610. assert tz_compare(result.tz, ts_tz.tz)
  611. result = ts_tz - off
  612. assert isinstance(result, Timestamp)
  613. assert result._creso == ts_tz._creso
  614. assert result.year == ts_tz.year - 1
  615. assert result.day == 31
  616. assert result.month == 12
  617. assert tz_compare(result.tz, ts_tz.tz)
  618. def test_sub_datetimelike_mismatched_reso(self, ts_tz):
  619. # case with non-lossy rounding
  620. ts = ts_tz
  621. # choose a unit for `other` that doesn't match ts_tz's;
  622. # this construction ensures we get cases with other._creso < ts._creso
  623. # and cases with other._creso > ts._creso
  624. unit = {
  625. NpyDatetimeUnit.NPY_FR_us.value: "ms",
  626. NpyDatetimeUnit.NPY_FR_ms.value: "s",
  627. NpyDatetimeUnit.NPY_FR_s.value: "us",
  628. }[ts._creso]
  629. other = ts.as_unit(unit)
  630. assert other._creso != ts._creso
  631. result = ts - other
  632. assert isinstance(result, Timedelta)
  633. assert result._value == 0
  634. assert result._creso == max(ts._creso, other._creso)
  635. result = other - ts
  636. assert isinstance(result, Timedelta)
  637. assert result._value == 0
  638. assert result._creso == max(ts._creso, other._creso)
  639. if ts._creso < other._creso:
  640. # Case where rounding is lossy
  641. other2 = other + Timedelta._from_value_and_reso(1, other._creso)
  642. exp = ts.as_unit(other.unit) - other2
  643. res = ts - other2
  644. assert res == exp
  645. assert res._creso == max(ts._creso, other._creso)
  646. res = other2 - ts
  647. assert res == -exp
  648. assert res._creso == max(ts._creso, other._creso)
  649. else:
  650. ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
  651. exp = ts2 - other.as_unit(ts2.unit)
  652. res = ts2 - other
  653. assert res == exp
  654. assert res._creso == max(ts._creso, other._creso)
  655. res = other - ts2
  656. assert res == -exp
  657. assert res._creso == max(ts._creso, other._creso)
  658. def test_sub_timedeltalike_mismatched_reso(self, ts_tz):
  659. # case with non-lossy rounding
  660. ts = ts_tz
  661. # choose a unit for `other` that doesn't match ts_tz's;
  662. # this construction ensures we get cases with other._creso < ts._creso
  663. # and cases with other._creso > ts._creso
  664. unit = {
  665. NpyDatetimeUnit.NPY_FR_us.value: "ms",
  666. NpyDatetimeUnit.NPY_FR_ms.value: "s",
  667. NpyDatetimeUnit.NPY_FR_s.value: "us",
  668. }[ts._creso]
  669. other = Timedelta(0).as_unit(unit)
  670. assert other._creso != ts._creso
  671. result = ts + other
  672. assert isinstance(result, Timestamp)
  673. assert result == ts
  674. assert result._creso == max(ts._creso, other._creso)
  675. result = other + ts
  676. assert isinstance(result, Timestamp)
  677. assert result == ts
  678. assert result._creso == max(ts._creso, other._creso)
  679. if ts._creso < other._creso:
  680. # Case where rounding is lossy
  681. other2 = other + Timedelta._from_value_and_reso(1, other._creso)
  682. exp = ts.as_unit(other.unit) + other2
  683. res = ts + other2
  684. assert res == exp
  685. assert res._creso == max(ts._creso, other._creso)
  686. res = other2 + ts
  687. assert res == exp
  688. assert res._creso == max(ts._creso, other._creso)
  689. else:
  690. ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
  691. exp = ts2 + other.as_unit(ts2.unit)
  692. res = ts2 + other
  693. assert res == exp
  694. assert res._creso == max(ts._creso, other._creso)
  695. res = other + ts2
  696. assert res == exp
  697. assert res._creso == max(ts._creso, other._creso)
  698. def test_addition_doesnt_downcast_reso(self):
  699. # https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413
  700. ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us")
  701. td = Timedelta(microseconds=1).as_unit("us")
  702. res = ts + td
  703. assert res._creso == ts._creso
  704. def test_sub_timedelta64_mismatched_reso(self, ts_tz):
  705. ts = ts_tz
  706. res = ts + np.timedelta64(1, "ns")
  707. exp = ts.as_unit("ns") + np.timedelta64(1, "ns")
  708. assert exp == res
  709. assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value
  710. def test_min(self, ts):
  711. assert ts.min <= ts
  712. assert ts.min._creso == ts._creso
  713. assert ts.min._value == NaT._value + 1
  714. def test_max(self, ts):
  715. assert ts.max >= ts
  716. assert ts.max._creso == ts._creso
  717. assert ts.max._value == np.iinfo(np.int64).max
  718. def test_resolution(self, ts):
  719. expected = Timedelta._from_value_and_reso(1, ts._creso)
  720. result = ts.resolution
  721. assert result == expected
  722. assert result._creso == expected._creso
  723. def test_out_of_ns_bounds(self):
  724. # https://github.com/pandas-dev/pandas/issues/51060
  725. result = Timestamp(-52700112000, unit="s")
  726. assert result == Timestamp("0300-01-01")
  727. assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s")
  728. def test_timestamp_class_min_max_resolution():
  729. # when accessed on the class (as opposed to an instance), we default
  730. # to nanoseconds
  731. assert Timestamp.min == Timestamp(NaT._value + 1)
  732. assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
  733. assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
  734. assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
  735. assert Timestamp.resolution == Timedelta(1)
  736. assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
  737. def test_delimited_date():
  738. # https://github.com/pandas-dev/pandas/issues/50231
  739. with tm.assert_produces_warning(None):
  740. result = Timestamp("13-01-2000")
  741. expected = Timestamp(2000, 1, 13)
  742. assert result == expected
  743. def test_utctimetuple():
  744. # GH 32174
  745. ts = Timestamp("2000-01-01", tz="UTC")
  746. result = ts.utctimetuple()
  747. expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0))
  748. assert result == expected
  749. def test_negative_dates():
  750. # https://github.com/pandas-dev/pandas/issues/50787
  751. ts = Timestamp("-2000-01-01")
  752. msg = (
  753. " not yet supported on Timestamps which are outside the range of "
  754. "Python's standard library. For now, please call the components you need "
  755. r"\(such as `.year` and `.month`\) and construct your string from there.$"
  756. )
  757. func = "^strftime"
  758. with pytest.raises(NotImplementedError, match=func + msg):
  759. ts.strftime("%Y")
  760. msg = (
  761. " not yet supported on Timestamps which "
  762. "are outside the range of Python's standard library. "
  763. )
  764. func = "^date"
  765. with pytest.raises(NotImplementedError, match=func + msg):
  766. ts.date()
  767. func = "^isocalendar"
  768. with pytest.raises(NotImplementedError, match=func + msg):
  769. ts.isocalendar()
  770. func = "^timetuple"
  771. with pytest.raises(NotImplementedError, match=func + msg):
  772. ts.timetuple()
  773. func = "^toordinal"
  774. with pytest.raises(NotImplementedError, match=func + msg):
  775. ts.toordinal()