710 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			710 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from datetime import (
 | |
|     datetime,
 | |
|     timedelta,
 | |
| )
 | |
| import operator
 | |
| 
 | |
| import numpy as np
 | |
| import pytest
 | |
| import pytz
 | |
| 
 | |
| from pandas._libs.tslibs import iNaT
 | |
| from pandas.compat.numpy import np_version_gte1p24p3
 | |
| 
 | |
| from pandas import (
 | |
|     DatetimeIndex,
 | |
|     DatetimeTZDtype,
 | |
|     Index,
 | |
|     NaT,
 | |
|     Period,
 | |
|     Series,
 | |
|     Timedelta,
 | |
|     TimedeltaIndex,
 | |
|     Timestamp,
 | |
|     isna,
 | |
|     offsets,
 | |
| )
 | |
| import pandas._testing as tm
 | |
| from pandas.core import roperator
 | |
| from pandas.core.arrays import (
 | |
|     DatetimeArray,
 | |
|     PeriodArray,
 | |
|     TimedeltaArray,
 | |
| )
 | |
| 
 | |
| 
 | |
| class TestNaTFormatting:
 | |
|     def test_repr(self):
 | |
|         assert repr(NaT) == "NaT"
 | |
| 
 | |
|     def test_str(self):
 | |
|         assert str(NaT) == "NaT"
 | |
| 
 | |
|     def test_isoformat(self):
 | |
|         assert NaT.isoformat() == "NaT"
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "nat,idx",
 | |
|     [
 | |
|         (Timestamp("NaT"), DatetimeArray),
 | |
|         (Timedelta("NaT"), TimedeltaArray),
 | |
|         (Period("NaT", freq="M"), PeriodArray),
 | |
|     ],
 | |
| )
 | |
| def test_nat_fields(nat, idx):
 | |
|     for field in idx._field_ops:
 | |
|         # weekday is a property of DTI, but a method
 | |
|         # on NaT/Timestamp for compat with datetime
 | |
|         if field == "weekday":
 | |
|             continue
 | |
| 
 | |
|         result = getattr(NaT, field)
 | |
|         assert np.isnan(result)
 | |
| 
 | |
|         result = getattr(nat, field)
 | |
|         assert np.isnan(result)
 | |
| 
 | |
|     for field in idx._bool_ops:
 | |
|         result = getattr(NaT, field)
 | |
|         assert result is False
 | |
| 
 | |
|         result = getattr(nat, field)
 | |
|         assert result is False
 | |
| 
 | |
| 
 | |
| def test_nat_vector_field_access():
 | |
|     idx = DatetimeIndex(["1/1/2000", None, None, "1/4/2000"])
 | |
| 
 | |
|     for field in DatetimeArray._field_ops:
 | |
|         # weekday is a property of DTI, but a method
 | |
|         # on NaT/Timestamp for compat with datetime
 | |
|         if field == "weekday":
 | |
|             continue
 | |
| 
 | |
|         result = getattr(idx, field)
 | |
|         expected = Index([getattr(x, field) for x in idx])
 | |
|         tm.assert_index_equal(result, expected)
 | |
| 
 | |
|     ser = Series(idx)
 | |
| 
 | |
|     for field in DatetimeArray._field_ops:
 | |
|         # weekday is a property of DTI, but a method
 | |
|         # on NaT/Timestamp for compat with datetime
 | |
|         if field == "weekday":
 | |
|             continue
 | |
| 
 | |
|         result = getattr(ser.dt, field)
 | |
|         expected = [getattr(x, field) for x in idx]
 | |
|         tm.assert_series_equal(result, Series(expected))
 | |
| 
 | |
|     for field in DatetimeArray._bool_ops:
 | |
|         result = getattr(ser.dt, field)
 | |
|         expected = [getattr(x, field) for x in idx]
 | |
|         tm.assert_series_equal(result, Series(expected))
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("klass", [Timestamp, Timedelta, Period])
 | |
| @pytest.mark.parametrize(
 | |
|     "value", [None, np.nan, iNaT, float("nan"), NaT, "NaT", "nat", "", "NAT"]
 | |
| )
 | |
| def test_identity(klass, value):
 | |
|     assert klass(value) is NaT
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("klass", [Timestamp, Timedelta])
 | |
| @pytest.mark.parametrize("method", ["round", "floor", "ceil"])
 | |
| @pytest.mark.parametrize("freq", ["s", "5s", "min", "5min", "h", "5h"])
 | |
| def test_round_nat(klass, method, freq):
 | |
|     # see gh-14940
 | |
|     ts = klass("nat")
 | |
| 
 | |
|     round_method = getattr(ts, method)
 | |
|     assert round_method(freq) is ts
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "method",
 | |
|     [
 | |
|         "astimezone",
 | |
|         "combine",
 | |
|         "ctime",
 | |
|         "dst",
 | |
|         "fromordinal",
 | |
|         "fromtimestamp",
 | |
|         "fromisocalendar",
 | |
|         "isocalendar",
 | |
|         "strftime",
 | |
|         "strptime",
 | |
|         "time",
 | |
|         "timestamp",
 | |
|         "timetuple",
 | |
|         "timetz",
 | |
|         "toordinal",
 | |
|         "tzname",
 | |
|         "utcfromtimestamp",
 | |
|         "utcnow",
 | |
|         "utcoffset",
 | |
|         "utctimetuple",
 | |
|         "timestamp",
 | |
|     ],
 | |
| )
 | |
| def test_nat_methods_raise(method):
 | |
|     # see gh-9513, gh-17329
 | |
|     msg = f"NaTType does not support {method}"
 | |
| 
 | |
|     with pytest.raises(ValueError, match=msg):
 | |
|         getattr(NaT, method)()
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("method", ["weekday", "isoweekday"])
 | |
| def test_nat_methods_nan(method):
 | |
|     # see gh-9513, gh-17329
 | |
|     assert np.isnan(getattr(NaT, method)())
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "method", ["date", "now", "replace", "today", "tz_convert", "tz_localize"]
 | |
| )
 | |
| def test_nat_methods_nat(method):
 | |
|     # see gh-8254, gh-9513, gh-17329
 | |
|     assert getattr(NaT, method)() is NaT
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "get_nat", [lambda x: NaT, lambda x: Timedelta(x), lambda x: Timestamp(x)]
 | |
| )
 | |
| def test_nat_iso_format(get_nat):
 | |
|     # see gh-12300
 | |
|     assert get_nat("NaT").isoformat() == "NaT"
 | |
|     assert get_nat("NaT").isoformat(timespec="nanoseconds") == "NaT"
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "klass,expected",
 | |
|     [
 | |
|         (Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]),
 | |
|         (
 | |
|             Timedelta,
 | |
|             [
 | |
|                 "components",
 | |
|                 "resolution_string",
 | |
|                 "to_pytimedelta",
 | |
|                 "to_timedelta64",
 | |
|                 "unit",
 | |
|                 "view",
 | |
|             ],
 | |
|         ),
 | |
|     ],
 | |
| )
 | |
| def test_missing_public_nat_methods(klass, expected):
 | |
|     # see gh-17327
 | |
|     #
 | |
|     # NaT should have *most* of the Timestamp and Timedelta methods.
 | |
|     # Here, we check which public methods NaT does not have. We
 | |
|     # ignore any missing private methods.
 | |
|     nat_names = dir(NaT)
 | |
|     klass_names = dir(klass)
 | |
| 
 | |
|     missing = [x for x in klass_names if x not in nat_names and not x.startswith("_")]
 | |
|     missing.sort()
 | |
| 
 | |
|     assert missing == expected
 | |
| 
 | |
| 
 | |
| def _get_overlap_public_nat_methods(klass, as_tuple=False):
 | |
|     """
 | |
|     Get overlapping public methods between NaT and another class.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     klass : type
 | |
|         The class to compare with NaT
 | |
|     as_tuple : bool, default False
 | |
|         Whether to return a list of tuples of the form (klass, method).
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     overlap : list
 | |
|     """
 | |
|     nat_names = dir(NaT)
 | |
|     klass_names = dir(klass)
 | |
| 
 | |
|     overlap = [
 | |
|         x
 | |
|         for x in nat_names
 | |
|         if x in klass_names and not x.startswith("_") and callable(getattr(klass, x))
 | |
|     ]
 | |
| 
 | |
|     # Timestamp takes precedence over Timedelta in terms of overlap.
 | |
|     if klass is Timedelta:
 | |
|         ts_names = dir(Timestamp)
 | |
|         overlap = [x for x in overlap if x not in ts_names]
 | |
| 
 | |
|     if as_tuple:
 | |
|         overlap = [(klass, method) for method in overlap]
 | |
| 
 | |
|     overlap.sort()
 | |
|     return overlap
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "klass,expected",
 | |
|     [
 | |
|         (
 | |
|             Timestamp,
 | |
|             [
 | |
|                 "as_unit",
 | |
|                 "astimezone",
 | |
|                 "ceil",
 | |
|                 "combine",
 | |
|                 "ctime",
 | |
|                 "date",
 | |
|                 "day_name",
 | |
|                 "dst",
 | |
|                 "floor",
 | |
|                 "fromisocalendar",
 | |
|                 "fromisoformat",
 | |
|                 "fromordinal",
 | |
|                 "fromtimestamp",
 | |
|                 "isocalendar",
 | |
|                 "isoformat",
 | |
|                 "isoweekday",
 | |
|                 "month_name",
 | |
|                 "now",
 | |
|                 "replace",
 | |
|                 "round",
 | |
|                 "strftime",
 | |
|                 "strptime",
 | |
|                 "time",
 | |
|                 "timestamp",
 | |
|                 "timetuple",
 | |
|                 "timetz",
 | |
|                 "to_datetime64",
 | |
|                 "to_numpy",
 | |
|                 "to_pydatetime",
 | |
|                 "today",
 | |
|                 "toordinal",
 | |
|                 "tz_convert",
 | |
|                 "tz_localize",
 | |
|                 "tzname",
 | |
|                 "utcfromtimestamp",
 | |
|                 "utcnow",
 | |
|                 "utcoffset",
 | |
|                 "utctimetuple",
 | |
|                 "weekday",
 | |
|             ],
 | |
|         ),
 | |
|         (Timedelta, ["total_seconds"]),
 | |
|     ],
 | |
| )
 | |
| def test_overlap_public_nat_methods(klass, expected):
 | |
|     # see gh-17327
 | |
|     #
 | |
|     # NaT should have *most* of the Timestamp and Timedelta methods.
 | |
|     # In case when Timestamp, Timedelta, and NaT are overlap, the overlap
 | |
|     # is considered to be with Timestamp and NaT, not Timedelta.
 | |
|     assert _get_overlap_public_nat_methods(klass) == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "compare",
 | |
|     (
 | |
|         _get_overlap_public_nat_methods(Timestamp, True)
 | |
|         + _get_overlap_public_nat_methods(Timedelta, True)
 | |
|     ),
 | |
|     ids=lambda x: f"{x[0].__name__}.{x[1]}",
 | |
| )
 | |
| def test_nat_doc_strings(compare):
 | |
|     # see gh-17327
 | |
|     #
 | |
|     # The docstrings for overlapping methods should match.
 | |
|     klass, method = compare
 | |
|     klass_doc = getattr(klass, method).__doc__
 | |
| 
 | |
|     if klass == Timestamp and method == "isoformat":
 | |
|         pytest.skip(
 | |
|             "Ignore differences with Timestamp.isoformat() as they're intentional"
 | |
|         )
 | |
| 
 | |
|     if method == "to_numpy":
 | |
|         # GH#44460 can return either dt64 or td64 depending on dtype,
 | |
|         #  different docstring is intentional
 | |
|         pytest.skip(f"different docstring for {method} is intentional")
 | |
| 
 | |
|     nat_doc = getattr(NaT, method).__doc__
 | |
|     assert klass_doc == nat_doc
 | |
| 
 | |
| 
 | |
| _ops = {
 | |
|     "left_plus_right": lambda a, b: a + b,
 | |
|     "right_plus_left": lambda a, b: b + a,
 | |
|     "left_minus_right": lambda a, b: a - b,
 | |
|     "right_minus_left": lambda a, b: b - a,
 | |
|     "left_times_right": lambda a, b: a * b,
 | |
|     "right_times_left": lambda a, b: b * a,
 | |
|     "left_div_right": lambda a, b: a / b,
 | |
|     "right_div_left": lambda a, b: b / a,
 | |
| }
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("op_name", list(_ops.keys()))
 | |
| @pytest.mark.parametrize(
 | |
|     "value,val_type",
 | |
|     [
 | |
|         (2, "scalar"),
 | |
|         (1.5, "floating"),
 | |
|         (np.nan, "floating"),
 | |
|         ("foo", "str"),
 | |
|         (timedelta(3600), "timedelta"),
 | |
|         (Timedelta("5s"), "timedelta"),
 | |
|         (datetime(2014, 1, 1), "timestamp"),
 | |
|         (Timestamp("2014-01-01"), "timestamp"),
 | |
|         (Timestamp("2014-01-01", tz="UTC"), "timestamp"),
 | |
|         (Timestamp("2014-01-01", tz="US/Eastern"), "timestamp"),
 | |
|         (pytz.timezone("Asia/Tokyo").localize(datetime(2014, 1, 1)), "timestamp"),
 | |
|     ],
 | |
| )
 | |
| def test_nat_arithmetic_scalar(op_name, value, val_type):
 | |
|     # see gh-6873
 | |
|     invalid_ops = {
 | |
|         "scalar": {"right_div_left"},
 | |
|         "floating": {
 | |
|             "right_div_left",
 | |
|             "left_minus_right",
 | |
|             "right_minus_left",
 | |
|             "left_plus_right",
 | |
|             "right_plus_left",
 | |
|         },
 | |
|         "str": set(_ops.keys()),
 | |
|         "timedelta": {"left_times_right", "right_times_left"},
 | |
|         "timestamp": {
 | |
|             "left_times_right",
 | |
|             "right_times_left",
 | |
|             "left_div_right",
 | |
|             "right_div_left",
 | |
|         },
 | |
|     }
 | |
| 
 | |
|     op = _ops[op_name]
 | |
| 
 | |
|     if op_name in invalid_ops.get(val_type, set()):
 | |
|         if (
 | |
|             val_type == "timedelta"
 | |
|             and "times" in op_name
 | |
|             and isinstance(value, Timedelta)
 | |
|         ):
 | |
|             typs = "(Timedelta|NaTType)"
 | |
|             msg = rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'"
 | |
|         elif val_type == "str":
 | |
|             # un-specific check here because the message comes from str
 | |
|             #  and varies by method
 | |
|             msg = "|".join(
 | |
|                 [
 | |
|                     "can only concatenate str",
 | |
|                     "unsupported operand type",
 | |
|                     "can't multiply sequence",
 | |
|                     "Can't convert 'NaTType'",
 | |
|                     "must be str, not NaTType",
 | |
|                 ]
 | |
|             )
 | |
|         else:
 | |
|             msg = "unsupported operand type"
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             op(NaT, value)
 | |
|     else:
 | |
|         if val_type == "timedelta" and "div" in op_name:
 | |
|             expected = np.nan
 | |
|         else:
 | |
|             expected = NaT
 | |
| 
 | |
|         assert op(NaT, value) is expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "val,expected", [(np.nan, NaT), (NaT, np.nan), (np.timedelta64("NaT"), np.nan)]
 | |
| )
 | |
| def test_nat_rfloordiv_timedelta(val, expected):
 | |
|     # see gh-#18846
 | |
|     #
 | |
|     # See also test_timedelta.TestTimedeltaArithmetic.test_floordiv
 | |
|     td = Timedelta(hours=3, minutes=4)
 | |
|     assert td // val is expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "op_name",
 | |
|     ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
 | |
| )
 | |
| @pytest.mark.parametrize(
 | |
|     "value",
 | |
|     [
 | |
|         DatetimeIndex(["2011-01-01", "2011-01-02"], name="x"),
 | |
|         DatetimeIndex(["2011-01-01", "2011-01-02"], tz="US/Eastern", name="x"),
 | |
|         DatetimeArray._from_sequence(["2011-01-01", "2011-01-02"], dtype="M8[ns]"),
 | |
|         DatetimeArray._from_sequence(
 | |
|             ["2011-01-01", "2011-01-02"], dtype=DatetimeTZDtype(tz="US/Pacific")
 | |
|         ),
 | |
|         TimedeltaIndex(["1 day", "2 day"], name="x"),
 | |
|     ],
 | |
| )
 | |
| def test_nat_arithmetic_index(op_name, value):
 | |
|     # see gh-11718
 | |
|     exp_name = "x"
 | |
|     exp_data = [NaT] * 2
 | |
| 
 | |
|     if value.dtype.kind == "M" and "plus" in op_name:
 | |
|         expected = DatetimeIndex(exp_data, tz=value.tz, name=exp_name)
 | |
|     else:
 | |
|         expected = TimedeltaIndex(exp_data, name=exp_name)
 | |
|     expected = expected.as_unit(value.unit)
 | |
| 
 | |
|     if not isinstance(value, Index):
 | |
|         expected = expected.array
 | |
| 
 | |
|     op = _ops[op_name]
 | |
|     result = op(NaT, value)
 | |
|     tm.assert_equal(result, expected)
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "op_name",
 | |
|     ["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
 | |
| )
 | |
| @pytest.mark.parametrize("box", [TimedeltaIndex, Series, TimedeltaArray._from_sequence])
 | |
| def test_nat_arithmetic_td64_vector(op_name, box):
 | |
|     # see gh-19124
 | |
|     vec = box(["1 day", "2 day"], dtype="timedelta64[ns]")
 | |
|     box_nat = box([NaT, NaT], dtype="timedelta64[ns]")
 | |
|     tm.assert_equal(_ops[op_name](vec, NaT), box_nat)
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "dtype,op,out_dtype",
 | |
|     [
 | |
|         ("datetime64[ns]", operator.add, "datetime64[ns]"),
 | |
|         ("datetime64[ns]", roperator.radd, "datetime64[ns]"),
 | |
|         ("datetime64[ns]", operator.sub, "timedelta64[ns]"),
 | |
|         ("datetime64[ns]", roperator.rsub, "timedelta64[ns]"),
 | |
|         ("timedelta64[ns]", operator.add, "datetime64[ns]"),
 | |
|         ("timedelta64[ns]", roperator.radd, "datetime64[ns]"),
 | |
|         ("timedelta64[ns]", operator.sub, "datetime64[ns]"),
 | |
|         ("timedelta64[ns]", roperator.rsub, "timedelta64[ns]"),
 | |
|     ],
 | |
| )
 | |
| def test_nat_arithmetic_ndarray(dtype, op, out_dtype):
 | |
|     other = np.arange(10).astype(dtype)
 | |
|     result = op(NaT, other)
 | |
| 
 | |
|     expected = np.empty(other.shape, dtype=out_dtype)
 | |
|     expected.fill("NaT")
 | |
|     tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
| 
 | |
| def test_nat_pinned_docstrings():
 | |
|     # see gh-17327
 | |
|     assert NaT.ctime.__doc__ == Timestamp.ctime.__doc__
 | |
| 
 | |
| 
 | |
| def test_to_numpy_alias():
 | |
|     # GH 24653: alias .to_numpy() for scalars
 | |
|     expected = NaT.to_datetime64()
 | |
|     result = NaT.to_numpy()
 | |
| 
 | |
|     assert isna(expected) and isna(result)
 | |
| 
 | |
|     # GH#44460
 | |
|     result = NaT.to_numpy("M8[s]")
 | |
|     assert isinstance(result, np.datetime64)
 | |
|     assert result.dtype == "M8[s]"
 | |
| 
 | |
|     result = NaT.to_numpy("m8[ns]")
 | |
|     assert isinstance(result, np.timedelta64)
 | |
|     assert result.dtype == "m8[ns]"
 | |
| 
 | |
|     result = NaT.to_numpy("m8[s]")
 | |
|     assert isinstance(result, np.timedelta64)
 | |
|     assert result.dtype == "m8[s]"
 | |
| 
 | |
|     with pytest.raises(ValueError, match="NaT.to_numpy dtype must be a "):
 | |
|         NaT.to_numpy(np.int64)
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "other",
 | |
|     [
 | |
|         Timedelta(0),
 | |
|         Timedelta(0).to_pytimedelta(),
 | |
|         pytest.param(
 | |
|             Timedelta(0).to_timedelta64(),
 | |
|             marks=pytest.mark.xfail(
 | |
|                 not np_version_gte1p24p3,
 | |
|                 reason="td64 doesn't return NotImplemented, see numpy#17017",
 | |
|                 # When this xfail is fixed, test_nat_comparisons_numpy
 | |
|                 #  can be removed.
 | |
|             ),
 | |
|         ),
 | |
|         Timestamp(0),
 | |
|         Timestamp(0).to_pydatetime(),
 | |
|         pytest.param(
 | |
|             Timestamp(0).to_datetime64(),
 | |
|             marks=pytest.mark.xfail(
 | |
|                 not np_version_gte1p24p3,
 | |
|                 reason="dt64 doesn't return NotImplemented, see numpy#17017",
 | |
|             ),
 | |
|         ),
 | |
|         Timestamp(0).tz_localize("UTC"),
 | |
|         NaT,
 | |
|     ],
 | |
| )
 | |
| def test_nat_comparisons(compare_operators_no_eq_ne, other):
 | |
|     # GH 26039
 | |
|     opname = compare_operators_no_eq_ne
 | |
| 
 | |
|     assert getattr(NaT, opname)(other) is False
 | |
| 
 | |
|     op = getattr(operator, opname.strip("_"))
 | |
|     assert op(NaT, other) is False
 | |
|     assert op(other, NaT) is False
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("other", [np.timedelta64(0, "ns"), np.datetime64("now", "ns")])
 | |
| def test_nat_comparisons_numpy(other):
 | |
|     # Once numpy#17017 is fixed and the xfailed cases in test_nat_comparisons
 | |
|     #  pass, this test can be removed
 | |
|     assert not NaT == other
 | |
|     assert NaT != other
 | |
|     assert not NaT < other
 | |
|     assert not NaT > other
 | |
|     assert not NaT <= other
 | |
|     assert not NaT >= other
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("other_and_type", [("foo", "str"), (2, "int"), (2.0, "float")])
 | |
| @pytest.mark.parametrize(
 | |
|     "symbol_and_op",
 | |
|     [("<=", operator.le), ("<", operator.lt), (">=", operator.ge), (">", operator.gt)],
 | |
| )
 | |
| def test_nat_comparisons_invalid(other_and_type, symbol_and_op):
 | |
|     # GH#35585
 | |
|     other, other_type = other_and_type
 | |
|     symbol, op = symbol_and_op
 | |
| 
 | |
|     assert not NaT == other
 | |
|     assert not other == NaT
 | |
| 
 | |
|     assert NaT != other
 | |
|     assert other != NaT
 | |
| 
 | |
|     msg = f"'{symbol}' not supported between instances of 'NaTType' and '{other_type}'"
 | |
|     with pytest.raises(TypeError, match=msg):
 | |
|         op(NaT, other)
 | |
| 
 | |
|     msg = f"'{symbol}' not supported between instances of '{other_type}' and 'NaTType'"
 | |
|     with pytest.raises(TypeError, match=msg):
 | |
|         op(other, NaT)
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "other",
 | |
|     [
 | |
|         np.array(["foo"] * 2, dtype=object),
 | |
|         np.array([2, 3], dtype="int64"),
 | |
|         np.array([2.0, 3.5], dtype="float64"),
 | |
|     ],
 | |
|     ids=["str", "int", "float"],
 | |
| )
 | |
| def test_nat_comparisons_invalid_ndarray(other):
 | |
|     # GH#40722
 | |
|     expected = np.array([False, False])
 | |
|     result = NaT == other
 | |
|     tm.assert_numpy_array_equal(result, expected)
 | |
|     result = other == NaT
 | |
|     tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
|     expected = np.array([True, True])
 | |
|     result = NaT != other
 | |
|     tm.assert_numpy_array_equal(result, expected)
 | |
|     result = other != NaT
 | |
|     tm.assert_numpy_array_equal(result, expected)
 | |
| 
 | |
|     for symbol, op in [
 | |
|         ("<=", operator.le),
 | |
|         ("<", operator.lt),
 | |
|         (">=", operator.ge),
 | |
|         (">", operator.gt),
 | |
|     ]:
 | |
|         msg = f"'{symbol}' not supported between"
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             op(NaT, other)
 | |
| 
 | |
|         if other.dtype == np.dtype("object"):
 | |
|             # uses the reverse operator, so symbol changes
 | |
|             msg = None
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             op(other, NaT)
 | |
| 
 | |
| 
 | |
| def test_compare_date(fixed_now_ts):
 | |
|     # GH#39151 comparing NaT with date object is deprecated
 | |
|     # See also: tests.scalar.timestamps.test_comparisons::test_compare_date
 | |
| 
 | |
|     dt = fixed_now_ts.to_pydatetime().date()
 | |
| 
 | |
|     msg = "Cannot compare NaT with datetime.date object"
 | |
|     for left, right in [(NaT, dt), (dt, NaT)]:
 | |
|         assert not left == right
 | |
|         assert left != right
 | |
| 
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             left < right
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             left <= right
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             left > right
 | |
|         with pytest.raises(TypeError, match=msg):
 | |
|             left >= right
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "obj",
 | |
|     [
 | |
|         offsets.YearEnd(2),
 | |
|         offsets.YearBegin(2),
 | |
|         offsets.MonthBegin(1),
 | |
|         offsets.MonthEnd(2),
 | |
|         offsets.MonthEnd(12),
 | |
|         offsets.Day(2),
 | |
|         offsets.Day(5),
 | |
|         offsets.Hour(24),
 | |
|         offsets.Hour(3),
 | |
|         offsets.Minute(),
 | |
|         np.timedelta64(3, "h"),
 | |
|         np.timedelta64(4, "h"),
 | |
|         np.timedelta64(3200, "s"),
 | |
|         np.timedelta64(3600, "s"),
 | |
|         np.timedelta64(3600 * 24, "s"),
 | |
|         np.timedelta64(2, "D"),
 | |
|         np.timedelta64(365, "D"),
 | |
|         timedelta(-2),
 | |
|         timedelta(365),
 | |
|         timedelta(minutes=120),
 | |
|         timedelta(days=4, minutes=180),
 | |
|         timedelta(hours=23),
 | |
|         timedelta(hours=23, minutes=30),
 | |
|         timedelta(hours=48),
 | |
|     ],
 | |
| )
 | |
| def test_nat_addsub_tdlike_scalar(obj):
 | |
|     assert NaT + obj is NaT
 | |
|     assert obj + NaT is NaT
 | |
|     assert NaT - obj is NaT
 | |
| 
 | |
| 
 | |
| def test_pickle():
 | |
|     # GH#4606
 | |
|     p = tm.round_trip_pickle(NaT)
 | |
|     assert p is NaT
 |