test_nat.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. from datetime import (
  2. datetime,
  3. timedelta,
  4. )
  5. import operator
  6. import numpy as np
  7. import pytest
  8. import pytz
  9. from pandas._libs.tslibs import iNaT
  10. from pandas.compat.numpy import np_version_gte1p24p3
  11. from pandas import (
  12. DatetimeIndex,
  13. DatetimeTZDtype,
  14. Index,
  15. NaT,
  16. Period,
  17. Series,
  18. Timedelta,
  19. TimedeltaIndex,
  20. Timestamp,
  21. isna,
  22. offsets,
  23. )
  24. import pandas._testing as tm
  25. from pandas.core import roperator
  26. from pandas.core.arrays import (
  27. DatetimeArray,
  28. PeriodArray,
  29. TimedeltaArray,
  30. )
  31. class TestNaTFormatting:
  32. def test_repr(self):
  33. assert repr(NaT) == "NaT"
  34. def test_str(self):
  35. assert str(NaT) == "NaT"
  36. def test_isoformat(self):
  37. assert NaT.isoformat() == "NaT"
  38. @pytest.mark.parametrize(
  39. "nat,idx",
  40. [
  41. (Timestamp("NaT"), DatetimeArray),
  42. (Timedelta("NaT"), TimedeltaArray),
  43. (Period("NaT", freq="M"), PeriodArray),
  44. ],
  45. )
  46. def test_nat_fields(nat, idx):
  47. for field in idx._field_ops:
  48. # weekday is a property of DTI, but a method
  49. # on NaT/Timestamp for compat with datetime
  50. if field == "weekday":
  51. continue
  52. result = getattr(NaT, field)
  53. assert np.isnan(result)
  54. result = getattr(nat, field)
  55. assert np.isnan(result)
  56. for field in idx._bool_ops:
  57. result = getattr(NaT, field)
  58. assert result is False
  59. result = getattr(nat, field)
  60. assert result is False
  61. def test_nat_vector_field_access():
  62. idx = DatetimeIndex(["1/1/2000", None, None, "1/4/2000"])
  63. for field in DatetimeArray._field_ops:
  64. # weekday is a property of DTI, but a method
  65. # on NaT/Timestamp for compat with datetime
  66. if field == "weekday":
  67. continue
  68. result = getattr(idx, field)
  69. expected = Index([getattr(x, field) for x in idx])
  70. tm.assert_index_equal(result, expected)
  71. ser = Series(idx)
  72. for field in DatetimeArray._field_ops:
  73. # weekday is a property of DTI, but a method
  74. # on NaT/Timestamp for compat with datetime
  75. if field == "weekday":
  76. continue
  77. result = getattr(ser.dt, field)
  78. expected = [getattr(x, field) for x in idx]
  79. tm.assert_series_equal(result, Series(expected))
  80. for field in DatetimeArray._bool_ops:
  81. result = getattr(ser.dt, field)
  82. expected = [getattr(x, field) for x in idx]
  83. tm.assert_series_equal(result, Series(expected))
  84. @pytest.mark.parametrize("klass", [Timestamp, Timedelta, Period])
  85. @pytest.mark.parametrize(
  86. "value", [None, np.nan, iNaT, float("nan"), NaT, "NaT", "nat", "", "NAT"]
  87. )
  88. def test_identity(klass, value):
  89. assert klass(value) is NaT
  90. @pytest.mark.parametrize("klass", [Timestamp, Timedelta])
  91. @pytest.mark.parametrize("method", ["round", "floor", "ceil"])
  92. @pytest.mark.parametrize("freq", ["s", "5s", "min", "5min", "h", "5h"])
  93. def test_round_nat(klass, method, freq):
  94. # see gh-14940
  95. ts = klass("nat")
  96. round_method = getattr(ts, method)
  97. assert round_method(freq) is ts
  98. @pytest.mark.parametrize(
  99. "method",
  100. [
  101. "astimezone",
  102. "combine",
  103. "ctime",
  104. "dst",
  105. "fromordinal",
  106. "fromtimestamp",
  107. "fromisocalendar",
  108. "isocalendar",
  109. "strftime",
  110. "strptime",
  111. "time",
  112. "timestamp",
  113. "timetuple",
  114. "timetz",
  115. "toordinal",
  116. "tzname",
  117. "utcfromtimestamp",
  118. "utcnow",
  119. "utcoffset",
  120. "utctimetuple",
  121. "timestamp",
  122. ],
  123. )
  124. def test_nat_methods_raise(method):
  125. # see gh-9513, gh-17329
  126. msg = f"NaTType does not support {method}"
  127. with pytest.raises(ValueError, match=msg):
  128. getattr(NaT, method)()
  129. @pytest.mark.parametrize("method", ["weekday", "isoweekday"])
  130. def test_nat_methods_nan(method):
  131. # see gh-9513, gh-17329
  132. assert np.isnan(getattr(NaT, method)())
  133. @pytest.mark.parametrize(
  134. "method", ["date", "now", "replace", "today", "tz_convert", "tz_localize"]
  135. )
  136. def test_nat_methods_nat(method):
  137. # see gh-8254, gh-9513, gh-17329
  138. assert getattr(NaT, method)() is NaT
  139. @pytest.mark.parametrize(
  140. "get_nat", [lambda x: NaT, lambda x: Timedelta(x), lambda x: Timestamp(x)]
  141. )
  142. def test_nat_iso_format(get_nat):
  143. # see gh-12300
  144. assert get_nat("NaT").isoformat() == "NaT"
  145. assert get_nat("NaT").isoformat(timespec="nanoseconds") == "NaT"
  146. @pytest.mark.parametrize(
  147. "klass,expected",
  148. [
  149. (Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]),
  150. (
  151. Timedelta,
  152. [
  153. "components",
  154. "resolution_string",
  155. "to_pytimedelta",
  156. "to_timedelta64",
  157. "unit",
  158. "view",
  159. ],
  160. ),
  161. ],
  162. )
  163. def test_missing_public_nat_methods(klass, expected):
  164. # see gh-17327
  165. #
  166. # NaT should have *most* of the Timestamp and Timedelta methods.
  167. # Here, we check which public methods NaT does not have. We
  168. # ignore any missing private methods.
  169. nat_names = dir(NaT)
  170. klass_names = dir(klass)
  171. missing = [x for x in klass_names if x not in nat_names and not x.startswith("_")]
  172. missing.sort()
  173. assert missing == expected
  174. def _get_overlap_public_nat_methods(klass, as_tuple=False):
  175. """
  176. Get overlapping public methods between NaT and another class.
  177. Parameters
  178. ----------
  179. klass : type
  180. The class to compare with NaT
  181. as_tuple : bool, default False
  182. Whether to return a list of tuples of the form (klass, method).
  183. Returns
  184. -------
  185. overlap : list
  186. """
  187. nat_names = dir(NaT)
  188. klass_names = dir(klass)
  189. overlap = [
  190. x
  191. for x in nat_names
  192. if x in klass_names and not x.startswith("_") and callable(getattr(klass, x))
  193. ]
  194. # Timestamp takes precedence over Timedelta in terms of overlap.
  195. if klass is Timedelta:
  196. ts_names = dir(Timestamp)
  197. overlap = [x for x in overlap if x not in ts_names]
  198. if as_tuple:
  199. overlap = [(klass, method) for method in overlap]
  200. overlap.sort()
  201. return overlap
  202. @pytest.mark.parametrize(
  203. "klass,expected",
  204. [
  205. (
  206. Timestamp,
  207. [
  208. "as_unit",
  209. "astimezone",
  210. "ceil",
  211. "combine",
  212. "ctime",
  213. "date",
  214. "day_name",
  215. "dst",
  216. "floor",
  217. "fromisocalendar",
  218. "fromisoformat",
  219. "fromordinal",
  220. "fromtimestamp",
  221. "isocalendar",
  222. "isoformat",
  223. "isoweekday",
  224. "month_name",
  225. "now",
  226. "replace",
  227. "round",
  228. "strftime",
  229. "strptime",
  230. "time",
  231. "timestamp",
  232. "timetuple",
  233. "timetz",
  234. "to_datetime64",
  235. "to_numpy",
  236. "to_pydatetime",
  237. "today",
  238. "toordinal",
  239. "tz_convert",
  240. "tz_localize",
  241. "tzname",
  242. "utcfromtimestamp",
  243. "utcnow",
  244. "utcoffset",
  245. "utctimetuple",
  246. "weekday",
  247. ],
  248. ),
  249. (Timedelta, ["total_seconds"]),
  250. ],
  251. )
  252. def test_overlap_public_nat_methods(klass, expected):
  253. # see gh-17327
  254. #
  255. # NaT should have *most* of the Timestamp and Timedelta methods.
  256. # In case when Timestamp, Timedelta, and NaT are overlap, the overlap
  257. # is considered to be with Timestamp and NaT, not Timedelta.
  258. assert _get_overlap_public_nat_methods(klass) == expected
  259. @pytest.mark.parametrize(
  260. "compare",
  261. (
  262. _get_overlap_public_nat_methods(Timestamp, True)
  263. + _get_overlap_public_nat_methods(Timedelta, True)
  264. ),
  265. ids=lambda x: f"{x[0].__name__}.{x[1]}",
  266. )
  267. def test_nat_doc_strings(compare):
  268. # see gh-17327
  269. #
  270. # The docstrings for overlapping methods should match.
  271. klass, method = compare
  272. klass_doc = getattr(klass, method).__doc__
  273. if klass == Timestamp and method == "isoformat":
  274. pytest.skip(
  275. "Ignore differences with Timestamp.isoformat() as they're intentional"
  276. )
  277. if method == "to_numpy":
  278. # GH#44460 can return either dt64 or td64 depending on dtype,
  279. # different docstring is intentional
  280. pytest.skip(f"different docstring for {method} is intentional")
  281. nat_doc = getattr(NaT, method).__doc__
  282. assert klass_doc == nat_doc
  283. _ops = {
  284. "left_plus_right": lambda a, b: a + b,
  285. "right_plus_left": lambda a, b: b + a,
  286. "left_minus_right": lambda a, b: a - b,
  287. "right_minus_left": lambda a, b: b - a,
  288. "left_times_right": lambda a, b: a * b,
  289. "right_times_left": lambda a, b: b * a,
  290. "left_div_right": lambda a, b: a / b,
  291. "right_div_left": lambda a, b: b / a,
  292. }
  293. @pytest.mark.parametrize("op_name", list(_ops.keys()))
  294. @pytest.mark.parametrize(
  295. "value,val_type",
  296. [
  297. (2, "scalar"),
  298. (1.5, "floating"),
  299. (np.nan, "floating"),
  300. ("foo", "str"),
  301. (timedelta(3600), "timedelta"),
  302. (Timedelta("5s"), "timedelta"),
  303. (datetime(2014, 1, 1), "timestamp"),
  304. (Timestamp("2014-01-01"), "timestamp"),
  305. (Timestamp("2014-01-01", tz="UTC"), "timestamp"),
  306. (Timestamp("2014-01-01", tz="US/Eastern"), "timestamp"),
  307. (pytz.timezone("Asia/Tokyo").localize(datetime(2014, 1, 1)), "timestamp"),
  308. ],
  309. )
  310. def test_nat_arithmetic_scalar(op_name, value, val_type):
  311. # see gh-6873
  312. invalid_ops = {
  313. "scalar": {"right_div_left"},
  314. "floating": {
  315. "right_div_left",
  316. "left_minus_right",
  317. "right_minus_left",
  318. "left_plus_right",
  319. "right_plus_left",
  320. },
  321. "str": set(_ops.keys()),
  322. "timedelta": {"left_times_right", "right_times_left"},
  323. "timestamp": {
  324. "left_times_right",
  325. "right_times_left",
  326. "left_div_right",
  327. "right_div_left",
  328. },
  329. }
  330. op = _ops[op_name]
  331. if op_name in invalid_ops.get(val_type, set()):
  332. if (
  333. val_type == "timedelta"
  334. and "times" in op_name
  335. and isinstance(value, Timedelta)
  336. ):
  337. typs = "(Timedelta|NaTType)"
  338. msg = rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'"
  339. elif val_type == "str":
  340. # un-specific check here because the message comes from str
  341. # and varies by method
  342. msg = "|".join(
  343. [
  344. "can only concatenate str",
  345. "unsupported operand type",
  346. "can't multiply sequence",
  347. "Can't convert 'NaTType'",
  348. "must be str, not NaTType",
  349. ]
  350. )
  351. else:
  352. msg = "unsupported operand type"
  353. with pytest.raises(TypeError, match=msg):
  354. op(NaT, value)
  355. else:
  356. if val_type == "timedelta" and "div" in op_name:
  357. expected = np.nan
  358. else:
  359. expected = NaT
  360. assert op(NaT, value) is expected
  361. @pytest.mark.parametrize(
  362. "val,expected", [(np.nan, NaT), (NaT, np.nan), (np.timedelta64("NaT"), np.nan)]
  363. )
  364. def test_nat_rfloordiv_timedelta(val, expected):
  365. # see gh-#18846
  366. #
  367. # See also test_timedelta.TestTimedeltaArithmetic.test_floordiv
  368. td = Timedelta(hours=3, minutes=4)
  369. assert td // val is expected
  370. @pytest.mark.parametrize(
  371. "op_name",
  372. ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
  373. )
  374. @pytest.mark.parametrize(
  375. "value",
  376. [
  377. DatetimeIndex(["2011-01-01", "2011-01-02"], name="x"),
  378. DatetimeIndex(["2011-01-01", "2011-01-02"], tz="US/Eastern", name="x"),
  379. DatetimeArray._from_sequence(["2011-01-01", "2011-01-02"], dtype="M8[ns]"),
  380. DatetimeArray._from_sequence(
  381. ["2011-01-01", "2011-01-02"], dtype=DatetimeTZDtype(tz="US/Pacific")
  382. ),
  383. TimedeltaIndex(["1 day", "2 day"], name="x"),
  384. ],
  385. )
  386. def test_nat_arithmetic_index(op_name, value):
  387. # see gh-11718
  388. exp_name = "x"
  389. exp_data = [NaT] * 2
  390. if value.dtype.kind == "M" and "plus" in op_name:
  391. expected = DatetimeIndex(exp_data, tz=value.tz, name=exp_name)
  392. else:
  393. expected = TimedeltaIndex(exp_data, name=exp_name)
  394. expected = expected.as_unit(value.unit)
  395. if not isinstance(value, Index):
  396. expected = expected.array
  397. op = _ops[op_name]
  398. result = op(NaT, value)
  399. tm.assert_equal(result, expected)
  400. @pytest.mark.parametrize(
  401. "op_name",
  402. ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
  403. )
  404. @pytest.mark.parametrize("box", [TimedeltaIndex, Series, TimedeltaArray._from_sequence])
  405. def test_nat_arithmetic_td64_vector(op_name, box):
  406. # see gh-19124
  407. vec = box(["1 day", "2 day"], dtype="timedelta64[ns]")
  408. box_nat = box([NaT, NaT], dtype="timedelta64[ns]")
  409. tm.assert_equal(_ops[op_name](vec, NaT), box_nat)
  410. @pytest.mark.parametrize(
  411. "dtype,op,out_dtype",
  412. [
  413. ("datetime64[ns]", operator.add, "datetime64[ns]"),
  414. ("datetime64[ns]", roperator.radd, "datetime64[ns]"),
  415. ("datetime64[ns]", operator.sub, "timedelta64[ns]"),
  416. ("datetime64[ns]", roperator.rsub, "timedelta64[ns]"),
  417. ("timedelta64[ns]", operator.add, "datetime64[ns]"),
  418. ("timedelta64[ns]", roperator.radd, "datetime64[ns]"),
  419. ("timedelta64[ns]", operator.sub, "datetime64[ns]"),
  420. ("timedelta64[ns]", roperator.rsub, "timedelta64[ns]"),
  421. ],
  422. )
  423. def test_nat_arithmetic_ndarray(dtype, op, out_dtype):
  424. other = np.arange(10).astype(dtype)
  425. result = op(NaT, other)
  426. expected = np.empty(other.shape, dtype=out_dtype)
  427. expected.fill("NaT")
  428. tm.assert_numpy_array_equal(result, expected)
  429. def test_nat_pinned_docstrings():
  430. # see gh-17327
  431. assert NaT.ctime.__doc__ == Timestamp.ctime.__doc__
  432. def test_to_numpy_alias():
  433. # GH 24653: alias .to_numpy() for scalars
  434. expected = NaT.to_datetime64()
  435. result = NaT.to_numpy()
  436. assert isna(expected) and isna(result)
  437. # GH#44460
  438. result = NaT.to_numpy("M8[s]")
  439. assert isinstance(result, np.datetime64)
  440. assert result.dtype == "M8[s]"
  441. result = NaT.to_numpy("m8[ns]")
  442. assert isinstance(result, np.timedelta64)
  443. assert result.dtype == "m8[ns]"
  444. result = NaT.to_numpy("m8[s]")
  445. assert isinstance(result, np.timedelta64)
  446. assert result.dtype == "m8[s]"
  447. with pytest.raises(ValueError, match="NaT.to_numpy dtype must be a "):
  448. NaT.to_numpy(np.int64)
  449. @pytest.mark.parametrize(
  450. "other",
  451. [
  452. Timedelta(0),
  453. Timedelta(0).to_pytimedelta(),
  454. pytest.param(
  455. Timedelta(0).to_timedelta64(),
  456. marks=pytest.mark.xfail(
  457. not np_version_gte1p24p3,
  458. reason="td64 doesn't return NotImplemented, see numpy#17017",
  459. # When this xfail is fixed, test_nat_comparisons_numpy
  460. # can be removed.
  461. ),
  462. ),
  463. Timestamp(0),
  464. Timestamp(0).to_pydatetime(),
  465. pytest.param(
  466. Timestamp(0).to_datetime64(),
  467. marks=pytest.mark.xfail(
  468. not np_version_gte1p24p3,
  469. reason="dt64 doesn't return NotImplemented, see numpy#17017",
  470. ),
  471. ),
  472. Timestamp(0).tz_localize("UTC"),
  473. NaT,
  474. ],
  475. )
  476. def test_nat_comparisons(compare_operators_no_eq_ne, other):
  477. # GH 26039
  478. opname = compare_operators_no_eq_ne
  479. assert getattr(NaT, opname)(other) is False
  480. op = getattr(operator, opname.strip("_"))
  481. assert op(NaT, other) is False
  482. assert op(other, NaT) is False
  483. @pytest.mark.parametrize("other", [np.timedelta64(0, "ns"), np.datetime64("now", "ns")])
  484. def test_nat_comparisons_numpy(other):
  485. # Once numpy#17017 is fixed and the xfailed cases in test_nat_comparisons
  486. # pass, this test can be removed
  487. assert not NaT == other
  488. assert NaT != other
  489. assert not NaT < other
  490. assert not NaT > other
  491. assert not NaT <= other
  492. assert not NaT >= other
  493. @pytest.mark.parametrize("other_and_type", [("foo", "str"), (2, "int"), (2.0, "float")])
  494. @pytest.mark.parametrize(
  495. "symbol_and_op",
  496. [("<=", operator.le), ("<", operator.lt), (">=", operator.ge), (">", operator.gt)],
  497. )
  498. def test_nat_comparisons_invalid(other_and_type, symbol_and_op):
  499. # GH#35585
  500. other, other_type = other_and_type
  501. symbol, op = symbol_and_op
  502. assert not NaT == other
  503. assert not other == NaT
  504. assert NaT != other
  505. assert other != NaT
  506. msg = f"'{symbol}' not supported between instances of 'NaTType' and '{other_type}'"
  507. with pytest.raises(TypeError, match=msg):
  508. op(NaT, other)
  509. msg = f"'{symbol}' not supported between instances of '{other_type}' and 'NaTType'"
  510. with pytest.raises(TypeError, match=msg):
  511. op(other, NaT)
  512. @pytest.mark.parametrize(
  513. "other",
  514. [
  515. np.array(["foo"] * 2, dtype=object),
  516. np.array([2, 3], dtype="int64"),
  517. np.array([2.0, 3.5], dtype="float64"),
  518. ],
  519. ids=["str", "int", "float"],
  520. )
  521. def test_nat_comparisons_invalid_ndarray(other):
  522. # GH#40722
  523. expected = np.array([False, False])
  524. result = NaT == other
  525. tm.assert_numpy_array_equal(result, expected)
  526. result = other == NaT
  527. tm.assert_numpy_array_equal(result, expected)
  528. expected = np.array([True, True])
  529. result = NaT != other
  530. tm.assert_numpy_array_equal(result, expected)
  531. result = other != NaT
  532. tm.assert_numpy_array_equal(result, expected)
  533. for symbol, op in [
  534. ("<=", operator.le),
  535. ("<", operator.lt),
  536. (">=", operator.ge),
  537. (">", operator.gt),
  538. ]:
  539. msg = f"'{symbol}' not supported between"
  540. with pytest.raises(TypeError, match=msg):
  541. op(NaT, other)
  542. if other.dtype == np.dtype("object"):
  543. # uses the reverse operator, so symbol changes
  544. msg = None
  545. with pytest.raises(TypeError, match=msg):
  546. op(other, NaT)
  547. def test_compare_date(fixed_now_ts):
  548. # GH#39151 comparing NaT with date object is deprecated
  549. # See also: tests.scalar.timestamps.test_comparisons::test_compare_date
  550. dt = fixed_now_ts.to_pydatetime().date()
  551. msg = "Cannot compare NaT with datetime.date object"
  552. for left, right in [(NaT, dt), (dt, NaT)]:
  553. assert not left == right
  554. assert left != right
  555. with pytest.raises(TypeError, match=msg):
  556. left < right
  557. with pytest.raises(TypeError, match=msg):
  558. left <= right
  559. with pytest.raises(TypeError, match=msg):
  560. left > right
  561. with pytest.raises(TypeError, match=msg):
  562. left >= right
  563. @pytest.mark.parametrize(
  564. "obj",
  565. [
  566. offsets.YearEnd(2),
  567. offsets.YearBegin(2),
  568. offsets.MonthBegin(1),
  569. offsets.MonthEnd(2),
  570. offsets.MonthEnd(12),
  571. offsets.Day(2),
  572. offsets.Day(5),
  573. offsets.Hour(24),
  574. offsets.Hour(3),
  575. offsets.Minute(),
  576. np.timedelta64(3, "h"),
  577. np.timedelta64(4, "h"),
  578. np.timedelta64(3200, "s"),
  579. np.timedelta64(3600, "s"),
  580. np.timedelta64(3600 * 24, "s"),
  581. np.timedelta64(2, "D"),
  582. np.timedelta64(365, "D"),
  583. timedelta(-2),
  584. timedelta(365),
  585. timedelta(minutes=120),
  586. timedelta(days=4, minutes=180),
  587. timedelta(hours=23),
  588. timedelta(hours=23, minutes=30),
  589. timedelta(hours=48),
  590. ],
  591. )
  592. def test_nat_addsub_tdlike_scalar(obj):
  593. assert NaT + obj is NaT
  594. assert obj + NaT is NaT
  595. assert NaT - obj is NaT
  596. def test_pickle():
  597. # GH#4606
  598. p = tm.round_trip_pickle(NaT)
  599. assert p is NaT