first commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,63 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("method", ["__add__", "__sub__"])
|
||||
@pytest.mark.parametrize(
|
||||
"interval",
|
||||
[
|
||||
Interval(Timestamp("2017-01-01 00:00:00"), Timestamp("2018-01-01 00:00:00")),
|
||||
Interval(Timedelta(days=7), Timedelta(days=14)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
|
||||
)
|
||||
def test_time_interval_add_subtract_timedelta(interval, delta, method):
|
||||
# https://github.com/pandas-dev/pandas/issues/32023
|
||||
result = getattr(interval, method)(delta)
|
||||
left = getattr(interval.left, method)(delta)
|
||||
right = getattr(interval.right, method)(delta)
|
||||
expected = Interval(left, right)
|
||||
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("interval", [Interval(1, 2), Interval(1.0, 2.0)])
|
||||
@pytest.mark.parametrize(
|
||||
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
|
||||
)
|
||||
def test_numeric_interval_add_timedelta_raises(interval, delta):
|
||||
# https://github.com/pandas-dev/pandas/issues/32023
|
||||
msg = "|".join(
|
||||
[
|
||||
"unsupported operand",
|
||||
"cannot use operands",
|
||||
"Only numeric, Timestamp and Timedelta endpoints are allowed",
|
||||
]
|
||||
)
|
||||
with pytest.raises((TypeError, ValueError), match=msg):
|
||||
interval + delta
|
||||
|
||||
with pytest.raises((TypeError, ValueError), match=msg):
|
||||
delta + interval
|
||||
|
||||
|
||||
@pytest.mark.parametrize("klass", [timedelta, np.timedelta64, Timedelta])
|
||||
def test_timdelta_add_timestamp_interval(klass):
|
||||
delta = klass(0)
|
||||
expected = Interval(Timestamp("2020-01-01"), Timestamp("2020-02-01"))
|
||||
|
||||
result = delta + expected
|
||||
assert result == expected
|
||||
|
||||
result = expected + delta
|
||||
assert result == expected
|
||||
@@ -0,0 +1,283 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Period,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
import pandas.core.common as com
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def interval():
|
||||
return Interval(0, 1)
|
||||
|
||||
|
||||
class TestInterval:
|
||||
def test_properties(self, interval):
|
||||
assert interval.closed == "right"
|
||||
assert interval.left == 0
|
||||
assert interval.right == 1
|
||||
assert interval.mid == 0.5
|
||||
|
||||
def test_repr(self, interval):
|
||||
assert repr(interval) == "Interval(0, 1, closed='right')"
|
||||
assert str(interval) == "(0, 1]"
|
||||
|
||||
interval_left = Interval(0, 1, closed="left")
|
||||
assert repr(interval_left) == "Interval(0, 1, closed='left')"
|
||||
assert str(interval_left) == "[0, 1)"
|
||||
|
||||
def test_contains(self, interval):
|
||||
assert 0.5 in interval
|
||||
assert 1 in interval
|
||||
assert 0 not in interval
|
||||
|
||||
msg = "__contains__ not defined for two intervals"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval in interval
|
||||
|
||||
interval_both = Interval(0, 1, closed="both")
|
||||
assert 0 in interval_both
|
||||
assert 1 in interval_both
|
||||
|
||||
interval_neither = Interval(0, 1, closed="neither")
|
||||
assert 0 not in interval_neither
|
||||
assert 0.5 in interval_neither
|
||||
assert 1 not in interval_neither
|
||||
|
||||
def test_equal(self):
|
||||
assert Interval(0, 1) == Interval(0, 1, closed="right")
|
||||
assert Interval(0, 1) != Interval(0, 1, closed="left")
|
||||
assert Interval(0, 1) != 0
|
||||
|
||||
def test_comparison(self):
|
||||
msg = (
|
||||
"'<' not supported between instances of "
|
||||
"'pandas._libs.interval.Interval' and 'int'"
|
||||
)
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Interval(0, 1) < 2
|
||||
|
||||
assert Interval(0, 1) < Interval(1, 2)
|
||||
assert Interval(0, 1) < Interval(0, 2)
|
||||
assert Interval(0, 1) < Interval(0.5, 1.5)
|
||||
assert Interval(0, 1) <= Interval(0, 1)
|
||||
assert Interval(0, 1) > Interval(-1, 2)
|
||||
assert Interval(0, 1) >= Interval(0, 1)
|
||||
|
||||
def test_hash(self, interval):
|
||||
# should not raise
|
||||
hash(interval)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left, right, expected",
|
||||
[
|
||||
(0, 5, 5),
|
||||
(-2, 5.5, 7.5),
|
||||
(10, 10, 0),
|
||||
(10, np.inf, np.inf),
|
||||
(-np.inf, -5, np.inf),
|
||||
(-np.inf, np.inf, np.inf),
|
||||
(Timedelta("0 days"), Timedelta("5 days"), Timedelta("5 days")),
|
||||
(Timedelta("10 days"), Timedelta("10 days"), Timedelta("0 days")),
|
||||
(Timedelta("1H10min"), Timedelta("5H5min"), Timedelta("3H55min")),
|
||||
(Timedelta("5S"), Timedelta("1H"), Timedelta("59min55S")),
|
||||
],
|
||||
)
|
||||
def test_length(self, left, right, expected):
|
||||
# GH 18789
|
||||
iv = Interval(left, right)
|
||||
result = iv.length
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left, right, expected",
|
||||
[
|
||||
("2017-01-01", "2017-01-06", "5 days"),
|
||||
("2017-01-01", "2017-01-01 12:00:00", "12 hours"),
|
||||
("2017-01-01 12:00", "2017-01-01 12:00:00", "0 days"),
|
||||
("2017-01-01 12:01", "2017-01-05 17:31:00", "4 days 5 hours 30 min"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("tz", (None, "UTC", "CET", "US/Eastern"))
|
||||
def test_length_timestamp(self, tz, left, right, expected):
|
||||
# GH 18789
|
||||
iv = Interval(Timestamp(left, tz=tz), Timestamp(right, tz=tz))
|
||||
result = iv.length
|
||||
expected = Timedelta(expected)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left, right",
|
||||
[
|
||||
(0, 1),
|
||||
(Timedelta("0 days"), Timedelta("1 day")),
|
||||
(Timestamp("2018-01-01"), Timestamp("2018-01-02")),
|
||||
(
|
||||
Timestamp("2018-01-01", tz="US/Eastern"),
|
||||
Timestamp("2018-01-02", tz="US/Eastern"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_is_empty(self, left, right, closed):
|
||||
# GH27219
|
||||
# non-empty always return False
|
||||
iv = Interval(left, right, closed)
|
||||
assert iv.is_empty is False
|
||||
|
||||
# same endpoint is empty except when closed='both' (contains one point)
|
||||
iv = Interval(left, left, closed)
|
||||
result = iv.is_empty
|
||||
expected = closed != "both"
|
||||
assert result is expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left, right",
|
||||
[
|
||||
("a", "z"),
|
||||
(("a", "b"), ("c", "d")),
|
||||
(list("AB"), list("ab")),
|
||||
(Interval(0, 1), Interval(1, 2)),
|
||||
(Period("2018Q1", freq="Q"), Period("2018Q1", freq="Q")),
|
||||
],
|
||||
)
|
||||
def test_construct_errors(self, left, right):
|
||||
# GH 23013
|
||||
msg = "Only numeric, Timestamp and Timedelta endpoints are allowed"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Interval(left, right)
|
||||
|
||||
def test_math_add(self, closed):
|
||||
interval = Interval(0, 1, closed=closed)
|
||||
expected = Interval(1, 2, closed=closed)
|
||||
|
||||
result = interval + 1
|
||||
assert result == expected
|
||||
|
||||
result = 1 + interval
|
||||
assert result == expected
|
||||
|
||||
result = interval
|
||||
result += 1
|
||||
assert result == expected
|
||||
|
||||
msg = r"unsupported operand type\(s\) for \+"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval + interval
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval + "foo"
|
||||
|
||||
def test_math_sub(self, closed):
|
||||
interval = Interval(0, 1, closed=closed)
|
||||
expected = Interval(-1, 0, closed=closed)
|
||||
|
||||
result = interval - 1
|
||||
assert result == expected
|
||||
|
||||
result = interval
|
||||
result -= 1
|
||||
assert result == expected
|
||||
|
||||
msg = r"unsupported operand type\(s\) for -"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval - interval
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval - "foo"
|
||||
|
||||
def test_math_mult(self, closed):
|
||||
interval = Interval(0, 1, closed=closed)
|
||||
expected = Interval(0, 2, closed=closed)
|
||||
|
||||
result = interval * 2
|
||||
assert result == expected
|
||||
|
||||
result = 2 * interval
|
||||
assert result == expected
|
||||
|
||||
result = interval
|
||||
result *= 2
|
||||
assert result == expected
|
||||
|
||||
msg = r"unsupported operand type\(s\) for \*"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval * interval
|
||||
|
||||
msg = r"can\'t multiply sequence by non-int"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval * "foo"
|
||||
|
||||
def test_math_div(self, closed):
|
||||
interval = Interval(0, 1, closed=closed)
|
||||
expected = Interval(0, 0.5, closed=closed)
|
||||
|
||||
result = interval / 2.0
|
||||
assert result == expected
|
||||
|
||||
result = interval
|
||||
result /= 2.0
|
||||
assert result == expected
|
||||
|
||||
msg = r"unsupported operand type\(s\) for /"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval / interval
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval / "foo"
|
||||
|
||||
def test_math_floordiv(self, closed):
|
||||
interval = Interval(1, 2, closed=closed)
|
||||
expected = Interval(0, 1, closed=closed)
|
||||
|
||||
result = interval // 2
|
||||
assert result == expected
|
||||
|
||||
result = interval
|
||||
result //= 2
|
||||
assert result == expected
|
||||
|
||||
msg = r"unsupported operand type\(s\) for //"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval // interval
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval // "foo"
|
||||
|
||||
def test_constructor_errors(self):
|
||||
msg = "invalid option for 'closed': foo"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Interval(0, 1, closed="foo")
|
||||
|
||||
msg = "left side of interval must be <= right side"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Interval(1, 0)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tz_left, tz_right", [(None, "UTC"), ("UTC", None), ("UTC", "US/Eastern")]
|
||||
)
|
||||
def test_constructor_errors_tz(self, tz_left, tz_right):
|
||||
# GH 18538
|
||||
left = Timestamp("2017-01-01", tz=tz_left)
|
||||
right = Timestamp("2017-01-02", tz=tz_right)
|
||||
|
||||
if com.any_none(tz_left, tz_right):
|
||||
error = TypeError
|
||||
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
||||
else:
|
||||
error = ValueError
|
||||
msg = "left and right must have the same time zone"
|
||||
with pytest.raises(error, match=msg):
|
||||
Interval(left, right)
|
||||
|
||||
def test_equality_comparison_broadcasts_over_array(self):
|
||||
# https://github.com/pandas-dev/pandas/issues/35931
|
||||
interval = Interval(0, 1)
|
||||
arr = np.array([interval, interval])
|
||||
result = interval == arr
|
||||
expected = np.array([True, True])
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Tests for Interval-Interval operations, such as overlaps, contains, etc."""
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
(Timedelta("0 days"), Timedelta("1 day")),
|
||||
(Timestamp("2018-01-01"), Timedelta("1 day")),
|
||||
(0, 1),
|
||||
],
|
||||
ids=lambda x: type(x[0]).__name__,
|
||||
)
|
||||
def start_shift(request):
|
||||
"""
|
||||
Fixture for generating intervals of types from a start value and a shift
|
||||
value that can be added to start to generate an endpoint
|
||||
"""
|
||||
return request.param
|
||||
|
||||
|
||||
class TestOverlaps:
|
||||
def test_overlaps_self(self, start_shift, closed):
|
||||
start, shift = start_shift
|
||||
interval = Interval(start, start + shift, closed)
|
||||
assert interval.overlaps(interval)
|
||||
|
||||
def test_overlaps_nested(self, start_shift, closed, other_closed):
|
||||
start, shift = start_shift
|
||||
interval1 = Interval(start, start + 3 * shift, other_closed)
|
||||
interval2 = Interval(start + shift, start + 2 * shift, closed)
|
||||
|
||||
# nested intervals should always overlap
|
||||
assert interval1.overlaps(interval2)
|
||||
|
||||
def test_overlaps_disjoint(self, start_shift, closed, other_closed):
|
||||
start, shift = start_shift
|
||||
interval1 = Interval(start, start + shift, other_closed)
|
||||
interval2 = Interval(start + 2 * shift, start + 3 * shift, closed)
|
||||
|
||||
# disjoint intervals should never overlap
|
||||
assert not interval1.overlaps(interval2)
|
||||
|
||||
def test_overlaps_endpoint(self, start_shift, closed, other_closed):
|
||||
start, shift = start_shift
|
||||
interval1 = Interval(start, start + shift, other_closed)
|
||||
interval2 = Interval(start + shift, start + 2 * shift, closed)
|
||||
|
||||
# overlap if shared endpoint is closed for both (overlap at a point)
|
||||
result = interval1.overlaps(interval2)
|
||||
expected = interval1.closed_right and interval2.closed_left
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[10, True, "foo", Timedelta("1 day"), Timestamp("2018-01-01")],
|
||||
ids=lambda x: type(x).__name__,
|
||||
)
|
||||
def test_overlaps_invalid_type(self, other):
|
||||
interval = Interval(0, 1)
|
||||
msg = f"`other` must be an Interval, got {type(other).__name__}"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval.overlaps(other)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,801 @@
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs.dtypes import _period_code_map
|
||||
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
|
||||
from pandas import (
|
||||
Period,
|
||||
Timestamp,
|
||||
offsets,
|
||||
)
|
||||
|
||||
|
||||
class TestFreqConversion:
|
||||
"""Test frequency conversion of date objects"""
|
||||
|
||||
@pytest.mark.parametrize("freq", ["A", "Q", "M", "W", "B", "D"])
|
||||
def test_asfreq_near_zero(self, freq):
|
||||
# GH#19643, GH#19650
|
||||
per = Period("0001-01-01", freq=freq)
|
||||
tup1 = (per.year, per.hour, per.day)
|
||||
|
||||
prev = per - 1
|
||||
assert prev.ordinal == per.ordinal - 1
|
||||
tup2 = (prev.year, prev.month, prev.day)
|
||||
assert tup2 < tup1
|
||||
|
||||
def test_asfreq_near_zero_weekly(self):
|
||||
# GH#19834
|
||||
per1 = Period("0001-01-01", "D") + 6
|
||||
per2 = Period("0001-01-01", "D") - 6
|
||||
week1 = per1.asfreq("W")
|
||||
week2 = per2.asfreq("W")
|
||||
assert week1 != week2
|
||||
assert week1.asfreq("D", "E") >= per1
|
||||
assert week2.asfreq("D", "S") <= per2
|
||||
|
||||
def test_to_timestamp_out_of_bounds(self):
|
||||
# GH#19643, used to incorrectly give Timestamp in 1754
|
||||
per = Period("0001-01-01", freq="B")
|
||||
msg = "Out of bounds nanosecond timestamp"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
per.to_timestamp()
|
||||
|
||||
def test_asfreq_corner(self):
|
||||
val = Period(freq="A", year=2007)
|
||||
result1 = val.asfreq("5t")
|
||||
result2 = val.asfreq("t")
|
||||
expected = Period("2007-12-31 23:59", freq="t")
|
||||
assert result1.ordinal == expected.ordinal
|
||||
assert result1.freqstr == "5T"
|
||||
assert result2.ordinal == expected.ordinal
|
||||
assert result2.freqstr == "T"
|
||||
|
||||
def test_conv_annual(self):
|
||||
# frequency conversion tests: from Annual Frequency
|
||||
|
||||
ival_A = Period(freq="A", year=2007)
|
||||
|
||||
ival_AJAN = Period(freq="A-JAN", year=2007)
|
||||
ival_AJUN = Period(freq="A-JUN", year=2007)
|
||||
ival_ANOV = Period(freq="A-NOV", year=2007)
|
||||
|
||||
ival_A_to_Q_start = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_A_to_Q_end = Period(freq="Q", year=2007, quarter=4)
|
||||
ival_A_to_M_start = Period(freq="M", year=2007, month=1)
|
||||
ival_A_to_M_end = Period(freq="M", year=2007, month=12)
|
||||
ival_A_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
||||
ival_A_to_W_end = Period(freq="W", year=2007, month=12, day=31)
|
||||
ival_A_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_A_to_B_end = Period(freq="B", year=2007, month=12, day=31)
|
||||
ival_A_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_A_to_D_end = Period(freq="D", year=2007, month=12, day=31)
|
||||
ival_A_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_A_to_H_end = Period(freq="H", year=2007, month=12, day=31, hour=23)
|
||||
ival_A_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_A_to_T_end = Period(
|
||||
freq="Min", year=2007, month=12, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_A_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_A_to_S_end = Period(
|
||||
freq="S", year=2007, month=12, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
ival_AJAN_to_D_end = Period(freq="D", year=2007, month=1, day=31)
|
||||
ival_AJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
|
||||
ival_AJUN_to_D_end = Period(freq="D", year=2007, month=6, day=30)
|
||||
ival_AJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
|
||||
ival_ANOV_to_D_end = Period(freq="D", year=2007, month=11, day=30)
|
||||
ival_ANOV_to_D_start = Period(freq="D", year=2006, month=12, day=1)
|
||||
|
||||
assert ival_A.asfreq("Q", "S") == ival_A_to_Q_start
|
||||
assert ival_A.asfreq("Q", "e") == ival_A_to_Q_end
|
||||
assert ival_A.asfreq("M", "s") == ival_A_to_M_start
|
||||
assert ival_A.asfreq("M", "E") == ival_A_to_M_end
|
||||
assert ival_A.asfreq("W", "S") == ival_A_to_W_start
|
||||
assert ival_A.asfreq("W", "E") == ival_A_to_W_end
|
||||
assert ival_A.asfreq("B", "S") == ival_A_to_B_start
|
||||
assert ival_A.asfreq("B", "E") == ival_A_to_B_end
|
||||
assert ival_A.asfreq("D", "S") == ival_A_to_D_start
|
||||
assert ival_A.asfreq("D", "E") == ival_A_to_D_end
|
||||
assert ival_A.asfreq("H", "S") == ival_A_to_H_start
|
||||
assert ival_A.asfreq("H", "E") == ival_A_to_H_end
|
||||
assert ival_A.asfreq("min", "S") == ival_A_to_T_start
|
||||
assert ival_A.asfreq("min", "E") == ival_A_to_T_end
|
||||
assert ival_A.asfreq("T", "S") == ival_A_to_T_start
|
||||
assert ival_A.asfreq("T", "E") == ival_A_to_T_end
|
||||
assert ival_A.asfreq("S", "S") == ival_A_to_S_start
|
||||
assert ival_A.asfreq("S", "E") == ival_A_to_S_end
|
||||
|
||||
assert ival_AJAN.asfreq("D", "S") == ival_AJAN_to_D_start
|
||||
assert ival_AJAN.asfreq("D", "E") == ival_AJAN_to_D_end
|
||||
|
||||
assert ival_AJUN.asfreq("D", "S") == ival_AJUN_to_D_start
|
||||
assert ival_AJUN.asfreq("D", "E") == ival_AJUN_to_D_end
|
||||
|
||||
assert ival_ANOV.asfreq("D", "S") == ival_ANOV_to_D_start
|
||||
assert ival_ANOV.asfreq("D", "E") == ival_ANOV_to_D_end
|
||||
|
||||
assert ival_A.asfreq("A") == ival_A
|
||||
|
||||
def test_conv_quarterly(self):
|
||||
# frequency conversion tests: from Quarterly Frequency
|
||||
|
||||
ival_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_Q_end_of_year = Period(freq="Q", year=2007, quarter=4)
|
||||
|
||||
ival_QEJAN = Period(freq="Q-JAN", year=2007, quarter=1)
|
||||
ival_QEJUN = Period(freq="Q-JUN", year=2007, quarter=1)
|
||||
|
||||
ival_Q_to_A = Period(freq="A", year=2007)
|
||||
ival_Q_to_M_start = Period(freq="M", year=2007, month=1)
|
||||
ival_Q_to_M_end = Period(freq="M", year=2007, month=3)
|
||||
ival_Q_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
||||
ival_Q_to_W_end = Period(freq="W", year=2007, month=3, day=31)
|
||||
ival_Q_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_Q_to_B_end = Period(freq="B", year=2007, month=3, day=30)
|
||||
ival_Q_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_Q_to_D_end = Period(freq="D", year=2007, month=3, day=31)
|
||||
ival_Q_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_Q_to_H_end = Period(freq="H", year=2007, month=3, day=31, hour=23)
|
||||
ival_Q_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_Q_to_T_end = Period(
|
||||
freq="Min", year=2007, month=3, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_Q_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_Q_to_S_end = Period(
|
||||
freq="S", year=2007, month=3, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
ival_QEJAN_to_D_start = Period(freq="D", year=2006, month=2, day=1)
|
||||
ival_QEJAN_to_D_end = Period(freq="D", year=2006, month=4, day=30)
|
||||
|
||||
ival_QEJUN_to_D_start = Period(freq="D", year=2006, month=7, day=1)
|
||||
ival_QEJUN_to_D_end = Period(freq="D", year=2006, month=9, day=30)
|
||||
|
||||
assert ival_Q.asfreq("A") == ival_Q_to_A
|
||||
assert ival_Q_end_of_year.asfreq("A") == ival_Q_to_A
|
||||
|
||||
assert ival_Q.asfreq("M", "S") == ival_Q_to_M_start
|
||||
assert ival_Q.asfreq("M", "E") == ival_Q_to_M_end
|
||||
assert ival_Q.asfreq("W", "S") == ival_Q_to_W_start
|
||||
assert ival_Q.asfreq("W", "E") == ival_Q_to_W_end
|
||||
assert ival_Q.asfreq("B", "S") == ival_Q_to_B_start
|
||||
assert ival_Q.asfreq("B", "E") == ival_Q_to_B_end
|
||||
assert ival_Q.asfreq("D", "S") == ival_Q_to_D_start
|
||||
assert ival_Q.asfreq("D", "E") == ival_Q_to_D_end
|
||||
assert ival_Q.asfreq("H", "S") == ival_Q_to_H_start
|
||||
assert ival_Q.asfreq("H", "E") == ival_Q_to_H_end
|
||||
assert ival_Q.asfreq("Min", "S") == ival_Q_to_T_start
|
||||
assert ival_Q.asfreq("Min", "E") == ival_Q_to_T_end
|
||||
assert ival_Q.asfreq("S", "S") == ival_Q_to_S_start
|
||||
assert ival_Q.asfreq("S", "E") == ival_Q_to_S_end
|
||||
|
||||
assert ival_QEJAN.asfreq("D", "S") == ival_QEJAN_to_D_start
|
||||
assert ival_QEJAN.asfreq("D", "E") == ival_QEJAN_to_D_end
|
||||
assert ival_QEJUN.asfreq("D", "S") == ival_QEJUN_to_D_start
|
||||
assert ival_QEJUN.asfreq("D", "E") == ival_QEJUN_to_D_end
|
||||
|
||||
assert ival_Q.asfreq("Q") == ival_Q
|
||||
|
||||
def test_conv_monthly(self):
|
||||
# frequency conversion tests: from Monthly Frequency
|
||||
|
||||
ival_M = Period(freq="M", year=2007, month=1)
|
||||
ival_M_end_of_year = Period(freq="M", year=2007, month=12)
|
||||
ival_M_end_of_quarter = Period(freq="M", year=2007, month=3)
|
||||
ival_M_to_A = Period(freq="A", year=2007)
|
||||
ival_M_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_M_to_W_start = Period(freq="W", year=2007, month=1, day=1)
|
||||
ival_M_to_W_end = Period(freq="W", year=2007, month=1, day=31)
|
||||
ival_M_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_M_to_B_end = Period(freq="B", year=2007, month=1, day=31)
|
||||
ival_M_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_M_to_D_end = Period(freq="D", year=2007, month=1, day=31)
|
||||
ival_M_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_M_to_H_end = Period(freq="H", year=2007, month=1, day=31, hour=23)
|
||||
ival_M_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_M_to_T_end = Period(
|
||||
freq="Min", year=2007, month=1, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_M_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_M_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
assert ival_M.asfreq("A") == ival_M_to_A
|
||||
assert ival_M_end_of_year.asfreq("A") == ival_M_to_A
|
||||
assert ival_M.asfreq("Q") == ival_M_to_Q
|
||||
assert ival_M_end_of_quarter.asfreq("Q") == ival_M_to_Q
|
||||
|
||||
assert ival_M.asfreq("W", "S") == ival_M_to_W_start
|
||||
assert ival_M.asfreq("W", "E") == ival_M_to_W_end
|
||||
assert ival_M.asfreq("B", "S") == ival_M_to_B_start
|
||||
assert ival_M.asfreq("B", "E") == ival_M_to_B_end
|
||||
assert ival_M.asfreq("D", "S") == ival_M_to_D_start
|
||||
assert ival_M.asfreq("D", "E") == ival_M_to_D_end
|
||||
assert ival_M.asfreq("H", "S") == ival_M_to_H_start
|
||||
assert ival_M.asfreq("H", "E") == ival_M_to_H_end
|
||||
assert ival_M.asfreq("Min", "S") == ival_M_to_T_start
|
||||
assert ival_M.asfreq("Min", "E") == ival_M_to_T_end
|
||||
assert ival_M.asfreq("S", "S") == ival_M_to_S_start
|
||||
assert ival_M.asfreq("S", "E") == ival_M_to_S_end
|
||||
|
||||
assert ival_M.asfreq("M") == ival_M
|
||||
|
||||
def test_conv_weekly(self):
|
||||
# frequency conversion tests: from Weekly Frequency
|
||||
ival_W = Period(freq="W", year=2007, month=1, day=1)
|
||||
|
||||
ival_WSUN = Period(freq="W", year=2007, month=1, day=7)
|
||||
ival_WSAT = Period(freq="W-SAT", year=2007, month=1, day=6)
|
||||
ival_WFRI = Period(freq="W-FRI", year=2007, month=1, day=5)
|
||||
ival_WTHU = Period(freq="W-THU", year=2007, month=1, day=4)
|
||||
ival_WWED = Period(freq="W-WED", year=2007, month=1, day=3)
|
||||
ival_WTUE = Period(freq="W-TUE", year=2007, month=1, day=2)
|
||||
ival_WMON = Period(freq="W-MON", year=2007, month=1, day=1)
|
||||
|
||||
ival_WSUN_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_WSUN_to_D_end = Period(freq="D", year=2007, month=1, day=7)
|
||||
ival_WSAT_to_D_start = Period(freq="D", year=2006, month=12, day=31)
|
||||
ival_WSAT_to_D_end = Period(freq="D", year=2007, month=1, day=6)
|
||||
ival_WFRI_to_D_start = Period(freq="D", year=2006, month=12, day=30)
|
||||
ival_WFRI_to_D_end = Period(freq="D", year=2007, month=1, day=5)
|
||||
ival_WTHU_to_D_start = Period(freq="D", year=2006, month=12, day=29)
|
||||
ival_WTHU_to_D_end = Period(freq="D", year=2007, month=1, day=4)
|
||||
ival_WWED_to_D_start = Period(freq="D", year=2006, month=12, day=28)
|
||||
ival_WWED_to_D_end = Period(freq="D", year=2007, month=1, day=3)
|
||||
ival_WTUE_to_D_start = Period(freq="D", year=2006, month=12, day=27)
|
||||
ival_WTUE_to_D_end = Period(freq="D", year=2007, month=1, day=2)
|
||||
ival_WMON_to_D_start = Period(freq="D", year=2006, month=12, day=26)
|
||||
ival_WMON_to_D_end = Period(freq="D", year=2007, month=1, day=1)
|
||||
|
||||
ival_W_end_of_year = Period(freq="W", year=2007, month=12, day=31)
|
||||
ival_W_end_of_quarter = Period(freq="W", year=2007, month=3, day=31)
|
||||
ival_W_end_of_month = Period(freq="W", year=2007, month=1, day=31)
|
||||
ival_W_to_A = Period(freq="A", year=2007)
|
||||
ival_W_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_W_to_M = Period(freq="M", year=2007, month=1)
|
||||
|
||||
if Period(freq="D", year=2007, month=12, day=31).weekday == 6:
|
||||
ival_W_to_A_end_of_year = Period(freq="A", year=2007)
|
||||
else:
|
||||
ival_W_to_A_end_of_year = Period(freq="A", year=2008)
|
||||
|
||||
if Period(freq="D", year=2007, month=3, day=31).weekday == 6:
|
||||
ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=1)
|
||||
else:
|
||||
ival_W_to_Q_end_of_quarter = Period(freq="Q", year=2007, quarter=2)
|
||||
|
||||
if Period(freq="D", year=2007, month=1, day=31).weekday == 6:
|
||||
ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=1)
|
||||
else:
|
||||
ival_W_to_M_end_of_month = Period(freq="M", year=2007, month=2)
|
||||
|
||||
ival_W_to_B_start = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_W_to_B_end = Period(freq="B", year=2007, month=1, day=5)
|
||||
ival_W_to_D_start = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_W_to_D_end = Period(freq="D", year=2007, month=1, day=7)
|
||||
ival_W_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_W_to_H_end = Period(freq="H", year=2007, month=1, day=7, hour=23)
|
||||
ival_W_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_W_to_T_end = Period(
|
||||
freq="Min", year=2007, month=1, day=7, hour=23, minute=59
|
||||
)
|
||||
ival_W_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_W_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=7, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
assert ival_W.asfreq("A") == ival_W_to_A
|
||||
assert ival_W_end_of_year.asfreq("A") == ival_W_to_A_end_of_year
|
||||
|
||||
assert ival_W.asfreq("Q") == ival_W_to_Q
|
||||
assert ival_W_end_of_quarter.asfreq("Q") == ival_W_to_Q_end_of_quarter
|
||||
|
||||
assert ival_W.asfreq("M") == ival_W_to_M
|
||||
assert ival_W_end_of_month.asfreq("M") == ival_W_to_M_end_of_month
|
||||
|
||||
assert ival_W.asfreq("B", "S") == ival_W_to_B_start
|
||||
assert ival_W.asfreq("B", "E") == ival_W_to_B_end
|
||||
|
||||
assert ival_W.asfreq("D", "S") == ival_W_to_D_start
|
||||
assert ival_W.asfreq("D", "E") == ival_W_to_D_end
|
||||
|
||||
assert ival_WSUN.asfreq("D", "S") == ival_WSUN_to_D_start
|
||||
assert ival_WSUN.asfreq("D", "E") == ival_WSUN_to_D_end
|
||||
assert ival_WSAT.asfreq("D", "S") == ival_WSAT_to_D_start
|
||||
assert ival_WSAT.asfreq("D", "E") == ival_WSAT_to_D_end
|
||||
assert ival_WFRI.asfreq("D", "S") == ival_WFRI_to_D_start
|
||||
assert ival_WFRI.asfreq("D", "E") == ival_WFRI_to_D_end
|
||||
assert ival_WTHU.asfreq("D", "S") == ival_WTHU_to_D_start
|
||||
assert ival_WTHU.asfreq("D", "E") == ival_WTHU_to_D_end
|
||||
assert ival_WWED.asfreq("D", "S") == ival_WWED_to_D_start
|
||||
assert ival_WWED.asfreq("D", "E") == ival_WWED_to_D_end
|
||||
assert ival_WTUE.asfreq("D", "S") == ival_WTUE_to_D_start
|
||||
assert ival_WTUE.asfreq("D", "E") == ival_WTUE_to_D_end
|
||||
assert ival_WMON.asfreq("D", "S") == ival_WMON_to_D_start
|
||||
assert ival_WMON.asfreq("D", "E") == ival_WMON_to_D_end
|
||||
|
||||
assert ival_W.asfreq("H", "S") == ival_W_to_H_start
|
||||
assert ival_W.asfreq("H", "E") == ival_W_to_H_end
|
||||
assert ival_W.asfreq("Min", "S") == ival_W_to_T_start
|
||||
assert ival_W.asfreq("Min", "E") == ival_W_to_T_end
|
||||
assert ival_W.asfreq("S", "S") == ival_W_to_S_start
|
||||
assert ival_W.asfreq("S", "E") == ival_W_to_S_end
|
||||
|
||||
assert ival_W.asfreq("W") == ival_W
|
||||
|
||||
msg = INVALID_FREQ_ERR_MSG
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ival_W.asfreq("WK")
|
||||
|
||||
def test_conv_weekly_legacy(self):
|
||||
# frequency conversion tests: from Weekly Frequency
|
||||
msg = INVALID_FREQ_ERR_MSG
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK", year=2007, month=1, day=1)
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-SAT", year=2007, month=1, day=6)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-FRI", year=2007, month=1, day=5)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-THU", year=2007, month=1, day=4)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-WED", year=2007, month=1, day=3)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-TUE", year=2007, month=1, day=2)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period(freq="WK-MON", year=2007, month=1, day=1)
|
||||
|
||||
def test_conv_business(self):
|
||||
# frequency conversion tests: from Business Frequency"
|
||||
|
||||
ival_B = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_B_end_of_year = Period(freq="B", year=2007, month=12, day=31)
|
||||
ival_B_end_of_quarter = Period(freq="B", year=2007, month=3, day=30)
|
||||
ival_B_end_of_month = Period(freq="B", year=2007, month=1, day=31)
|
||||
ival_B_end_of_week = Period(freq="B", year=2007, month=1, day=5)
|
||||
|
||||
ival_B_to_A = Period(freq="A", year=2007)
|
||||
ival_B_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_B_to_M = Period(freq="M", year=2007, month=1)
|
||||
ival_B_to_W = Period(freq="W", year=2007, month=1, day=7)
|
||||
ival_B_to_D = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_B_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_B_to_H_end = Period(freq="H", year=2007, month=1, day=1, hour=23)
|
||||
ival_B_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_B_to_T_end = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
||||
)
|
||||
ival_B_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_B_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
assert ival_B.asfreq("A") == ival_B_to_A
|
||||
assert ival_B_end_of_year.asfreq("A") == ival_B_to_A
|
||||
assert ival_B.asfreq("Q") == ival_B_to_Q
|
||||
assert ival_B_end_of_quarter.asfreq("Q") == ival_B_to_Q
|
||||
assert ival_B.asfreq("M") == ival_B_to_M
|
||||
assert ival_B_end_of_month.asfreq("M") == ival_B_to_M
|
||||
assert ival_B.asfreq("W") == ival_B_to_W
|
||||
assert ival_B_end_of_week.asfreq("W") == ival_B_to_W
|
||||
|
||||
assert ival_B.asfreq("D") == ival_B_to_D
|
||||
|
||||
assert ival_B.asfreq("H", "S") == ival_B_to_H_start
|
||||
assert ival_B.asfreq("H", "E") == ival_B_to_H_end
|
||||
assert ival_B.asfreq("Min", "S") == ival_B_to_T_start
|
||||
assert ival_B.asfreq("Min", "E") == ival_B_to_T_end
|
||||
assert ival_B.asfreq("S", "S") == ival_B_to_S_start
|
||||
assert ival_B.asfreq("S", "E") == ival_B_to_S_end
|
||||
|
||||
assert ival_B.asfreq("B") == ival_B
|
||||
|
||||
def test_conv_daily(self):
|
||||
# frequency conversion tests: from Business Frequency"
|
||||
|
||||
ival_D = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_D_end_of_year = Period(freq="D", year=2007, month=12, day=31)
|
||||
ival_D_end_of_quarter = Period(freq="D", year=2007, month=3, day=31)
|
||||
ival_D_end_of_month = Period(freq="D", year=2007, month=1, day=31)
|
||||
ival_D_end_of_week = Period(freq="D", year=2007, month=1, day=7)
|
||||
|
||||
ival_D_friday = Period(freq="D", year=2007, month=1, day=5)
|
||||
ival_D_saturday = Period(freq="D", year=2007, month=1, day=6)
|
||||
ival_D_sunday = Period(freq="D", year=2007, month=1, day=7)
|
||||
|
||||
ival_B_friday = Period(freq="B", year=2007, month=1, day=5)
|
||||
ival_B_monday = Period(freq="B", year=2007, month=1, day=8)
|
||||
|
||||
ival_D_to_A = Period(freq="A", year=2007)
|
||||
|
||||
ival_Deoq_to_AJAN = Period(freq="A-JAN", year=2008)
|
||||
ival_Deoq_to_AJUN = Period(freq="A-JUN", year=2007)
|
||||
ival_Deoq_to_ADEC = Period(freq="A-DEC", year=2007)
|
||||
|
||||
ival_D_to_QEJAN = Period(freq="Q-JAN", year=2007, quarter=4)
|
||||
ival_D_to_QEJUN = Period(freq="Q-JUN", year=2007, quarter=3)
|
||||
ival_D_to_QEDEC = Period(freq="Q-DEC", year=2007, quarter=1)
|
||||
|
||||
ival_D_to_M = Period(freq="M", year=2007, month=1)
|
||||
ival_D_to_W = Period(freq="W", year=2007, month=1, day=7)
|
||||
|
||||
ival_D_to_H_start = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_D_to_H_end = Period(freq="H", year=2007, month=1, day=1, hour=23)
|
||||
ival_D_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_D_to_T_end = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
||||
)
|
||||
ival_D_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_D_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
||||
)
|
||||
|
||||
assert ival_D.asfreq("A") == ival_D_to_A
|
||||
|
||||
assert ival_D_end_of_quarter.asfreq("A-JAN") == ival_Deoq_to_AJAN
|
||||
assert ival_D_end_of_quarter.asfreq("A-JUN") == ival_Deoq_to_AJUN
|
||||
assert ival_D_end_of_quarter.asfreq("A-DEC") == ival_Deoq_to_ADEC
|
||||
|
||||
assert ival_D_end_of_year.asfreq("A") == ival_D_to_A
|
||||
assert ival_D_end_of_quarter.asfreq("Q") == ival_D_to_QEDEC
|
||||
assert ival_D.asfreq("Q-JAN") == ival_D_to_QEJAN
|
||||
assert ival_D.asfreq("Q-JUN") == ival_D_to_QEJUN
|
||||
assert ival_D.asfreq("Q-DEC") == ival_D_to_QEDEC
|
||||
assert ival_D.asfreq("M") == ival_D_to_M
|
||||
assert ival_D_end_of_month.asfreq("M") == ival_D_to_M
|
||||
assert ival_D.asfreq("W") == ival_D_to_W
|
||||
assert ival_D_end_of_week.asfreq("W") == ival_D_to_W
|
||||
|
||||
assert ival_D_friday.asfreq("B") == ival_B_friday
|
||||
assert ival_D_saturday.asfreq("B", "S") == ival_B_friday
|
||||
assert ival_D_saturday.asfreq("B", "E") == ival_B_monday
|
||||
assert ival_D_sunday.asfreq("B", "S") == ival_B_friday
|
||||
assert ival_D_sunday.asfreq("B", "E") == ival_B_monday
|
||||
|
||||
assert ival_D.asfreq("H", "S") == ival_D_to_H_start
|
||||
assert ival_D.asfreq("H", "E") == ival_D_to_H_end
|
||||
assert ival_D.asfreq("Min", "S") == ival_D_to_T_start
|
||||
assert ival_D.asfreq("Min", "E") == ival_D_to_T_end
|
||||
assert ival_D.asfreq("S", "S") == ival_D_to_S_start
|
||||
assert ival_D.asfreq("S", "E") == ival_D_to_S_end
|
||||
|
||||
assert ival_D.asfreq("D") == ival_D
|
||||
|
||||
def test_conv_hourly(self):
|
||||
# frequency conversion tests: from Hourly Frequency"
|
||||
|
||||
ival_H = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_H_end_of_year = Period(freq="H", year=2007, month=12, day=31, hour=23)
|
||||
ival_H_end_of_quarter = Period(freq="H", year=2007, month=3, day=31, hour=23)
|
||||
ival_H_end_of_month = Period(freq="H", year=2007, month=1, day=31, hour=23)
|
||||
ival_H_end_of_week = Period(freq="H", year=2007, month=1, day=7, hour=23)
|
||||
ival_H_end_of_day = Period(freq="H", year=2007, month=1, day=1, hour=23)
|
||||
ival_H_end_of_bus = Period(freq="H", year=2007, month=1, day=1, hour=23)
|
||||
|
||||
ival_H_to_A = Period(freq="A", year=2007)
|
||||
ival_H_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_H_to_M = Period(freq="M", year=2007, month=1)
|
||||
ival_H_to_W = Period(freq="W", year=2007, month=1, day=7)
|
||||
ival_H_to_D = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_H_to_B = Period(freq="B", year=2007, month=1, day=1)
|
||||
|
||||
ival_H_to_T_start = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=0
|
||||
)
|
||||
ival_H_to_T_end = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=59
|
||||
)
|
||||
ival_H_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_H_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=59, second=59
|
||||
)
|
||||
|
||||
assert ival_H.asfreq("A") == ival_H_to_A
|
||||
assert ival_H_end_of_year.asfreq("A") == ival_H_to_A
|
||||
assert ival_H.asfreq("Q") == ival_H_to_Q
|
||||
assert ival_H_end_of_quarter.asfreq("Q") == ival_H_to_Q
|
||||
assert ival_H.asfreq("M") == ival_H_to_M
|
||||
assert ival_H_end_of_month.asfreq("M") == ival_H_to_M
|
||||
assert ival_H.asfreq("W") == ival_H_to_W
|
||||
assert ival_H_end_of_week.asfreq("W") == ival_H_to_W
|
||||
assert ival_H.asfreq("D") == ival_H_to_D
|
||||
assert ival_H_end_of_day.asfreq("D") == ival_H_to_D
|
||||
assert ival_H.asfreq("B") == ival_H_to_B
|
||||
assert ival_H_end_of_bus.asfreq("B") == ival_H_to_B
|
||||
|
||||
assert ival_H.asfreq("Min", "S") == ival_H_to_T_start
|
||||
assert ival_H.asfreq("Min", "E") == ival_H_to_T_end
|
||||
assert ival_H.asfreq("S", "S") == ival_H_to_S_start
|
||||
assert ival_H.asfreq("S", "E") == ival_H_to_S_end
|
||||
|
||||
assert ival_H.asfreq("H") == ival_H
|
||||
|
||||
def test_conv_minutely(self):
|
||||
# frequency conversion tests: from Minutely Frequency"
|
||||
|
||||
ival_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
|
||||
ival_T_end_of_year = Period(
|
||||
freq="Min", year=2007, month=12, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_quarter = Period(
|
||||
freq="Min", year=2007, month=3, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_month = Period(
|
||||
freq="Min", year=2007, month=1, day=31, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_week = Period(
|
||||
freq="Min", year=2007, month=1, day=7, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_day = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_bus = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=23, minute=59
|
||||
)
|
||||
ival_T_end_of_hour = Period(
|
||||
freq="Min", year=2007, month=1, day=1, hour=0, minute=59
|
||||
)
|
||||
|
||||
ival_T_to_A = Period(freq="A", year=2007)
|
||||
ival_T_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_T_to_M = Period(freq="M", year=2007, month=1)
|
||||
ival_T_to_W = Period(freq="W", year=2007, month=1, day=7)
|
||||
ival_T_to_D = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_T_to_B = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_T_to_H = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
|
||||
ival_T_to_S_start = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0
|
||||
)
|
||||
ival_T_to_S_end = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=59
|
||||
)
|
||||
|
||||
assert ival_T.asfreq("A") == ival_T_to_A
|
||||
assert ival_T_end_of_year.asfreq("A") == ival_T_to_A
|
||||
assert ival_T.asfreq("Q") == ival_T_to_Q
|
||||
assert ival_T_end_of_quarter.asfreq("Q") == ival_T_to_Q
|
||||
assert ival_T.asfreq("M") == ival_T_to_M
|
||||
assert ival_T_end_of_month.asfreq("M") == ival_T_to_M
|
||||
assert ival_T.asfreq("W") == ival_T_to_W
|
||||
assert ival_T_end_of_week.asfreq("W") == ival_T_to_W
|
||||
assert ival_T.asfreq("D") == ival_T_to_D
|
||||
assert ival_T_end_of_day.asfreq("D") == ival_T_to_D
|
||||
assert ival_T.asfreq("B") == ival_T_to_B
|
||||
assert ival_T_end_of_bus.asfreq("B") == ival_T_to_B
|
||||
assert ival_T.asfreq("H") == ival_T_to_H
|
||||
assert ival_T_end_of_hour.asfreq("H") == ival_T_to_H
|
||||
|
||||
assert ival_T.asfreq("S", "S") == ival_T_to_S_start
|
||||
assert ival_T.asfreq("S", "E") == ival_T_to_S_end
|
||||
|
||||
assert ival_T.asfreq("Min") == ival_T
|
||||
|
||||
def test_conv_secondly(self):
|
||||
# frequency conversion tests: from Secondly Frequency"
|
||||
|
||||
ival_S = Period(freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=0)
|
||||
ival_S_end_of_year = Period(
|
||||
freq="S", year=2007, month=12, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_quarter = Period(
|
||||
freq="S", year=2007, month=3, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_month = Period(
|
||||
freq="S", year=2007, month=1, day=31, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_week = Period(
|
||||
freq="S", year=2007, month=1, day=7, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_day = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_bus = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=23, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_hour = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=59, second=59
|
||||
)
|
||||
ival_S_end_of_minute = Period(
|
||||
freq="S", year=2007, month=1, day=1, hour=0, minute=0, second=59
|
||||
)
|
||||
|
||||
ival_S_to_A = Period(freq="A", year=2007)
|
||||
ival_S_to_Q = Period(freq="Q", year=2007, quarter=1)
|
||||
ival_S_to_M = Period(freq="M", year=2007, month=1)
|
||||
ival_S_to_W = Period(freq="W", year=2007, month=1, day=7)
|
||||
ival_S_to_D = Period(freq="D", year=2007, month=1, day=1)
|
||||
ival_S_to_B = Period(freq="B", year=2007, month=1, day=1)
|
||||
ival_S_to_H = Period(freq="H", year=2007, month=1, day=1, hour=0)
|
||||
ival_S_to_T = Period(freq="Min", year=2007, month=1, day=1, hour=0, minute=0)
|
||||
|
||||
assert ival_S.asfreq("A") == ival_S_to_A
|
||||
assert ival_S_end_of_year.asfreq("A") == ival_S_to_A
|
||||
assert ival_S.asfreq("Q") == ival_S_to_Q
|
||||
assert ival_S_end_of_quarter.asfreq("Q") == ival_S_to_Q
|
||||
assert ival_S.asfreq("M") == ival_S_to_M
|
||||
assert ival_S_end_of_month.asfreq("M") == ival_S_to_M
|
||||
assert ival_S.asfreq("W") == ival_S_to_W
|
||||
assert ival_S_end_of_week.asfreq("W") == ival_S_to_W
|
||||
assert ival_S.asfreq("D") == ival_S_to_D
|
||||
assert ival_S_end_of_day.asfreq("D") == ival_S_to_D
|
||||
assert ival_S.asfreq("B") == ival_S_to_B
|
||||
assert ival_S_end_of_bus.asfreq("B") == ival_S_to_B
|
||||
assert ival_S.asfreq("H") == ival_S_to_H
|
||||
assert ival_S_end_of_hour.asfreq("H") == ival_S_to_H
|
||||
assert ival_S.asfreq("Min") == ival_S_to_T
|
||||
assert ival_S_end_of_minute.asfreq("Min") == ival_S_to_T
|
||||
|
||||
assert ival_S.asfreq("S") == ival_S
|
||||
|
||||
def test_conv_microsecond(self):
|
||||
# GH#31475 Avoid floating point errors dropping the start_time to
|
||||
# before the beginning of the Period
|
||||
per = Period("2020-01-30 15:57:27.576166", freq="U")
|
||||
assert per.ordinal == 1580399847576166
|
||||
|
||||
start = per.start_time
|
||||
expected = Timestamp("2020-01-30 15:57:27.576166")
|
||||
assert start == expected
|
||||
assert start.value == per.ordinal * 1000
|
||||
|
||||
per2 = Period("2300-01-01", "us")
|
||||
msg = "2300-01-01"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
per2.start_time
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
per2.end_time
|
||||
|
||||
def test_asfreq_mult(self):
|
||||
# normal freq to mult freq
|
||||
p = Period(freq="A", year=2007)
|
||||
# ordinal will not change
|
||||
for freq in ["3A", offsets.YearEnd(3)]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2007", freq="3A")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
# ordinal will not change
|
||||
for freq in ["3A", offsets.YearEnd(3)]:
|
||||
result = p.asfreq(freq, how="S")
|
||||
expected = Period("2007", freq="3A")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
# mult freq to normal freq
|
||||
p = Period(freq="3A", year=2007)
|
||||
# ordinal will change because how=E is the default
|
||||
for freq in ["A", offsets.YearEnd()]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2009", freq="A")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
# ordinal will not change
|
||||
for freq in ["A", offsets.YearEnd()]:
|
||||
result = p.asfreq(freq, how="S")
|
||||
expected = Period("2007", freq="A")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
p = Period(freq="A", year=2007)
|
||||
for freq in ["2M", offsets.MonthEnd(2)]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2007-12", freq="2M")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
for freq in ["2M", offsets.MonthEnd(2)]:
|
||||
result = p.asfreq(freq, how="S")
|
||||
expected = Period("2007-01", freq="2M")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
p = Period(freq="3A", year=2007)
|
||||
for freq in ["2M", offsets.MonthEnd(2)]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2009-12", freq="2M")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
for freq in ["2M", offsets.MonthEnd(2)]:
|
||||
result = p.asfreq(freq, how="S")
|
||||
expected = Period("2007-01", freq="2M")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
def test_asfreq_combined(self):
|
||||
# normal freq to combined freq
|
||||
p = Period("2007", freq="H")
|
||||
|
||||
# ordinal will not change
|
||||
expected = Period("2007", freq="25H")
|
||||
for freq, how in zip(["1D1H", "1H1D"], ["E", "S"]):
|
||||
result = p.asfreq(freq, how=how)
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
# combined freq to normal freq
|
||||
p1 = Period(freq="1D1H", year=2007)
|
||||
p2 = Period(freq="1H1D", year=2007)
|
||||
|
||||
# ordinal will change because how=E is the default
|
||||
result1 = p1.asfreq("H")
|
||||
result2 = p2.asfreq("H")
|
||||
expected = Period("2007-01-02", freq="H")
|
||||
assert result1 == expected
|
||||
assert result1.ordinal == expected.ordinal
|
||||
assert result1.freq == expected.freq
|
||||
assert result2 == expected
|
||||
assert result2.ordinal == expected.ordinal
|
||||
assert result2.freq == expected.freq
|
||||
|
||||
# ordinal will not change
|
||||
result1 = p1.asfreq("H", how="S")
|
||||
result2 = p2.asfreq("H", how="S")
|
||||
expected = Period("2007-01-01", freq="H")
|
||||
assert result1 == expected
|
||||
assert result1.ordinal == expected.ordinal
|
||||
assert result1.freq == expected.freq
|
||||
assert result2 == expected
|
||||
assert result2.ordinal == expected.ordinal
|
||||
assert result2.freq == expected.freq
|
||||
|
||||
def test_asfreq_MS(self):
|
||||
initial = Period("2013")
|
||||
|
||||
assert initial.asfreq(freq="M", how="S") == Period("2013-01", "M")
|
||||
|
||||
msg = INVALID_FREQ_ERR_MSG
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
initial.asfreq(freq="MS", how="S")
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period("2013-01", "MS")
|
||||
|
||||
assert _period_code_map.get("MS") is None
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,307 @@
|
||||
import pickle
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs.missing import NA
|
||||
|
||||
from pandas.core.dtypes.common import is_scalar
|
||||
|
||||
import pandas as pd
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
def test_singleton():
|
||||
assert NA is NA
|
||||
new_NA = type(NA)()
|
||||
assert new_NA is NA
|
||||
|
||||
|
||||
def test_repr():
|
||||
assert repr(NA) == "<NA>"
|
||||
assert str(NA) == "<NA>"
|
||||
|
||||
|
||||
def test_format():
|
||||
# GH-34740
|
||||
assert format(NA) == "<NA>"
|
||||
assert format(NA, ">10") == " <NA>"
|
||||
assert format(NA, "xxx") == "<NA>" # NA is flexible, accept any format spec
|
||||
|
||||
assert f"{NA}" == "<NA>"
|
||||
assert f"{NA:>10}" == " <NA>"
|
||||
assert f"{NA:xxx}" == "<NA>"
|
||||
|
||||
|
||||
def test_truthiness():
|
||||
msg = "boolean value of NA is ambiguous"
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
bool(NA)
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
not NA
|
||||
|
||||
|
||||
def test_hashable():
|
||||
assert hash(NA) == hash(NA)
|
||||
d = {NA: "test"}
|
||||
assert d[NA] == "test"
|
||||
|
||||
|
||||
def test_arithmetic_ops(all_arithmetic_functions):
|
||||
op = all_arithmetic_functions
|
||||
|
||||
for other in [NA, 1, 1.0, "a", np.int64(1), np.nan]:
|
||||
if op.__name__ in ("pow", "rpow", "rmod") and isinstance(other, str):
|
||||
continue
|
||||
if op.__name__ in ("divmod", "rdivmod"):
|
||||
assert op(NA, other) is (NA, NA)
|
||||
else:
|
||||
if op.__name__ == "rpow":
|
||||
# avoid special case
|
||||
other += 1
|
||||
assert op(NA, other) is NA
|
||||
|
||||
|
||||
def test_comparison_ops():
|
||||
|
||||
for other in [NA, 1, 1.0, "a", np.int64(1), np.nan, np.bool_(True)]:
|
||||
assert (NA == other) is NA
|
||||
assert (NA != other) is NA
|
||||
assert (NA > other) is NA
|
||||
assert (NA >= other) is NA
|
||||
assert (NA < other) is NA
|
||||
assert (NA <= other) is NA
|
||||
assert (other == NA) is NA
|
||||
assert (other != NA) is NA
|
||||
assert (other > NA) is NA
|
||||
assert (other >= NA) is NA
|
||||
assert (other < NA) is NA
|
||||
assert (other <= NA) is NA
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
0,
|
||||
0.0,
|
||||
-0,
|
||||
-0.0,
|
||||
False,
|
||||
np.bool_(False),
|
||||
np.int_(0),
|
||||
np.float_(0),
|
||||
np.int_(-0),
|
||||
np.float_(-0),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("asarray", [True, False])
|
||||
def test_pow_special(value, asarray):
|
||||
if asarray:
|
||||
value = np.array([value])
|
||||
result = NA**value
|
||||
|
||||
if asarray:
|
||||
result = result[0]
|
||||
else:
|
||||
# this assertion isn't possible for ndarray.
|
||||
assert isinstance(result, type(value))
|
||||
assert result == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value", [1, 1.0, True, np.bool_(True), np.int_(1), np.float_(1)]
|
||||
)
|
||||
@pytest.mark.parametrize("asarray", [True, False])
|
||||
def test_rpow_special(value, asarray):
|
||||
if asarray:
|
||||
value = np.array([value])
|
||||
result = value**NA
|
||||
|
||||
if asarray:
|
||||
result = result[0]
|
||||
elif not isinstance(value, (np.float_, np.bool_, np.int_)):
|
||||
# this assertion isn't possible with asarray=True
|
||||
assert isinstance(result, type(value))
|
||||
|
||||
assert result == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", [-1, -1.0, np.int_(-1), np.float_(-1)])
|
||||
@pytest.mark.parametrize("asarray", [True, False])
|
||||
def test_rpow_minus_one(value, asarray):
|
||||
if asarray:
|
||||
value = np.array([value])
|
||||
result = value**NA
|
||||
|
||||
if asarray:
|
||||
result = result[0]
|
||||
|
||||
assert pd.isna(result)
|
||||
|
||||
|
||||
def test_unary_ops():
|
||||
assert +NA is NA
|
||||
assert -NA is NA
|
||||
assert abs(NA) is NA
|
||||
assert ~NA is NA
|
||||
|
||||
|
||||
def test_logical_and():
|
||||
|
||||
assert NA & True is NA
|
||||
assert True & NA is NA
|
||||
assert NA & False is False
|
||||
assert False & NA is False
|
||||
assert NA & NA is NA
|
||||
|
||||
msg = "unsupported operand type"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
NA & 5
|
||||
|
||||
|
||||
def test_logical_or():
|
||||
|
||||
assert NA | True is True
|
||||
assert True | NA is True
|
||||
assert NA | False is NA
|
||||
assert False | NA is NA
|
||||
assert NA | NA is NA
|
||||
|
||||
msg = "unsupported operand type"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
NA | 5
|
||||
|
||||
|
||||
def test_logical_xor():
|
||||
|
||||
assert NA ^ True is NA
|
||||
assert True ^ NA is NA
|
||||
assert NA ^ False is NA
|
||||
assert False ^ NA is NA
|
||||
assert NA ^ NA is NA
|
||||
|
||||
msg = "unsupported operand type"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
NA ^ 5
|
||||
|
||||
|
||||
def test_logical_not():
|
||||
assert ~NA is NA
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shape", [(3,), (3, 3), (1, 2, 3)])
|
||||
def test_arithmetic_ndarray(shape, all_arithmetic_functions):
|
||||
op = all_arithmetic_functions
|
||||
a = np.zeros(shape)
|
||||
if op.__name__ == "pow":
|
||||
a += 5
|
||||
result = op(NA, a)
|
||||
expected = np.full(a.shape, NA, dtype=object)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
|
||||
def test_is_scalar():
|
||||
assert is_scalar(NA) is True
|
||||
|
||||
|
||||
def test_isna():
|
||||
assert pd.isna(NA) is True
|
||||
assert pd.notna(NA) is False
|
||||
|
||||
|
||||
def test_series_isna():
|
||||
s = pd.Series([1, NA], dtype=object)
|
||||
expected = pd.Series([False, True])
|
||||
tm.assert_series_equal(s.isna(), expected)
|
||||
|
||||
|
||||
def test_ufunc():
|
||||
assert np.log(NA) is NA
|
||||
assert np.add(NA, 1) is NA
|
||||
result = np.divmod(NA, 1)
|
||||
assert result[0] is NA and result[1] is NA
|
||||
|
||||
result = np.frexp(NA)
|
||||
assert result[0] is NA and result[1] is NA
|
||||
|
||||
|
||||
def test_ufunc_raises():
|
||||
msg = "ufunc method 'at'"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
np.log.at(NA, 0)
|
||||
|
||||
|
||||
def test_binary_input_not_dunder():
|
||||
a = np.array([1, 2, 3])
|
||||
expected = np.array([NA, NA, NA], dtype=object)
|
||||
result = np.logaddexp(a, NA)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = np.logaddexp(NA, a)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
# all NA, multiple inputs
|
||||
assert np.logaddexp(NA, NA) is NA
|
||||
|
||||
result = np.modf(NA, NA)
|
||||
assert len(result) == 2
|
||||
assert all(x is NA for x in result)
|
||||
|
||||
|
||||
def test_divmod_ufunc():
|
||||
# binary in, binary out.
|
||||
a = np.array([1, 2, 3])
|
||||
expected = np.array([NA, NA, NA], dtype=object)
|
||||
|
||||
result = np.divmod(a, NA)
|
||||
assert isinstance(result, tuple)
|
||||
for arr in result:
|
||||
tm.assert_numpy_array_equal(arr, expected)
|
||||
tm.assert_numpy_array_equal(arr, expected)
|
||||
|
||||
result = np.divmod(NA, a)
|
||||
for arr in result:
|
||||
tm.assert_numpy_array_equal(arr, expected)
|
||||
tm.assert_numpy_array_equal(arr, expected)
|
||||
|
||||
|
||||
def test_integer_hash_collision_dict():
|
||||
# GH 30013
|
||||
result = {NA: "foo", hash(NA): "bar"}
|
||||
|
||||
assert result[NA] == "foo"
|
||||
assert result[hash(NA)] == "bar"
|
||||
|
||||
|
||||
def test_integer_hash_collision_set():
|
||||
# GH 30013
|
||||
result = {NA, hash(NA)}
|
||||
|
||||
assert len(result) == 2
|
||||
assert NA in result
|
||||
assert hash(NA) in result
|
||||
|
||||
|
||||
def test_pickle_roundtrip():
|
||||
# https://github.com/pandas-dev/pandas/issues/31847
|
||||
result = pickle.loads(pickle.dumps(NA))
|
||||
assert result is NA
|
||||
|
||||
|
||||
def test_pickle_roundtrip_pandas():
|
||||
result = tm.round_trip_pickle(NA)
|
||||
assert result is NA
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"values, dtype", [([1, 2, NA], "Int64"), (["A", "B", NA], "string")]
|
||||
)
|
||||
@pytest.mark.parametrize("as_frame", [True, False])
|
||||
def test_pickle_roundtrip_containers(as_frame, values, dtype):
|
||||
s = pd.Series(pd.array(values, dtype=dtype))
|
||||
if as_frame:
|
||||
s = s.to_frame(name="A")
|
||||
result = tm.round_trip_pickle(s)
|
||||
tm.assert_equal(result, s)
|
||||
@@ -0,0 +1,725 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
import operator
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas._libs.tslibs import iNaT
|
||||
|
||||
from pandas.core.dtypes.common import is_datetime64_any_dtype
|
||||
|
||||
from pandas import (
|
||||
DatetimeIndex,
|
||||
DatetimeTZDtype,
|
||||
Index,
|
||||
NaT,
|
||||
Period,
|
||||
Series,
|
||||
Timedelta,
|
||||
TimedeltaIndex,
|
||||
Timestamp,
|
||||
isna,
|
||||
offsets,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
from pandas.core.arrays import (
|
||||
DatetimeArray,
|
||||
PeriodArray,
|
||||
TimedeltaArray,
|
||||
)
|
||||
from pandas.core.ops import roperator
|
||||
|
||||
|
||||
@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
|
||||
if field in ["week", "weekofyear"]:
|
||||
# GH#33595 Deprecate week and weekofyear
|
||||
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
|
||||
if field in ["week", "weekofyear"]:
|
||||
# GH#33595 Deprecate week and weekofyear
|
||||
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"])
|
||||
def test_identity(klass, value):
|
||||
assert klass(value) is NaT
|
||||
|
||||
|
||||
@pytest.mark.parametrize("klass", [Timestamp, Timedelta, Period])
|
||||
@pytest.mark.parametrize("value", ["", "nat", "NAT", None, np.nan])
|
||||
def test_equality(klass, value):
|
||||
if klass is Period and value == "":
|
||||
pytest.skip("Period cannot parse empty string")
|
||||
|
||||
assert klass(value).value == iNaT
|
||||
|
||||
|
||||
@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, ["freqstr", "normalize", "to_julian_date", "to_period", "tz"]),
|
||||
(
|
||||
Timedelta,
|
||||
[
|
||||
"components",
|
||||
"delta",
|
||||
"is_populated",
|
||||
"resolution_string",
|
||||
"to_pytimedelta",
|
||||
"to_timedelta64",
|
||||
"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,
|
||||
[
|
||||
"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)
|
||||
),
|
||||
)
|
||||
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__
|
||||
|
||||
# Ignore differences with Timestamp.isoformat() as they're intentional
|
||||
if klass == Timestamp and method == "isoformat":
|
||||
return
|
||||
|
||||
if method == "to_numpy":
|
||||
# GH#44460 can return either dt64 or td64 depending on dtype,
|
||||
# different docstring is intentional
|
||||
return
|
||||
|
||||
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"]),
|
||||
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 is_datetime64_any_dtype(value.dtype) and "plus" in op_name:
|
||||
expected = DatetimeIndex(exp_data, tz=value.tz, name=exp_name)
|
||||
else:
|
||||
expected = TimedeltaIndex(exp_data, name=exp_name)
|
||||
|
||||
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__ == datetime.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(
|
||||
reason="td64 doesn't return NotImplemented, see numpy#17017"
|
||||
),
|
||||
),
|
||||
Timestamp(0),
|
||||
Timestamp(0).to_pydatetime(),
|
||||
pytest.param(
|
||||
Timestamp(0).to_datetime64(),
|
||||
marks=pytest.mark.xfail(
|
||||
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()
|
||||
|
||||
for left, right in [(NaT, dt), (dt, NaT)]:
|
||||
assert not left == right
|
||||
assert left != right
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left < right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left <= right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left > right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left >= right
|
||||
|
||||
# Once the deprecation is enforced, the following assertions
|
||||
# can be enabled:
|
||||
# assert not left == right
|
||||
# assert left != right
|
||||
#
|
||||
# with pytest.raises(TypeError):
|
||||
# left < right
|
||||
# with pytest.raises(TypeError):
|
||||
# left <= right
|
||||
# with pytest.raises(TypeError):
|
||||
# left > right
|
||||
# with pytest.raises(TypeError):
|
||||
# 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
|
||||
|
||||
|
||||
def test_freq_deprecated():
|
||||
with tm.assert_produces_warning(FutureWarning, match="deprecated"):
|
||||
NaT.freq
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,379 @@
|
||||
from datetime import timedelta
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs import OutOfBoundsTimedelta
|
||||
|
||||
from pandas import (
|
||||
Timedelta,
|
||||
offsets,
|
||||
to_timedelta,
|
||||
)
|
||||
|
||||
|
||||
def test_construction():
|
||||
expected = np.timedelta64(10, "D").astype("m8[ns]").view("i8")
|
||||
assert Timedelta(10, unit="d").value == expected
|
||||
assert Timedelta(10.0, unit="d").value == expected
|
||||
assert Timedelta("10 days").value == expected
|
||||
assert Timedelta(days=10).value == expected
|
||||
assert Timedelta(days=10.0).value == expected
|
||||
|
||||
expected += np.timedelta64(10, "s").astype("m8[ns]").view("i8")
|
||||
assert Timedelta("10 days 00:00:10").value == expected
|
||||
assert Timedelta(days=10, seconds=10).value == expected
|
||||
assert Timedelta(days=10, milliseconds=10 * 1000).value == expected
|
||||
assert Timedelta(days=10, microseconds=10 * 1000 * 1000).value == expected
|
||||
|
||||
# rounding cases
|
||||
assert Timedelta(82739999850000).value == 82739999850000
|
||||
assert "0 days 22:58:59.999850" in str(Timedelta(82739999850000))
|
||||
assert Timedelta(123072001000000).value == 123072001000000
|
||||
assert "1 days 10:11:12.001" in str(Timedelta(123072001000000))
|
||||
|
||||
# string conversion with/without leading zero
|
||||
# GH#9570
|
||||
assert Timedelta("0:00:00") == timedelta(hours=0)
|
||||
assert Timedelta("00:00:00") == timedelta(hours=0)
|
||||
assert Timedelta("-1:00:00") == -timedelta(hours=1)
|
||||
assert Timedelta("-01:00:00") == -timedelta(hours=1)
|
||||
|
||||
# more strings & abbrevs
|
||||
# GH#8190
|
||||
assert Timedelta("1 h") == timedelta(hours=1)
|
||||
assert Timedelta("1 hour") == timedelta(hours=1)
|
||||
assert Timedelta("1 hr") == timedelta(hours=1)
|
||||
assert Timedelta("1 hours") == timedelta(hours=1)
|
||||
assert Timedelta("-1 hours") == -timedelta(hours=1)
|
||||
assert Timedelta("1 m") == timedelta(minutes=1)
|
||||
assert Timedelta("1.5 m") == timedelta(seconds=90)
|
||||
assert Timedelta("1 minute") == timedelta(minutes=1)
|
||||
assert Timedelta("1 minutes") == timedelta(minutes=1)
|
||||
assert Timedelta("1 s") == timedelta(seconds=1)
|
||||
assert Timedelta("1 second") == timedelta(seconds=1)
|
||||
assert Timedelta("1 seconds") == timedelta(seconds=1)
|
||||
assert Timedelta("1 ms") == timedelta(milliseconds=1)
|
||||
assert Timedelta("1 milli") == timedelta(milliseconds=1)
|
||||
assert Timedelta("1 millisecond") == timedelta(milliseconds=1)
|
||||
assert Timedelta("1 us") == timedelta(microseconds=1)
|
||||
assert Timedelta("1 µs") == timedelta(microseconds=1)
|
||||
assert Timedelta("1 micros") == timedelta(microseconds=1)
|
||||
assert Timedelta("1 microsecond") == timedelta(microseconds=1)
|
||||
assert Timedelta("1.5 microsecond") == Timedelta("00:00:00.000001500")
|
||||
assert Timedelta("1 ns") == Timedelta("00:00:00.000000001")
|
||||
assert Timedelta("1 nano") == Timedelta("00:00:00.000000001")
|
||||
assert Timedelta("1 nanosecond") == Timedelta("00:00:00.000000001")
|
||||
|
||||
# combos
|
||||
assert Timedelta("10 days 1 hour") == timedelta(days=10, hours=1)
|
||||
assert Timedelta("10 days 1 h") == timedelta(days=10, hours=1)
|
||||
assert Timedelta("10 days 1 h 1m 1s") == timedelta(
|
||||
days=10, hours=1, minutes=1, seconds=1
|
||||
)
|
||||
assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
|
||||
days=10, hours=1, minutes=1, seconds=1
|
||||
)
|
||||
assert Timedelta("-10 days 1 h 1m 1s") == -timedelta(
|
||||
days=10, hours=1, minutes=1, seconds=1
|
||||
)
|
||||
assert Timedelta("-10 days 1 h 1m 1s 3us") == -timedelta(
|
||||
days=10, hours=1, minutes=1, seconds=1, microseconds=3
|
||||
)
|
||||
assert Timedelta("-10 days 1 h 1.5m 1s 3us") == -timedelta(
|
||||
days=10, hours=1, minutes=1, seconds=31, microseconds=3
|
||||
)
|
||||
|
||||
# Currently invalid as it has a - on the hh:mm:dd part
|
||||
# (only allowed on the days)
|
||||
msg = "only leading negative signs are allowed"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("-10 days -1 h 1.5m 1s 3us")
|
||||
|
||||
# only leading neg signs are allowed
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("10 days -1 h 1.5m 1s 3us")
|
||||
|
||||
# no units specified
|
||||
msg = "no units specified"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("3.1415")
|
||||
|
||||
# invalid construction
|
||||
msg = "cannot construct a Timedelta"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta()
|
||||
|
||||
msg = "unit abbreviation w/o a number"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("foo")
|
||||
|
||||
msg = (
|
||||
"cannot construct a Timedelta from "
|
||||
"the passed arguments, allowed keywords are "
|
||||
)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta(day=10)
|
||||
|
||||
# floats
|
||||
expected = np.timedelta64(10, "s").astype("m8[ns]").view("i8") + np.timedelta64(
|
||||
500, "ms"
|
||||
).astype("m8[ns]").view("i8")
|
||||
assert Timedelta(10.5, unit="s").value == expected
|
||||
|
||||
# offset
|
||||
assert to_timedelta(offsets.Hour(2)) == Timedelta(hours=2)
|
||||
assert Timedelta(offsets.Hour(2)) == Timedelta(hours=2)
|
||||
assert Timedelta(offsets.Second(2)) == Timedelta(seconds=2)
|
||||
|
||||
# GH#11995: unicode
|
||||
expected = Timedelta("1H")
|
||||
result = Timedelta("1H")
|
||||
assert result == expected
|
||||
assert to_timedelta(offsets.Hour(2)) == Timedelta("0 days, 02:00:00")
|
||||
|
||||
msg = "unit abbreviation w/o a number"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("foo bar")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"item",
|
||||
list(
|
||||
{
|
||||
"days": "D",
|
||||
"seconds": "s",
|
||||
"microseconds": "us",
|
||||
"milliseconds": "ms",
|
||||
"minutes": "m",
|
||||
"hours": "h",
|
||||
"weeks": "W",
|
||||
}.items()
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"npdtype", [np.int64, np.int32, np.int16, np.float64, np.float32, np.float16]
|
||||
)
|
||||
def test_td_construction_with_np_dtypes(npdtype, item):
|
||||
# GH#8757: test construction with np dtypes
|
||||
pykwarg, npkwarg = item
|
||||
expected = np.timedelta64(1, npkwarg).astype("m8[ns]").view("i8")
|
||||
assert Timedelta(**{pykwarg: npdtype(1)}).value == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val",
|
||||
[
|
||||
"1s",
|
||||
"-1s",
|
||||
"1us",
|
||||
"-1us",
|
||||
"1 day",
|
||||
"-1 day",
|
||||
"-23:59:59.999999",
|
||||
"-1 days +23:59:59.999999",
|
||||
"-1ns",
|
||||
"1ns",
|
||||
"-23:59:59.999999999",
|
||||
],
|
||||
)
|
||||
def test_td_from_repr_roundtrip(val):
|
||||
# round-trip both for string and value
|
||||
td = Timedelta(val)
|
||||
assert Timedelta(td.value) == td
|
||||
|
||||
assert Timedelta(str(td)) == td
|
||||
assert Timedelta(td._repr_base(format="all")) == td
|
||||
assert Timedelta(td._repr_base()) == td
|
||||
|
||||
|
||||
def test_overflow_on_construction():
|
||||
msg = "int too (large|big) to convert"
|
||||
|
||||
# GH#3374
|
||||
value = Timedelta("1day").value * 20169940
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timedelta(value)
|
||||
|
||||
# xref GH#17637
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timedelta(7 * 19999, unit="D")
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(timedelta(days=13 * 19999))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val, unit, name",
|
||||
[
|
||||
(3508, "M", " months"),
|
||||
(15251, "W", " weeks"), # 1
|
||||
(106752, "D", " days"), # change from previous:
|
||||
(2562048, "h", " hours"), # 0 hours
|
||||
(153722868, "m", " minutes"), # 13 minutes
|
||||
(9223372037, "s", " seconds"), # 44 seconds
|
||||
],
|
||||
)
|
||||
def test_construction_out_of_bounds_td64(val, unit, name):
|
||||
# TODO: parametrize over units just above/below the implementation bounds
|
||||
# once GH#38964 is resolved
|
||||
|
||||
# Timedelta.max is just under 106752 days
|
||||
td64 = np.timedelta64(val, unit)
|
||||
assert td64.astype("m8[ns]").view("i8") < 0 # i.e. naive astype will be wrong
|
||||
|
||||
msg = str(val) + name
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(td64)
|
||||
|
||||
# But just back in bounds and we are OK
|
||||
assert Timedelta(td64 - 1) == td64 - 1
|
||||
|
||||
td64 *= -1
|
||||
assert td64.astype("m8[ns]").view("i8") > 0 # i.e. naive astype will be wrong
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="-" + msg):
|
||||
Timedelta(td64)
|
||||
|
||||
# But just back in bounds and we are OK
|
||||
assert Timedelta(td64 + 1) == td64 + 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fmt,exp",
|
||||
[
|
||||
(
|
||||
"P6DT0H50M3.010010012S",
|
||||
Timedelta(
|
||||
days=6,
|
||||
minutes=50,
|
||||
seconds=3,
|
||||
milliseconds=10,
|
||||
microseconds=10,
|
||||
nanoseconds=12,
|
||||
),
|
||||
),
|
||||
(
|
||||
"P-6DT0H50M3.010010012S",
|
||||
Timedelta(
|
||||
days=-6,
|
||||
minutes=50,
|
||||
seconds=3,
|
||||
milliseconds=10,
|
||||
microseconds=10,
|
||||
nanoseconds=12,
|
||||
),
|
||||
),
|
||||
("P4DT12H30M5S", Timedelta(days=4, hours=12, minutes=30, seconds=5)),
|
||||
("P0DT0H0M0.000000123S", Timedelta(nanoseconds=123)),
|
||||
("P0DT0H0M0.00001S", Timedelta(microseconds=10)),
|
||||
("P0DT0H0M0.001S", Timedelta(milliseconds=1)),
|
||||
("P0DT0H1M0S", Timedelta(minutes=1)),
|
||||
("P1DT25H61M61S", Timedelta(days=1, hours=25, minutes=61, seconds=61)),
|
||||
("PT1S", Timedelta(seconds=1)),
|
||||
("PT0S", Timedelta(seconds=0)),
|
||||
("P1WT0S", Timedelta(days=7, seconds=0)),
|
||||
("P1D", Timedelta(days=1)),
|
||||
("P1DT1H", Timedelta(days=1, hours=1)),
|
||||
("P1W", Timedelta(days=7)),
|
||||
("PT300S", Timedelta(seconds=300)),
|
||||
("P1DT0H0M00000000000S", Timedelta(days=1)),
|
||||
("PT-6H3M", Timedelta(hours=-6, minutes=3)),
|
||||
("-PT6H3M", Timedelta(hours=-6, minutes=-3)),
|
||||
("-PT-6H+3M", Timedelta(hours=6, minutes=-3)),
|
||||
],
|
||||
)
|
||||
def test_iso_constructor(fmt, exp):
|
||||
assert Timedelta(fmt) == exp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"fmt",
|
||||
[
|
||||
"PPPPPPPPPPPP",
|
||||
"PDTHMS",
|
||||
"P0DT999H999M999S",
|
||||
"P1DT0H0M0.0000000000000S",
|
||||
"P1DT0H0M0.S",
|
||||
"P",
|
||||
"-P",
|
||||
],
|
||||
)
|
||||
def test_iso_constructor_raises(fmt):
|
||||
msg = f"Invalid ISO 8601 Duration format - {fmt}"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta(fmt)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constructed_td, conversion",
|
||||
[
|
||||
(Timedelta(nanoseconds=100), "100ns"),
|
||||
(
|
||||
Timedelta(
|
||||
days=1,
|
||||
hours=1,
|
||||
minutes=1,
|
||||
weeks=1,
|
||||
seconds=1,
|
||||
milliseconds=1,
|
||||
microseconds=1,
|
||||
nanoseconds=1,
|
||||
),
|
||||
694861001001001,
|
||||
),
|
||||
(Timedelta(microseconds=1) + Timedelta(nanoseconds=1), "1us1ns"),
|
||||
(Timedelta(microseconds=1) - Timedelta(nanoseconds=1), "999ns"),
|
||||
(Timedelta(microseconds=1) + 5 * Timedelta(nanoseconds=-2), "990ns"),
|
||||
],
|
||||
)
|
||||
def test_td_constructor_on_nanoseconds(constructed_td, conversion):
|
||||
# GH#9273
|
||||
assert constructed_td == Timedelta(conversion)
|
||||
|
||||
|
||||
def test_td_constructor_value_error():
|
||||
msg = "Invalid type <class 'str'>. Must be int or float."
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timedelta(nanoseconds="abc")
|
||||
|
||||
|
||||
def test_timedelta_constructor_identity():
|
||||
# Test for #30543
|
||||
expected = Timedelta(np.timedelta64(1, "s"))
|
||||
result = Timedelta(expected)
|
||||
assert result is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constructor, value, unit, expectation",
|
||||
[
|
||||
(Timedelta, "10s", "ms", (ValueError, "unit must not be specified")),
|
||||
(to_timedelta, "10s", "ms", (ValueError, "unit must not be specified")),
|
||||
(to_timedelta, ["1", 2, 3], "s", (ValueError, "unit must not be specified")),
|
||||
],
|
||||
)
|
||||
def test_string_with_unit(constructor, value, unit, expectation):
|
||||
exp, match = expectation
|
||||
with pytest.raises(exp, match=match):
|
||||
_ = constructor(value, unit=unit)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
"".join(elements)
|
||||
for repetition in (1, 2)
|
||||
for elements in product("+-, ", repeat=repetition)
|
||||
],
|
||||
)
|
||||
def test_string_without_numbers(value):
|
||||
# GH39710 Timedelta input string with only symbols and no digits raises an error
|
||||
msg = (
|
||||
"symbols w/o a number"
|
||||
if value != "--"
|
||||
else "only leading negative signs are allowed"
|
||||
)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta(value)
|
||||
@@ -0,0 +1,44 @@
|
||||
import pytest
|
||||
|
||||
from pandas import Timedelta
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"td, expected_repr",
|
||||
[
|
||||
(Timedelta(10, unit="d"), "Timedelta('10 days 00:00:00')"),
|
||||
(Timedelta(10, unit="s"), "Timedelta('0 days 00:00:10')"),
|
||||
(Timedelta(10, unit="ms"), "Timedelta('0 days 00:00:00.010000')"),
|
||||
(Timedelta(-10, unit="ms"), "Timedelta('-1 days +23:59:59.990000')"),
|
||||
],
|
||||
)
|
||||
def test_repr(td, expected_repr):
|
||||
assert repr(td) == expected_repr
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"td, expected_iso",
|
||||
[
|
||||
(
|
||||
Timedelta(
|
||||
days=6,
|
||||
minutes=50,
|
||||
seconds=3,
|
||||
milliseconds=10,
|
||||
microseconds=10,
|
||||
nanoseconds=12,
|
||||
),
|
||||
"P6DT0H50M3.010010012S",
|
||||
),
|
||||
(Timedelta(days=4, hours=12, minutes=30, seconds=5), "P4DT12H30M5S"),
|
||||
(Timedelta(nanoseconds=123), "P0DT0H0M0.000000123S"),
|
||||
# trim nano
|
||||
(Timedelta(microseconds=10), "P0DT0H0M0.00001S"),
|
||||
# trim micro
|
||||
(Timedelta(milliseconds=1), "P0DT0H0M0.001S"),
|
||||
# don't strip every 0
|
||||
(Timedelta(minutes=1), "P0DT0H1M0S"),
|
||||
],
|
||||
)
|
||||
def test_isoformat(td, expected_iso):
|
||||
assert td.isoformat() == expected_iso
|
||||
@@ -0,0 +1,661 @@
|
||||
""" test the scalar Timedelta """
|
||||
from datetime import timedelta
|
||||
|
||||
from hypothesis import (
|
||||
given,
|
||||
strategies as st,
|
||||
)
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs import lib
|
||||
from pandas._libs.tslibs import (
|
||||
NaT,
|
||||
iNaT,
|
||||
)
|
||||
|
||||
import pandas as pd
|
||||
from pandas import (
|
||||
Timedelta,
|
||||
TimedeltaIndex,
|
||||
offsets,
|
||||
to_timedelta,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimedeltaUnaryOps:
|
||||
def test_invert(self):
|
||||
td = Timedelta(10, unit="d")
|
||||
|
||||
msg = "bad operand type for unary ~"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
~td
|
||||
|
||||
# check this matches pytimedelta and timedelta64
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
~(td.to_pytimedelta())
|
||||
|
||||
umsg = "ufunc 'invert' not supported for the input types"
|
||||
with pytest.raises(TypeError, match=umsg):
|
||||
~(td.to_timedelta64())
|
||||
|
||||
def test_unary_ops(self):
|
||||
td = Timedelta(10, unit="d")
|
||||
|
||||
# __neg__, __pos__
|
||||
assert -td == Timedelta(-10, unit="d")
|
||||
assert -td == Timedelta("-10d")
|
||||
assert +td == Timedelta(10, unit="d")
|
||||
|
||||
# __abs__, __abs__(__neg__)
|
||||
assert abs(td) == td
|
||||
assert abs(-td) == td
|
||||
assert abs(-td) == Timedelta("10d")
|
||||
|
||||
|
||||
class TestTimedeltas:
|
||||
@pytest.mark.parametrize(
|
||||
"unit, value, expected",
|
||||
[
|
||||
("us", 9.999, 9999),
|
||||
("ms", 9.999999, 9999999),
|
||||
("s", 9.999999999, 9999999999),
|
||||
],
|
||||
)
|
||||
def test_rounding_on_int_unit_construction(self, unit, value, expected):
|
||||
# GH 12690
|
||||
result = Timedelta(value, unit=unit)
|
||||
assert result.value == expected
|
||||
result = Timedelta(str(value) + unit)
|
||||
assert result.value == expected
|
||||
|
||||
def test_total_seconds_scalar(self):
|
||||
# see gh-10939
|
||||
rng = Timedelta("1 days, 10:11:12.100123456")
|
||||
expt = 1 * 86400 + 10 * 3600 + 11 * 60 + 12 + 100123456.0 / 1e9
|
||||
tm.assert_almost_equal(rng.total_seconds(), expt)
|
||||
|
||||
rng = Timedelta(np.nan)
|
||||
assert np.isnan(rng.total_seconds())
|
||||
|
||||
def test_conversion(self):
|
||||
|
||||
for td in [Timedelta(10, unit="d"), Timedelta("1 days, 10:11:12.012345")]:
|
||||
pydt = td.to_pytimedelta()
|
||||
assert td == Timedelta(pydt)
|
||||
assert td == pydt
|
||||
assert isinstance(pydt, timedelta) and not isinstance(pydt, Timedelta)
|
||||
|
||||
assert td == np.timedelta64(td.value, "ns")
|
||||
td64 = td.to_timedelta64()
|
||||
|
||||
assert td64 == np.timedelta64(td.value, "ns")
|
||||
assert td == td64
|
||||
|
||||
assert isinstance(td64, np.timedelta64)
|
||||
|
||||
# this is NOT equal and cannot be roundtripped (because of the nanos)
|
||||
td = Timedelta("1 days, 10:11:12.012345678")
|
||||
assert td != td.to_pytimedelta()
|
||||
|
||||
def test_fields(self):
|
||||
def check(value):
|
||||
# that we are int
|
||||
assert isinstance(value, int)
|
||||
|
||||
# compat to datetime.timedelta
|
||||
rng = to_timedelta("1 days, 10:11:12")
|
||||
assert rng.days == 1
|
||||
assert rng.seconds == 10 * 3600 + 11 * 60 + 12
|
||||
assert rng.microseconds == 0
|
||||
assert rng.nanoseconds == 0
|
||||
|
||||
msg = "'Timedelta' object has no attribute '{}'"
|
||||
with pytest.raises(AttributeError, match=msg.format("hours")):
|
||||
rng.hours
|
||||
with pytest.raises(AttributeError, match=msg.format("minutes")):
|
||||
rng.minutes
|
||||
with pytest.raises(AttributeError, match=msg.format("milliseconds")):
|
||||
rng.milliseconds
|
||||
|
||||
# GH 10050
|
||||
check(rng.days)
|
||||
check(rng.seconds)
|
||||
check(rng.microseconds)
|
||||
check(rng.nanoseconds)
|
||||
|
||||
td = Timedelta("-1 days, 10:11:12")
|
||||
assert abs(td) == Timedelta("13:48:48")
|
||||
assert str(td) == "-1 days +10:11:12"
|
||||
assert -td == Timedelta("0 days 13:48:48")
|
||||
assert -Timedelta("-1 days, 10:11:12").value == 49728000000000
|
||||
assert Timedelta("-1 days, 10:11:12").value == -49728000000000
|
||||
|
||||
rng = to_timedelta("-1 days, 10:11:12.100123456")
|
||||
assert rng.days == -1
|
||||
assert rng.seconds == 10 * 3600 + 11 * 60 + 12
|
||||
assert rng.microseconds == 100 * 1000 + 123
|
||||
assert rng.nanoseconds == 456
|
||||
msg = "'Timedelta' object has no attribute '{}'"
|
||||
with pytest.raises(AttributeError, match=msg.format("hours")):
|
||||
rng.hours
|
||||
with pytest.raises(AttributeError, match=msg.format("minutes")):
|
||||
rng.minutes
|
||||
with pytest.raises(AttributeError, match=msg.format("milliseconds")):
|
||||
rng.milliseconds
|
||||
|
||||
# components
|
||||
tup = to_timedelta(-1, "us").components
|
||||
assert tup.days == -1
|
||||
assert tup.hours == 23
|
||||
assert tup.minutes == 59
|
||||
assert tup.seconds == 59
|
||||
assert tup.milliseconds == 999
|
||||
assert tup.microseconds == 999
|
||||
assert tup.nanoseconds == 0
|
||||
|
||||
# GH 10050
|
||||
check(tup.days)
|
||||
check(tup.hours)
|
||||
check(tup.minutes)
|
||||
check(tup.seconds)
|
||||
check(tup.milliseconds)
|
||||
check(tup.microseconds)
|
||||
check(tup.nanoseconds)
|
||||
|
||||
tup = Timedelta("-1 days 1 us").components
|
||||
assert tup.days == -2
|
||||
assert tup.hours == 23
|
||||
assert tup.minutes == 59
|
||||
assert tup.seconds == 59
|
||||
assert tup.milliseconds == 999
|
||||
assert tup.microseconds == 999
|
||||
assert tup.nanoseconds == 0
|
||||
|
||||
def test_iso_conversion(self):
|
||||
# GH #21877
|
||||
expected = Timedelta(1, unit="s")
|
||||
assert to_timedelta("P0DT0H0M1S") == expected
|
||||
|
||||
def test_nat_converters(self):
|
||||
result = to_timedelta("nat").to_numpy()
|
||||
assert result.dtype.kind == "M"
|
||||
assert result.astype("int64") == iNaT
|
||||
|
||||
result = to_timedelta("nan").to_numpy()
|
||||
assert result.dtype.kind == "M"
|
||||
assert result.astype("int64") == iNaT
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"unit, np_unit",
|
||||
[(value, "W") for value in ["W", "w"]]
|
||||
+ [(value, "D") for value in ["D", "d", "days", "day", "Days", "Day"]]
|
||||
+ [
|
||||
(value, "m")
|
||||
for value in [
|
||||
"m",
|
||||
"minute",
|
||||
"min",
|
||||
"minutes",
|
||||
"t",
|
||||
"Minute",
|
||||
"Min",
|
||||
"Minutes",
|
||||
"T",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "s")
|
||||
for value in [
|
||||
"s",
|
||||
"seconds",
|
||||
"sec",
|
||||
"second",
|
||||
"S",
|
||||
"Seconds",
|
||||
"Sec",
|
||||
"Second",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "ms")
|
||||
for value in [
|
||||
"ms",
|
||||
"milliseconds",
|
||||
"millisecond",
|
||||
"milli",
|
||||
"millis",
|
||||
"l",
|
||||
"MS",
|
||||
"Milliseconds",
|
||||
"Millisecond",
|
||||
"Milli",
|
||||
"Millis",
|
||||
"L",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "us")
|
||||
for value in [
|
||||
"us",
|
||||
"microseconds",
|
||||
"microsecond",
|
||||
"micro",
|
||||
"micros",
|
||||
"u",
|
||||
"US",
|
||||
"Microseconds",
|
||||
"Microsecond",
|
||||
"Micro",
|
||||
"Micros",
|
||||
"U",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "ns")
|
||||
for value in [
|
||||
"ns",
|
||||
"nanoseconds",
|
||||
"nanosecond",
|
||||
"nano",
|
||||
"nanos",
|
||||
"n",
|
||||
"NS",
|
||||
"Nanoseconds",
|
||||
"Nanosecond",
|
||||
"Nano",
|
||||
"Nanos",
|
||||
"N",
|
||||
]
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("wrapper", [np.array, list, pd.Index])
|
||||
def test_unit_parser(self, unit, np_unit, wrapper):
|
||||
# validate all units, GH 6855, GH 21762
|
||||
# array-likes
|
||||
expected = TimedeltaIndex(
|
||||
[np.timedelta64(i, np_unit) for i in np.arange(5).tolist()]
|
||||
)
|
||||
result = to_timedelta(wrapper(range(5)), unit=unit)
|
||||
tm.assert_index_equal(result, expected)
|
||||
result = TimedeltaIndex(wrapper(range(5)), unit=unit)
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
str_repr = [f"{x}{unit}" for x in np.arange(5)]
|
||||
result = to_timedelta(wrapper(str_repr))
|
||||
tm.assert_index_equal(result, expected)
|
||||
result = to_timedelta(wrapper(str_repr))
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
# scalar
|
||||
expected = Timedelta(np.timedelta64(2, np_unit).astype("timedelta64[ns]"))
|
||||
result = to_timedelta(2, unit=unit)
|
||||
assert result == expected
|
||||
result = Timedelta(2, unit=unit)
|
||||
assert result == expected
|
||||
|
||||
result = to_timedelta(f"2{unit}")
|
||||
assert result == expected
|
||||
result = Timedelta(f"2{unit}")
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("unit", ["Y", "y", "M"])
|
||||
def test_unit_m_y_raises(self, unit):
|
||||
msg = "Units 'M', 'Y', and 'y' are no longer supported"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta(10, unit)
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
to_timedelta(10, unit)
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
to_timedelta([1, 2], unit)
|
||||
|
||||
def test_numeric_conversions(self):
|
||||
assert Timedelta(0) == np.timedelta64(0, "ns")
|
||||
assert Timedelta(10) == np.timedelta64(10, "ns")
|
||||
assert Timedelta(10, unit="ns") == np.timedelta64(10, "ns")
|
||||
|
||||
assert Timedelta(10, unit="us") == np.timedelta64(10, "us")
|
||||
assert Timedelta(10, unit="ms") == np.timedelta64(10, "ms")
|
||||
assert Timedelta(10, unit="s") == np.timedelta64(10, "s")
|
||||
assert Timedelta(10, unit="d") == np.timedelta64(10, "D")
|
||||
|
||||
def test_timedelta_conversions(self):
|
||||
assert Timedelta(timedelta(seconds=1)) == np.timedelta64(1, "s").astype(
|
||||
"m8[ns]"
|
||||
)
|
||||
assert Timedelta(timedelta(microseconds=1)) == np.timedelta64(1, "us").astype(
|
||||
"m8[ns]"
|
||||
)
|
||||
assert Timedelta(timedelta(days=1)) == np.timedelta64(1, "D").astype("m8[ns]")
|
||||
|
||||
def test_to_numpy_alias(self):
|
||||
# GH 24653: alias .to_numpy() for scalars
|
||||
td = Timedelta("10m7s")
|
||||
assert td.to_timedelta64() == td.to_numpy()
|
||||
|
||||
# GH#44460
|
||||
msg = "dtype and copy arguments are ignored"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
td.to_numpy("m8[s]")
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
td.to_numpy(copy=True)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"freq,s1,s2",
|
||||
[
|
||||
# This first case has s1, s2 being the same as t1,t2 below
|
||||
(
|
||||
"N",
|
||||
Timedelta("1 days 02:34:56.789123456"),
|
||||
Timedelta("-1 days 02:34:56.789123456"),
|
||||
),
|
||||
(
|
||||
"U",
|
||||
Timedelta("1 days 02:34:56.789123000"),
|
||||
Timedelta("-1 days 02:34:56.789123000"),
|
||||
),
|
||||
(
|
||||
"L",
|
||||
Timedelta("1 days 02:34:56.789000000"),
|
||||
Timedelta("-1 days 02:34:56.789000000"),
|
||||
),
|
||||
("S", Timedelta("1 days 02:34:57"), Timedelta("-1 days 02:34:57")),
|
||||
("2S", Timedelta("1 days 02:34:56"), Timedelta("-1 days 02:34:56")),
|
||||
("5S", Timedelta("1 days 02:34:55"), Timedelta("-1 days 02:34:55")),
|
||||
("T", Timedelta("1 days 02:35:00"), Timedelta("-1 days 02:35:00")),
|
||||
("12T", Timedelta("1 days 02:36:00"), Timedelta("-1 days 02:36:00")),
|
||||
("H", Timedelta("1 days 03:00:00"), Timedelta("-1 days 03:00:00")),
|
||||
("d", Timedelta("1 days"), Timedelta("-1 days")),
|
||||
],
|
||||
)
|
||||
def test_round(self, freq, s1, s2):
|
||||
|
||||
t1 = Timedelta("1 days 02:34:56.789123456")
|
||||
t2 = Timedelta("-1 days 02:34:56.789123456")
|
||||
|
||||
r1 = t1.round(freq)
|
||||
assert r1 == s1
|
||||
r2 = t2.round(freq)
|
||||
assert r2 == s2
|
||||
|
||||
def test_round_invalid(self):
|
||||
t1 = Timedelta("1 days 02:34:56.789123456")
|
||||
|
||||
for freq, msg in [
|
||||
("Y", "<YearEnd: month=12> is a non-fixed frequency"),
|
||||
("M", "<MonthEnd> is a non-fixed frequency"),
|
||||
("foobar", "Invalid frequency: foobar"),
|
||||
]:
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
t1.round(freq)
|
||||
|
||||
def test_round_implementation_bounds(self):
|
||||
# See also: analogous test for Timestamp
|
||||
# GH#38964
|
||||
result = Timedelta.min.ceil("s")
|
||||
expected = Timedelta.min + Timedelta(seconds=1) - Timedelta(145224193)
|
||||
assert result == expected
|
||||
|
||||
result = Timedelta.max.floor("s")
|
||||
expected = Timedelta.max - Timedelta(854775807)
|
||||
assert result == expected
|
||||
|
||||
with pytest.raises(OverflowError, match="value too large"):
|
||||
Timedelta.min.floor("s")
|
||||
|
||||
# the second message here shows up in windows builds
|
||||
msg = "|".join(
|
||||
["Python int too large to convert to C long", "int too big to convert"]
|
||||
)
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timedelta.max.ceil("s")
|
||||
|
||||
@given(val=st.integers(min_value=iNaT + 1, max_value=lib.i8max))
|
||||
@pytest.mark.parametrize(
|
||||
"method", [Timedelta.round, Timedelta.floor, Timedelta.ceil]
|
||||
)
|
||||
def test_round_sanity(self, val, method):
|
||||
val = np.int64(val)
|
||||
td = Timedelta(val)
|
||||
|
||||
assert method(td, "ns") == td
|
||||
|
||||
res = method(td, "us")
|
||||
nanos = 1000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
res = method(td, "ms")
|
||||
nanos = 1_000_000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
res = method(td, "s")
|
||||
nanos = 1_000_000_000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
res = method(td, "min")
|
||||
nanos = 60 * 1_000_000_000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
res = method(td, "h")
|
||||
nanos = 60 * 60 * 1_000_000_000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
res = method(td, "D")
|
||||
nanos = 24 * 60 * 60 * 1_000_000_000
|
||||
assert np.abs((res - td).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
|
||||
def test_contains(self):
|
||||
# Checking for any NaT-like objects
|
||||
# GH 13603
|
||||
td = to_timedelta(range(5), unit="d") + offsets.Hour(1)
|
||||
for v in [NaT, None, float("nan"), np.nan]:
|
||||
assert not (v in td)
|
||||
|
||||
td = to_timedelta([NaT])
|
||||
for v in [NaT, None, float("nan"), np.nan]:
|
||||
assert v in td
|
||||
|
||||
def test_identity(self):
|
||||
|
||||
td = Timedelta(10, unit="d")
|
||||
assert isinstance(td, Timedelta)
|
||||
assert isinstance(td, timedelta)
|
||||
|
||||
def test_short_format_converters(self):
|
||||
def conv(v):
|
||||
return v.astype("m8[ns]")
|
||||
|
||||
assert Timedelta("10") == np.timedelta64(10, "ns")
|
||||
assert Timedelta("10ns") == np.timedelta64(10, "ns")
|
||||
assert Timedelta("100") == np.timedelta64(100, "ns")
|
||||
assert Timedelta("100ns") == np.timedelta64(100, "ns")
|
||||
|
||||
assert Timedelta("1000") == np.timedelta64(1000, "ns")
|
||||
assert Timedelta("1000ns") == np.timedelta64(1000, "ns")
|
||||
assert Timedelta("1000NS") == np.timedelta64(1000, "ns")
|
||||
|
||||
assert Timedelta("10us") == np.timedelta64(10000, "ns")
|
||||
assert Timedelta("100us") == np.timedelta64(100000, "ns")
|
||||
assert Timedelta("1000us") == np.timedelta64(1000000, "ns")
|
||||
assert Timedelta("1000Us") == np.timedelta64(1000000, "ns")
|
||||
assert Timedelta("1000uS") == np.timedelta64(1000000, "ns")
|
||||
|
||||
assert Timedelta("1ms") == np.timedelta64(1000000, "ns")
|
||||
assert Timedelta("10ms") == np.timedelta64(10000000, "ns")
|
||||
assert Timedelta("100ms") == np.timedelta64(100000000, "ns")
|
||||
assert Timedelta("1000ms") == np.timedelta64(1000000000, "ns")
|
||||
|
||||
assert Timedelta("-1s") == -np.timedelta64(1000000000, "ns")
|
||||
assert Timedelta("1s") == np.timedelta64(1000000000, "ns")
|
||||
assert Timedelta("10s") == np.timedelta64(10000000000, "ns")
|
||||
assert Timedelta("100s") == np.timedelta64(100000000000, "ns")
|
||||
assert Timedelta("1000s") == np.timedelta64(1000000000000, "ns")
|
||||
|
||||
assert Timedelta("1d") == conv(np.timedelta64(1, "D"))
|
||||
assert Timedelta("-1d") == -conv(np.timedelta64(1, "D"))
|
||||
assert Timedelta("1D") == conv(np.timedelta64(1, "D"))
|
||||
assert Timedelta("10D") == conv(np.timedelta64(10, "D"))
|
||||
assert Timedelta("100D") == conv(np.timedelta64(100, "D"))
|
||||
assert Timedelta("1000D") == conv(np.timedelta64(1000, "D"))
|
||||
assert Timedelta("10000D") == conv(np.timedelta64(10000, "D"))
|
||||
|
||||
# space
|
||||
assert Timedelta(" 10000D ") == conv(np.timedelta64(10000, "D"))
|
||||
assert Timedelta(" - 10000D ") == -conv(np.timedelta64(10000, "D"))
|
||||
|
||||
# invalid
|
||||
msg = "invalid unit abbreviation"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("1foo")
|
||||
msg = "unit abbreviation w/o a number"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("foo")
|
||||
|
||||
def test_full_format_converters(self):
|
||||
def conv(v):
|
||||
return v.astype("m8[ns]")
|
||||
|
||||
d1 = np.timedelta64(1, "D")
|
||||
|
||||
assert Timedelta("1days") == conv(d1)
|
||||
assert Timedelta("1days,") == conv(d1)
|
||||
assert Timedelta("- 1days,") == -conv(d1)
|
||||
|
||||
assert Timedelta("00:00:01") == conv(np.timedelta64(1, "s"))
|
||||
assert Timedelta("06:00:01") == conv(np.timedelta64(6 * 3600 + 1, "s"))
|
||||
assert Timedelta("06:00:01.0") == conv(np.timedelta64(6 * 3600 + 1, "s"))
|
||||
assert Timedelta("06:00:01.01") == conv(
|
||||
np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
|
||||
)
|
||||
|
||||
assert Timedelta("- 1days, 00:00:01") == conv(-d1 + np.timedelta64(1, "s"))
|
||||
assert Timedelta("1days, 06:00:01") == conv(
|
||||
d1 + np.timedelta64(6 * 3600 + 1, "s")
|
||||
)
|
||||
assert Timedelta("1days, 06:00:01.01") == conv(
|
||||
d1 + np.timedelta64(1000 * (6 * 3600 + 1) + 10, "ms")
|
||||
)
|
||||
|
||||
# invalid
|
||||
msg = "have leftover units"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta("- 1days, 00")
|
||||
|
||||
def test_pickle(self):
|
||||
|
||||
v = Timedelta("1 days 10:11:12.0123456")
|
||||
v_p = tm.round_trip_pickle(v)
|
||||
assert v == v_p
|
||||
|
||||
def test_timedelta_hash_equality(self):
|
||||
# GH 11129
|
||||
v = Timedelta(1, "D")
|
||||
td = timedelta(days=1)
|
||||
assert hash(v) == hash(td)
|
||||
|
||||
d = {td: 2}
|
||||
assert d[v] == 2
|
||||
|
||||
tds = [Timedelta(seconds=1) + Timedelta(days=n) for n in range(20)]
|
||||
assert all(hash(td) == hash(td.to_pytimedelta()) for td in tds)
|
||||
|
||||
# python timedeltas drop ns resolution
|
||||
ns_td = Timedelta(1, "ns")
|
||||
assert hash(ns_td) != hash(ns_td.to_pytimedelta())
|
||||
|
||||
def test_implementation_limits(self):
|
||||
min_td = Timedelta(Timedelta.min)
|
||||
max_td = Timedelta(Timedelta.max)
|
||||
|
||||
# GH 12727
|
||||
# timedelta limits correspond to int64 boundaries
|
||||
assert min_td.value == iNaT + 1
|
||||
assert max_td.value == lib.i8max
|
||||
|
||||
# Beyond lower limit, a NAT before the Overflow
|
||||
assert (min_td - Timedelta(1, "ns")) is NaT
|
||||
|
||||
msg = "int too (large|big) to convert"
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
min_td - Timedelta(2, "ns")
|
||||
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
max_td + Timedelta(1, "ns")
|
||||
|
||||
# Same tests using the internal nanosecond values
|
||||
td = Timedelta(min_td.value - 1, "ns")
|
||||
assert td is NaT
|
||||
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timedelta(min_td.value - 2, "ns")
|
||||
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timedelta(max_td.value + 1, "ns")
|
||||
|
||||
def test_total_seconds_precision(self):
|
||||
# GH 19458
|
||||
assert Timedelta("30S").total_seconds() == 30.0
|
||||
assert Timedelta("0").total_seconds() == 0.0
|
||||
assert Timedelta("-2S").total_seconds() == -2.0
|
||||
assert Timedelta("5.324S").total_seconds() == 5.324
|
||||
assert (Timedelta("30S").total_seconds() - 30.0) < 1e-20
|
||||
assert (30.0 - Timedelta("30S").total_seconds()) < 1e-20
|
||||
|
||||
def test_resolution_string(self):
|
||||
assert Timedelta(days=1).resolution_string == "D"
|
||||
assert Timedelta(days=1, hours=6).resolution_string == "H"
|
||||
assert Timedelta(days=1, minutes=6).resolution_string == "T"
|
||||
assert Timedelta(days=1, seconds=6).resolution_string == "S"
|
||||
assert Timedelta(days=1, milliseconds=6).resolution_string == "L"
|
||||
assert Timedelta(days=1, microseconds=6).resolution_string == "U"
|
||||
assert Timedelta(days=1, nanoseconds=6).resolution_string == "N"
|
||||
|
||||
def test_resolution_deprecated(self):
|
||||
# GH#21344
|
||||
td = Timedelta(days=4, hours=3)
|
||||
result = td.resolution
|
||||
assert result == Timedelta(nanoseconds=1)
|
||||
|
||||
# Check that the attribute is available on the class, mirroring
|
||||
# the stdlib timedelta behavior
|
||||
result = Timedelta.resolution
|
||||
assert result == Timedelta(nanoseconds=1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, expected",
|
||||
[
|
||||
(Timedelta("10S"), True),
|
||||
(Timedelta("-10S"), True),
|
||||
(Timedelta(10, unit="ns"), True),
|
||||
(Timedelta(0, unit="ns"), False),
|
||||
(Timedelta(-10, unit="ns"), True),
|
||||
(Timedelta(None), True),
|
||||
(NaT, True),
|
||||
],
|
||||
)
|
||||
def test_truthiness(value, expected):
|
||||
# https://github.com/pandas-dev/pandas/issues/21484
|
||||
assert bool(value) is expected
|
||||
|
||||
|
||||
def test_timedelta_attribute_precision():
|
||||
# GH 31354
|
||||
td = Timedelta(1552211999999999872, unit="ns")
|
||||
result = td.days * 86400
|
||||
result += td.seconds
|
||||
result *= 1000000
|
||||
result += td.microseconds
|
||||
result *= 1000
|
||||
result += td.nanoseconds
|
||||
expected = td.value
|
||||
assert result == expected
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,321 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
timezone,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs import (
|
||||
OutOfBoundsDatetime,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
offsets,
|
||||
to_offset,
|
||||
)
|
||||
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampArithmetic:
|
||||
def test_overflow_offset(self):
|
||||
# no overflow expected
|
||||
|
||||
stamp = Timestamp("2000/1/1")
|
||||
offset_no_overflow = to_offset("D") * 100
|
||||
|
||||
expected = Timestamp("2000/04/10")
|
||||
assert stamp + offset_no_overflow == expected
|
||||
|
||||
assert offset_no_overflow + stamp == expected
|
||||
|
||||
expected = Timestamp("1999/09/23")
|
||||
assert stamp - offset_no_overflow == expected
|
||||
|
||||
def test_overflow_offset_raises(self):
|
||||
# xref https://github.com/statsmodels/statsmodels/issues/3374
|
||||
# ends up multiplying really large numbers which overflow
|
||||
|
||||
stamp = Timestamp("2017-01-13 00:00:00")
|
||||
offset_overflow = 20169940 * offsets.Day(1)
|
||||
msg = (
|
||||
"the add operation between "
|
||||
r"\<-?\d+ \* Days\> and \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} "
|
||||
"will overflow"
|
||||
)
|
||||
lmsg = "|".join(
|
||||
["Python int too large to convert to C long", "int too big to convert"]
|
||||
)
|
||||
|
||||
with pytest.raises(OverflowError, match=lmsg):
|
||||
stamp + offset_overflow
|
||||
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
offset_overflow + stamp
|
||||
|
||||
with pytest.raises(OverflowError, match=lmsg):
|
||||
stamp - offset_overflow
|
||||
|
||||
# xref https://github.com/pandas-dev/pandas/issues/14080
|
||||
# used to crash, so check for proper overflow exception
|
||||
|
||||
stamp = Timestamp("2000/1/1")
|
||||
offset_overflow = to_offset("D") * 100**5
|
||||
|
||||
with pytest.raises(OverflowError, match=lmsg):
|
||||
stamp + offset_overflow
|
||||
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
offset_overflow + stamp
|
||||
|
||||
with pytest.raises(OverflowError, match=lmsg):
|
||||
stamp - offset_overflow
|
||||
|
||||
def test_overflow_timestamp_raises(self):
|
||||
# https://github.com/pandas-dev/pandas/issues/31774
|
||||
msg = "Result is too large"
|
||||
a = Timestamp("2101-01-01 00:00:00")
|
||||
b = Timestamp("1688-01-01 00:00:00")
|
||||
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
a - b
|
||||
|
||||
# but we're OK for timestamp and datetime.datetime
|
||||
assert (a - b.to_pydatetime()) == (a.to_pydatetime() - b)
|
||||
|
||||
def test_delta_preserve_nanos(self):
|
||||
val = Timestamp(1337299200000000123)
|
||||
result = val + timedelta(1)
|
||||
assert result.nanosecond == val.nanosecond
|
||||
|
||||
def test_rsub_dtscalars(self, tz_naive_fixture):
|
||||
# In particular, check that datetime64 - Timestamp works GH#28286
|
||||
td = Timedelta(1235345642000)
|
||||
ts = Timestamp("2021-01-01", tz=tz_naive_fixture)
|
||||
other = ts + td
|
||||
|
||||
assert other - ts == td
|
||||
assert other.to_pydatetime() - ts == td
|
||||
if tz_naive_fixture is None:
|
||||
assert other.to_datetime64() - ts == td
|
||||
else:
|
||||
msg = "Cannot subtract tz-naive and tz-aware datetime-like objects"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
other.to_datetime64() - ts
|
||||
|
||||
def test_timestamp_sub_datetime(self):
|
||||
dt = datetime(2013, 10, 12)
|
||||
ts = Timestamp(datetime(2013, 10, 13))
|
||||
assert (ts - dt).days == 1
|
||||
assert (dt - ts).days == -1
|
||||
|
||||
def test_subtract_tzaware_datetime(self):
|
||||
t1 = Timestamp("2020-10-22T22:00:00+00:00")
|
||||
t2 = datetime(2020, 10, 22, 22, tzinfo=timezone.utc)
|
||||
|
||||
result = t1 - t2
|
||||
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result == Timedelta("0 days")
|
||||
|
||||
def test_subtract_timestamp_from_different_timezone(self):
|
||||
t1 = Timestamp("20130101").tz_localize("US/Eastern")
|
||||
t2 = Timestamp("20130101").tz_localize("CET")
|
||||
|
||||
result = t1 - t2
|
||||
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result == Timedelta("0 days 06:00:00")
|
||||
|
||||
def test_subtracting_involving_datetime_with_different_tz(self):
|
||||
t1 = datetime(2013, 1, 1, tzinfo=timezone(timedelta(hours=-5)))
|
||||
t2 = Timestamp("20130101").tz_localize("CET")
|
||||
|
||||
result = t1 - t2
|
||||
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result == Timedelta("0 days 06:00:00")
|
||||
|
||||
result = t2 - t1
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result == Timedelta("-1 days +18:00:00")
|
||||
|
||||
def test_subtracting_different_timezones(self, tz_aware_fixture):
|
||||
t_raw = Timestamp("20130101")
|
||||
t_UTC = t_raw.tz_localize("UTC")
|
||||
t_diff = t_UTC.tz_convert(tz_aware_fixture) + Timedelta("0 days 05:00:00")
|
||||
|
||||
result = t_diff - t_UTC
|
||||
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result == Timedelta("0 days 05:00:00")
|
||||
|
||||
def test_addition_subtraction_types(self):
|
||||
# Assert on the types resulting from Timestamp +/- various date/time
|
||||
# objects
|
||||
dt = datetime(2014, 3, 4)
|
||||
td = timedelta(seconds=1)
|
||||
# build a timestamp with a frequency, since then it supports
|
||||
# addition/subtraction of integers
|
||||
with tm.assert_produces_warning(FutureWarning, match="The 'freq' argument"):
|
||||
# freq deprecated
|
||||
ts = Timestamp(dt, freq="D")
|
||||
|
||||
msg = "Addition/subtraction of integers"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
# GH#22535 add/sub with integers is deprecated
|
||||
ts + 1
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
ts - 1
|
||||
|
||||
# Timestamp + datetime not supported, though subtraction is supported
|
||||
# and yields timedelta more tests in tseries/base/tests/test_base.py
|
||||
assert type(ts - dt) == Timedelta
|
||||
assert type(ts + td) == Timestamp
|
||||
assert type(ts - td) == Timestamp
|
||||
|
||||
# Timestamp +/- datetime64 not supported, so not tested (could possibly
|
||||
# assert error raised?)
|
||||
td64 = np.timedelta64(1, "D")
|
||||
assert type(ts + td64) == Timestamp
|
||||
assert type(ts - td64) == Timestamp
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"freq, td, td64",
|
||||
[
|
||||
("S", timedelta(seconds=1), np.timedelta64(1, "s")),
|
||||
("min", timedelta(minutes=1), np.timedelta64(1, "m")),
|
||||
("H", timedelta(hours=1), np.timedelta64(1, "h")),
|
||||
("D", timedelta(days=1), np.timedelta64(1, "D")),
|
||||
("W", timedelta(weeks=1), np.timedelta64(1, "W")),
|
||||
("M", None, np.timedelta64(1, "M")),
|
||||
],
|
||||
)
|
||||
@pytest.mark.filterwarnings("ignore:Timestamp.freq is deprecated:FutureWarning")
|
||||
@pytest.mark.filterwarnings("ignore:The 'freq' argument:FutureWarning")
|
||||
def test_addition_subtraction_preserve_frequency(self, freq, td, td64):
|
||||
ts = Timestamp("2014-03-05 00:00:00", freq=freq)
|
||||
original_freq = ts.freq
|
||||
|
||||
assert (ts + 1 * original_freq).freq == original_freq
|
||||
assert (ts - 1 * original_freq).freq == original_freq
|
||||
|
||||
if td is not None:
|
||||
# timedelta does not support months as unit
|
||||
assert (ts + td).freq == original_freq
|
||||
assert (ts - td).freq == original_freq
|
||||
|
||||
assert (ts + td64).freq == original_freq
|
||||
assert (ts - td64).freq == original_freq
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"td", [Timedelta(hours=3), np.timedelta64(3, "h"), timedelta(hours=3)]
|
||||
)
|
||||
def test_radd_tdscalar(self, td, fixed_now_ts):
|
||||
# GH#24775 timedelta64+Timestamp should not raise
|
||||
ts = fixed_now_ts
|
||||
assert td + ts == ts + td
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other,expected_difference",
|
||||
[
|
||||
(np.timedelta64(-123, "ns"), -123),
|
||||
(np.timedelta64(1234567898, "ns"), 1234567898),
|
||||
(np.timedelta64(-123, "us"), -123000),
|
||||
(np.timedelta64(-123, "ms"), -123000000),
|
||||
],
|
||||
)
|
||||
def test_timestamp_add_timedelta64_unit(self, other, expected_difference):
|
||||
ts = Timestamp(datetime.utcnow())
|
||||
result = ts + other
|
||||
valdiff = result.value - ts.value
|
||||
assert valdiff == expected_difference
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ts",
|
||||
[
|
||||
Timestamp("1776-07-04"),
|
||||
Timestamp("1776-07-04", tz="UTC"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[
|
||||
1,
|
||||
np.int64(1),
|
||||
np.array([1, 2], dtype=np.int32),
|
||||
np.array([3, 4], dtype=np.uint64),
|
||||
],
|
||||
)
|
||||
def test_add_int_with_freq(self, ts, other):
|
||||
msg = "Addition/subtraction of integers and integer-arrays"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
ts + other
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
other + ts
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
ts - other
|
||||
|
||||
msg = "unsupported operand type"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
other - ts
|
||||
|
||||
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
|
||||
def test_addsub_m8ndarray(self, shape):
|
||||
# GH#33296
|
||||
ts = Timestamp("2020-04-04 15:45")
|
||||
other = np.arange(6).astype("m8[h]").reshape(shape)
|
||||
|
||||
result = ts + other
|
||||
|
||||
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
|
||||
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = other + ts
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = ts - other
|
||||
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
|
||||
expected = np.array([x.asm8 for x in ex_stamps], dtype="M8[ns]").reshape(shape)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
other - ts
|
||||
|
||||
@pytest.mark.parametrize("shape", [(6,), (2, 3)])
|
||||
def test_addsub_m8ndarray_tzaware(self, shape):
|
||||
# GH#33296
|
||||
ts = Timestamp("2020-04-04 15:45", tz="US/Pacific")
|
||||
|
||||
other = np.arange(6).astype("m8[h]").reshape(shape)
|
||||
|
||||
result = ts + other
|
||||
|
||||
ex_stamps = [ts + Timedelta(hours=n) for n in range(6)]
|
||||
expected = np.array(ex_stamps).reshape(shape)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = other + ts
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = ts - other
|
||||
ex_stamps = [ts - Timedelta(hours=n) for n in range(6)]
|
||||
expected = np.array(ex_stamps).reshape(shape)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
msg = r"unsupported operand type\(s\) for -: 'numpy.ndarray' and 'Timestamp'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
other - ts
|
||||
|
||||
def test_subtract_different_utc_objects(self, utc_fixture, utc_fixture2):
|
||||
# GH 32619
|
||||
dt = datetime(2021, 1, 1)
|
||||
ts1 = Timestamp(dt, tz=utc_fixture)
|
||||
ts2 = Timestamp(dt, tz=utc_fixture2)
|
||||
result = ts1 - ts2
|
||||
expected = Timedelta(0)
|
||||
assert result == expected
|
||||
@@ -0,0 +1,321 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
import operator
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas import Timestamp
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampComparison:
|
||||
def test_comparison_dt64_ndarray(self, fixed_now_ts):
|
||||
ts = Timestamp("2021-01-01")
|
||||
ts2 = Timestamp("2019-04-05")
|
||||
arr = np.array([[ts.asm8, ts2.asm8]], dtype="M8[ns]")
|
||||
|
||||
result = ts == arr
|
||||
expected = np.array([[True, False]], dtype=bool)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = arr == ts
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = ts != arr
|
||||
tm.assert_numpy_array_equal(result, ~expected)
|
||||
|
||||
result = arr != ts
|
||||
tm.assert_numpy_array_equal(result, ~expected)
|
||||
|
||||
result = ts2 < arr
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
result = arr < ts2
|
||||
tm.assert_numpy_array_equal(result, np.array([[False, False]], dtype=bool))
|
||||
|
||||
result = ts2 <= arr
|
||||
tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
|
||||
|
||||
result = arr <= ts2
|
||||
tm.assert_numpy_array_equal(result, ~expected)
|
||||
|
||||
result = ts >= arr
|
||||
tm.assert_numpy_array_equal(result, np.array([[True, True]], dtype=bool))
|
||||
|
||||
result = arr >= ts
|
||||
tm.assert_numpy_array_equal(result, np.array([[True, False]], dtype=bool))
|
||||
|
||||
@pytest.mark.parametrize("reverse", [True, False])
|
||||
def test_comparison_dt64_ndarray_tzaware(self, reverse, comparison_op):
|
||||
|
||||
ts = Timestamp("2021-01-01 00:00:00.00000", tz="UTC")
|
||||
arr = np.array([ts.asm8, ts.asm8], dtype="M8[ns]")
|
||||
|
||||
left, right = ts, arr
|
||||
if reverse:
|
||||
left, right = arr, ts
|
||||
|
||||
if comparison_op is operator.eq:
|
||||
expected = np.array([False, False], dtype=bool)
|
||||
result = comparison_op(left, right)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
elif comparison_op is operator.ne:
|
||||
expected = np.array([True, True], dtype=bool)
|
||||
result = comparison_op(left, right)
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
else:
|
||||
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
comparison_op(left, right)
|
||||
|
||||
def test_comparison_object_array(self):
|
||||
# GH#15183
|
||||
ts = Timestamp("2011-01-03 00:00:00-0500", tz="US/Eastern")
|
||||
other = Timestamp("2011-01-01 00:00:00-0500", tz="US/Eastern")
|
||||
naive = Timestamp("2011-01-01 00:00:00")
|
||||
|
||||
arr = np.array([other, ts], dtype=object)
|
||||
res = arr == ts
|
||||
expected = np.array([False, True], dtype=bool)
|
||||
assert (res == expected).all()
|
||||
|
||||
# 2D case
|
||||
arr = np.array([[other, ts], [ts, other]], dtype=object)
|
||||
res = arr != ts
|
||||
expected = np.array([[True, False], [False, True]], dtype=bool)
|
||||
assert res.shape == expected.shape
|
||||
assert (res == expected).all()
|
||||
|
||||
# tzaware mismatch
|
||||
arr = np.array([naive], dtype=object)
|
||||
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
arr < ts
|
||||
|
||||
def test_comparison(self):
|
||||
# 5-18-2012 00:00:00.000
|
||||
stamp = 1337299200000000000
|
||||
|
||||
val = Timestamp(stamp)
|
||||
|
||||
assert val == val
|
||||
assert not val != val
|
||||
assert not val < val
|
||||
assert val <= val
|
||||
assert not val > val
|
||||
assert val >= val
|
||||
|
||||
other = datetime(2012, 5, 18)
|
||||
assert val == other
|
||||
assert not val != other
|
||||
assert not val < other
|
||||
assert val <= other
|
||||
assert not val > other
|
||||
assert val >= other
|
||||
|
||||
other = Timestamp(stamp + 100)
|
||||
|
||||
assert val != other
|
||||
assert val != other
|
||||
assert val < other
|
||||
assert val <= other
|
||||
assert other > val
|
||||
assert other >= val
|
||||
|
||||
def test_compare_invalid(self):
|
||||
# GH#8058
|
||||
val = Timestamp("20130101 12:01:02")
|
||||
assert not val == "foo"
|
||||
assert not val == 10.0
|
||||
assert not val == 1
|
||||
assert not val == []
|
||||
assert not val == {"foo": 1}
|
||||
assert not val == np.float64(1)
|
||||
assert not val == np.int64(1)
|
||||
|
||||
assert val != "foo"
|
||||
assert val != 10.0
|
||||
assert val != 1
|
||||
assert val != []
|
||||
assert val != {"foo": 1}
|
||||
assert val != np.float64(1)
|
||||
assert val != np.int64(1)
|
||||
|
||||
@pytest.mark.parametrize("tz", [None, "US/Pacific"])
|
||||
def test_compare_date(self, tz):
|
||||
# GH#36131 comparing Timestamp with date object is deprecated
|
||||
ts = Timestamp("2021-01-01 00:00:00.00000", tz=tz)
|
||||
dt = ts.to_pydatetime().date()
|
||||
# These are incorrectly considered as equal because they
|
||||
# dispatch to the date comparisons which truncates ts
|
||||
|
||||
for left, right in [(ts, dt), (dt, ts)]:
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert left == right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left != right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left < right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert left <= right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert not left > right
|
||||
with tm.assert_produces_warning(FutureWarning):
|
||||
assert left >= right
|
||||
|
||||
# Once the deprecation is enforced, the following assertions
|
||||
# can be enabled:
|
||||
# assert not left == right
|
||||
# assert left != right
|
||||
#
|
||||
# with pytest.raises(TypeError):
|
||||
# left < right
|
||||
# with pytest.raises(TypeError):
|
||||
# left <= right
|
||||
# with pytest.raises(TypeError):
|
||||
# left > right
|
||||
# with pytest.raises(TypeError):
|
||||
# left >= right
|
||||
|
||||
def test_cant_compare_tz_naive_w_aware(self, utc_fixture):
|
||||
# see GH#1404
|
||||
a = Timestamp("3/12/2012")
|
||||
b = Timestamp("3/12/2012", tz=utc_fixture)
|
||||
|
||||
msg = "Cannot compare tz-naive and tz-aware timestamps"
|
||||
assert not a == b
|
||||
assert a != b
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
a < b
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
a <= b
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
a > b
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
a >= b
|
||||
|
||||
assert not b == a
|
||||
assert b != a
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
b < a
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
b <= a
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
b > a
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
b >= a
|
||||
|
||||
assert not a == b.to_pydatetime()
|
||||
assert not a.to_pydatetime() == b
|
||||
|
||||
def test_timestamp_compare_scalars(self):
|
||||
# case where ndim == 0
|
||||
lhs = np.datetime64(datetime(2013, 12, 6))
|
||||
rhs = Timestamp("now")
|
||||
nat = Timestamp("nat")
|
||||
|
||||
ops = {"gt": "lt", "lt": "gt", "ge": "le", "le": "ge", "eq": "eq", "ne": "ne"}
|
||||
|
||||
for left, right in ops.items():
|
||||
left_f = getattr(operator, left)
|
||||
right_f = getattr(operator, right)
|
||||
expected = left_f(lhs, rhs)
|
||||
|
||||
result = right_f(rhs, lhs)
|
||||
assert result == expected
|
||||
|
||||
expected = left_f(rhs, nat)
|
||||
result = right_f(nat, rhs)
|
||||
assert result == expected
|
||||
|
||||
def test_timestamp_compare_with_early_datetime(self):
|
||||
# e.g. datetime.min
|
||||
stamp = Timestamp("2012-01-01")
|
||||
|
||||
assert not stamp == datetime.min
|
||||
assert not stamp == datetime(1600, 1, 1)
|
||||
assert not stamp == datetime(2700, 1, 1)
|
||||
assert stamp != datetime.min
|
||||
assert stamp != datetime(1600, 1, 1)
|
||||
assert stamp != datetime(2700, 1, 1)
|
||||
assert stamp > datetime(1600, 1, 1)
|
||||
assert stamp >= datetime(1600, 1, 1)
|
||||
assert stamp < datetime(2700, 1, 1)
|
||||
assert stamp <= datetime(2700, 1, 1)
|
||||
|
||||
other = Timestamp.min.to_pydatetime(warn=False)
|
||||
assert other - timedelta(microseconds=1) < Timestamp.min
|
||||
|
||||
def test_timestamp_compare_oob_dt64(self):
|
||||
us = np.timedelta64(1, "us")
|
||||
other = np.datetime64(Timestamp.min).astype("M8[us]")
|
||||
|
||||
# This may change if the implementation bound is dropped to match
|
||||
# DatetimeArray/DatetimeIndex GH#24124
|
||||
assert Timestamp.min > other
|
||||
# Note: numpy gets the reversed comparison wrong
|
||||
|
||||
other = np.datetime64(Timestamp.max).astype("M8[us]")
|
||||
assert Timestamp.max > other # not actually OOB
|
||||
assert other < Timestamp.max
|
||||
|
||||
assert Timestamp.max < other + us
|
||||
# Note: numpy gets the reversed comparison wrong
|
||||
|
||||
# GH-42794
|
||||
other = datetime(9999, 9, 9)
|
||||
assert Timestamp.min < other
|
||||
assert other > Timestamp.min
|
||||
assert Timestamp.max < other
|
||||
assert other > Timestamp.max
|
||||
|
||||
other = datetime(1, 1, 1)
|
||||
assert Timestamp.max > other
|
||||
assert other < Timestamp.max
|
||||
assert Timestamp.min > other
|
||||
assert other < Timestamp.min
|
||||
|
||||
def test_compare_zerodim_array(self, fixed_now_ts):
|
||||
# GH#26916
|
||||
ts = fixed_now_ts
|
||||
dt64 = np.datetime64("2016-01-01", "ns")
|
||||
arr = np.array(dt64)
|
||||
assert arr.ndim == 0
|
||||
|
||||
result = arr < ts
|
||||
assert result is np.bool_(True)
|
||||
result = arr > ts
|
||||
assert result is np.bool_(False)
|
||||
|
||||
|
||||
def test_rich_comparison_with_unsupported_type():
|
||||
# Comparisons with unsupported objects should return NotImplemented
|
||||
# (it previously raised TypeError, see #24011)
|
||||
|
||||
class Inf:
|
||||
def __lt__(self, o):
|
||||
return False
|
||||
|
||||
def __le__(self, o):
|
||||
return isinstance(o, Inf)
|
||||
|
||||
def __gt__(self, o):
|
||||
return not isinstance(o, Inf)
|
||||
|
||||
def __ge__(self, o):
|
||||
return True
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return isinstance(other, Inf)
|
||||
|
||||
inf = Inf()
|
||||
timestamp = Timestamp("2018-11-30")
|
||||
|
||||
for left, right in [(inf, timestamp), (timestamp, inf)]:
|
||||
assert left > right or left < right
|
||||
assert left >= right or left <= right
|
||||
assert not (left == right)
|
||||
assert left != right
|
||||
@@ -0,0 +1,637 @@
|
||||
import calendar
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
|
||||
import dateutil.tz
|
||||
from dateutil.tz import tzutc
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas.compat import PY310
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
|
||||
from pandas import (
|
||||
Period,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
from pandas.tseries import offsets
|
||||
|
||||
|
||||
class TestTimestampConstructors:
|
||||
def test_constructor_datetime64_with_tz(self):
|
||||
# GH#42288, GH#24559
|
||||
dt = np.datetime64("1970-01-01 05:00:00")
|
||||
tzstr = "UTC+05:00"
|
||||
|
||||
msg = "interpreted as a wall time"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
ts = Timestamp(dt, tz=tzstr)
|
||||
|
||||
# Check that we match the old behavior
|
||||
alt = Timestamp(dt).tz_localize("UTC").tz_convert(tzstr)
|
||||
assert ts == alt
|
||||
|
||||
# Check that we *don't* match the future behavior
|
||||
assert ts.hour != 5
|
||||
expected_future = Timestamp(dt).tz_localize(tzstr)
|
||||
assert ts != expected_future
|
||||
|
||||
def test_constructor(self):
|
||||
base_str = "2014-07-01 09:00"
|
||||
base_dt = datetime(2014, 7, 1, 9)
|
||||
base_expected = 1_404_205_200_000_000_000
|
||||
|
||||
# confirm base representation is correct
|
||||
assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
|
||||
|
||||
tests = [
|
||||
(base_str, base_dt, base_expected),
|
||||
(
|
||||
"2014-07-01 10:00",
|
||||
datetime(2014, 7, 1, 10),
|
||||
base_expected + 3600 * 1_000_000_000,
|
||||
),
|
||||
(
|
||||
"2014-07-01 09:00:00.000008000",
|
||||
datetime(2014, 7, 1, 9, 0, 0, 8),
|
||||
base_expected + 8000,
|
||||
),
|
||||
(
|
||||
"2014-07-01 09:00:00.000000005",
|
||||
Timestamp("2014-07-01 09:00:00.000000005"),
|
||||
base_expected + 5,
|
||||
),
|
||||
]
|
||||
|
||||
timezones = [
|
||||
(None, 0),
|
||||
("UTC", 0),
|
||||
(pytz.utc, 0),
|
||||
("Asia/Tokyo", 9),
|
||||
("US/Eastern", -4),
|
||||
("dateutil/US/Pacific", -7),
|
||||
(pytz.FixedOffset(-180), -3),
|
||||
(dateutil.tz.tzoffset(None, 18000), 5),
|
||||
]
|
||||
|
||||
for date_str, date, expected in tests:
|
||||
for result in [Timestamp(date_str), Timestamp(date)]:
|
||||
# only with timestring
|
||||
assert result.value == expected
|
||||
|
||||
# re-creation shouldn't affect to internal value
|
||||
result = Timestamp(result)
|
||||
assert result.value == expected
|
||||
|
||||
# with timezone
|
||||
for tz, offset in timezones:
|
||||
for result in [Timestamp(date_str, tz=tz), Timestamp(date, tz=tz)]:
|
||||
expected_tz = expected - offset * 3600 * 1_000_000_000
|
||||
assert result.value == expected_tz
|
||||
|
||||
# should preserve tz
|
||||
result = Timestamp(result)
|
||||
assert result.value == expected_tz
|
||||
|
||||
# should convert to UTC
|
||||
if tz is not None:
|
||||
result = Timestamp(result).tz_convert("UTC")
|
||||
else:
|
||||
result = Timestamp(result, tz="UTC")
|
||||
expected_utc = expected - offset * 3600 * 1_000_000_000
|
||||
assert result.value == expected_utc
|
||||
|
||||
def test_constructor_with_stringoffset(self):
|
||||
# GH 7833
|
||||
base_str = "2014-07-01 11:00:00+02:00"
|
||||
base_dt = datetime(2014, 7, 1, 9)
|
||||
base_expected = 1_404_205_200_000_000_000
|
||||
|
||||
# confirm base representation is correct
|
||||
assert calendar.timegm(base_dt.timetuple()) * 1_000_000_000 == base_expected
|
||||
|
||||
tests = [
|
||||
(base_str, base_expected),
|
||||
("2014-07-01 12:00:00+02:00", base_expected + 3600 * 1_000_000_000),
|
||||
("2014-07-01 11:00:00.000008000+02:00", base_expected + 8000),
|
||||
("2014-07-01 11:00:00.000000005+02:00", base_expected + 5),
|
||||
]
|
||||
|
||||
timezones = [
|
||||
(None, 0),
|
||||
("UTC", 0),
|
||||
(pytz.utc, 0),
|
||||
("Asia/Tokyo", 9),
|
||||
("US/Eastern", -4),
|
||||
("dateutil/US/Pacific", -7),
|
||||
(pytz.FixedOffset(-180), -3),
|
||||
(dateutil.tz.tzoffset(None, 18000), 5),
|
||||
]
|
||||
|
||||
for date_str, expected in tests:
|
||||
for result in [Timestamp(date_str)]:
|
||||
# only with timestring
|
||||
assert result.value == expected
|
||||
|
||||
# re-creation shouldn't affect to internal value
|
||||
result = Timestamp(result)
|
||||
assert result.value == expected
|
||||
|
||||
# with timezone
|
||||
for tz, offset in timezones:
|
||||
result = Timestamp(date_str, tz=tz)
|
||||
expected_tz = expected
|
||||
assert result.value == expected_tz
|
||||
|
||||
# should preserve tz
|
||||
result = Timestamp(result)
|
||||
assert result.value == expected_tz
|
||||
|
||||
# should convert to UTC
|
||||
result = Timestamp(result).tz_convert("UTC")
|
||||
expected_utc = expected
|
||||
assert result.value == expected_utc
|
||||
|
||||
# This should be 2013-11-01 05:00 in UTC
|
||||
# converted to Chicago tz
|
||||
result = Timestamp("2013-11-01 00:00:00-0500", tz="America/Chicago")
|
||||
assert result.value == Timestamp("2013-11-01 05:00").value
|
||||
expected = "Timestamp('2013-11-01 00:00:00-0500', tz='America/Chicago')"
|
||||
assert repr(result) == expected
|
||||
assert result == eval(repr(result))
|
||||
|
||||
# This should be 2013-11-01 05:00 in UTC
|
||||
# converted to Tokyo tz (+09:00)
|
||||
result = Timestamp("2013-11-01 00:00:00-0500", tz="Asia/Tokyo")
|
||||
assert result.value == Timestamp("2013-11-01 05:00").value
|
||||
expected = "Timestamp('2013-11-01 14:00:00+0900', tz='Asia/Tokyo')"
|
||||
assert repr(result) == expected
|
||||
assert result == eval(repr(result))
|
||||
|
||||
# GH11708
|
||||
# This should be 2015-11-18 10:00 in UTC
|
||||
# converted to Asia/Katmandu
|
||||
result = Timestamp("2015-11-18 15:45:00+05:45", tz="Asia/Katmandu")
|
||||
assert result.value == Timestamp("2015-11-18 10:00").value
|
||||
expected = "Timestamp('2015-11-18 15:45:00+0545', tz='Asia/Katmandu')"
|
||||
assert repr(result) == expected
|
||||
assert result == eval(repr(result))
|
||||
|
||||
# This should be 2015-11-18 10:00 in UTC
|
||||
# converted to Asia/Kolkata
|
||||
result = Timestamp("2015-11-18 15:30:00+05:30", tz="Asia/Kolkata")
|
||||
assert result.value == Timestamp("2015-11-18 10:00").value
|
||||
expected = "Timestamp('2015-11-18 15:30:00+0530', tz='Asia/Kolkata')"
|
||||
assert repr(result) == expected
|
||||
assert result == eval(repr(result))
|
||||
|
||||
def test_constructor_invalid(self):
|
||||
msg = "Cannot convert input"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp(slice(2))
|
||||
msg = "Cannot convert Period"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(Period("1000-01-01"))
|
||||
|
||||
def test_constructor_invalid_tz(self):
|
||||
# GH#17690
|
||||
msg = (
|
||||
"Argument 'tzinfo' has incorrect type "
|
||||
r"\(expected datetime.tzinfo, got str\)"
|
||||
)
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp("2017-10-22", tzinfo="US/Eastern")
|
||||
|
||||
msg = "at most one of"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp("2017-10-22", tzinfo=pytz.utc, tz="UTC")
|
||||
|
||||
msg = "Invalid frequency:"
|
||||
msg2 = "The 'freq' argument"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
# GH#5168
|
||||
# case where user tries to pass tz as an arg, not kwarg, gets
|
||||
# interpreted as a `freq`
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg2):
|
||||
Timestamp("2012-01-01", "US/Pacific")
|
||||
|
||||
def test_constructor_strptime(self):
|
||||
# GH25016
|
||||
# Test support for Timestamp.strptime
|
||||
fmt = "%Y%m%d-%H%M%S-%f%z"
|
||||
ts = "20190129-235348-000001+0000"
|
||||
msg = r"Timestamp.strptime\(\) is not implemented"
|
||||
with pytest.raises(NotImplementedError, match=msg):
|
||||
Timestamp.strptime(ts, fmt)
|
||||
|
||||
def test_constructor_tz_or_tzinfo(self):
|
||||
# GH#17943, GH#17690, GH#5168
|
||||
stamps = [
|
||||
Timestamp(year=2017, month=10, day=22, tz="UTC"),
|
||||
Timestamp(year=2017, month=10, day=22, tzinfo=pytz.utc),
|
||||
Timestamp(year=2017, month=10, day=22, tz=pytz.utc),
|
||||
Timestamp(datetime(2017, 10, 22), tzinfo=pytz.utc),
|
||||
Timestamp(datetime(2017, 10, 22), tz="UTC"),
|
||||
Timestamp(datetime(2017, 10, 22), tz=pytz.utc),
|
||||
]
|
||||
assert all(ts == stamps[0] for ts in stamps)
|
||||
|
||||
def test_constructor_positional(self):
|
||||
# see gh-10758
|
||||
msg = (
|
||||
"'NoneType' object cannot be interpreted as an integer"
|
||||
if PY310
|
||||
else "an integer is required"
|
||||
)
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp(2000, 1)
|
||||
|
||||
msg = "month must be in 1..12"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(2000, 0, 1)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(2000, 13, 1)
|
||||
|
||||
msg = "day is out of range for month"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(2000, 1, 0)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(2000, 1, 32)
|
||||
|
||||
# see gh-11630
|
||||
assert repr(Timestamp(2015, 11, 12)) == repr(Timestamp("20151112"))
|
||||
assert repr(Timestamp(2015, 11, 12, 1, 2, 3, 999999)) == repr(
|
||||
Timestamp("2015-11-12 01:02:03.999999")
|
||||
)
|
||||
|
||||
def test_constructor_keyword(self):
|
||||
# GH 10758
|
||||
msg = "function missing required argument 'day'|Required argument 'day'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp(year=2000, month=1)
|
||||
|
||||
msg = "month must be in 1..12"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(year=2000, month=0, day=1)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(year=2000, month=13, day=1)
|
||||
|
||||
msg = "day is out of range for month"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(year=2000, month=1, day=0)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(year=2000, month=1, day=32)
|
||||
|
||||
assert repr(Timestamp(year=2015, month=11, day=12)) == repr(
|
||||
Timestamp("20151112")
|
||||
)
|
||||
|
||||
assert repr(
|
||||
Timestamp(
|
||||
year=2015,
|
||||
month=11,
|
||||
day=12,
|
||||
hour=1,
|
||||
minute=2,
|
||||
second=3,
|
||||
microsecond=999999,
|
||||
)
|
||||
) == repr(Timestamp("2015-11-12 01:02:03.999999"))
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Timestamp.freq is:FutureWarning")
|
||||
@pytest.mark.filterwarnings("ignore:The 'freq' argument:FutureWarning")
|
||||
def test_constructor_fromordinal(self):
|
||||
base = datetime(2000, 1, 1)
|
||||
|
||||
ts = Timestamp.fromordinal(base.toordinal(), freq="D")
|
||||
assert base == ts
|
||||
assert ts.freq == "D"
|
||||
assert base.toordinal() == ts.toordinal()
|
||||
|
||||
ts = Timestamp.fromordinal(base.toordinal(), tz="US/Eastern")
|
||||
assert Timestamp("2000-01-01", tz="US/Eastern") == ts
|
||||
assert base.toordinal() == ts.toordinal()
|
||||
|
||||
# GH#3042
|
||||
dt = datetime(2011, 4, 16, 0, 0)
|
||||
ts = Timestamp.fromordinal(dt.toordinal())
|
||||
assert ts.to_pydatetime() == dt
|
||||
|
||||
# with a tzinfo
|
||||
stamp = Timestamp("2011-4-16", tz="US/Eastern")
|
||||
dt_tz = stamp.to_pydatetime()
|
||||
ts = Timestamp.fromordinal(dt_tz.toordinal(), tz="US/Eastern")
|
||||
assert ts.to_pydatetime() == dt_tz
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"result",
|
||||
[
|
||||
Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), nanosecond=1),
|
||||
Timestamp(
|
||||
year=2000,
|
||||
month=1,
|
||||
day=2,
|
||||
hour=3,
|
||||
minute=4,
|
||||
second=5,
|
||||
microsecond=6,
|
||||
nanosecond=1,
|
||||
),
|
||||
Timestamp(
|
||||
year=2000,
|
||||
month=1,
|
||||
day=2,
|
||||
hour=3,
|
||||
minute=4,
|
||||
second=5,
|
||||
microsecond=6,
|
||||
nanosecond=1,
|
||||
tz="UTC",
|
||||
),
|
||||
Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, None),
|
||||
# error: Argument 9 to "Timestamp" has incompatible type "_UTCclass";
|
||||
# expected "Optional[int]"
|
||||
Timestamp(2000, 1, 2, 3, 4, 5, 6, 1, pytz.UTC), # type: ignore[arg-type]
|
||||
],
|
||||
)
|
||||
def test_constructor_nanosecond(self, result):
|
||||
# GH 18898
|
||||
expected = Timestamp(datetime(2000, 1, 2, 3, 4, 5, 6), tz=result.tz)
|
||||
expected = expected + Timedelta(nanoseconds=1)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("z", ["Z0", "Z00"])
|
||||
def test_constructor_invalid_Z0_isostring(self, z):
|
||||
# GH 8910
|
||||
msg = "could not convert string to Timestamp"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(f"2014-11-02 01:00{z}")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arg",
|
||||
[
|
||||
"year",
|
||||
"month",
|
||||
"day",
|
||||
"hour",
|
||||
"minute",
|
||||
"second",
|
||||
"microsecond",
|
||||
"nanosecond",
|
||||
],
|
||||
)
|
||||
def test_invalid_date_kwarg_with_string_input(self, arg):
|
||||
kwarg = {arg: 1}
|
||||
msg = "Cannot pass a date attribute keyword argument"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp("2010-10-10 12:59:59.999999999", **kwarg)
|
||||
|
||||
def test_out_of_bounds_integer_value(self):
|
||||
# GH#26651 check that we raise OutOfBoundsDatetime, not OverflowError
|
||||
msg = str(Timestamp.max.value * 2)
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp(Timestamp.max.value * 2)
|
||||
msg = str(Timestamp.min.value * 2)
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp(Timestamp.min.value * 2)
|
||||
|
||||
def test_out_of_bounds_value(self):
|
||||
one_us = np.timedelta64(1).astype("timedelta64[us]")
|
||||
|
||||
# By definition we can't go out of bounds in [ns], so we
|
||||
# convert the datetime64s to [us] so we can go out of bounds
|
||||
min_ts_us = np.datetime64(Timestamp.min).astype("M8[us]") + one_us
|
||||
max_ts_us = np.datetime64(Timestamp.max).astype("M8[us]")
|
||||
|
||||
# No error for the min/max datetimes
|
||||
Timestamp(min_ts_us)
|
||||
Timestamp(max_ts_us)
|
||||
|
||||
msg = "Out of bounds"
|
||||
# One us less than the minimum is an error
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(min_ts_us - one_us)
|
||||
|
||||
# One us more than the maximum is an error
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(max_ts_us + one_us)
|
||||
|
||||
def test_out_of_bounds_string(self):
|
||||
msg = "Out of bounds"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp("1676-01-01")
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp("2263-01-01")
|
||||
|
||||
def test_barely_out_of_bounds(self):
|
||||
# GH#19529
|
||||
# GH#19382 close enough to bounds that dropping nanos would result
|
||||
# in an in-bounds datetime
|
||||
msg = "Out of bounds nanosecond timestamp: 2262-04-11 23:47:16"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp("2262-04-11 23:47:16.854775808")
|
||||
|
||||
def test_bounds_with_different_units(self):
|
||||
out_of_bounds_dates = ("1677-09-21", "2262-04-12")
|
||||
|
||||
time_units = ("D", "h", "m", "s", "ms", "us")
|
||||
|
||||
for date_string in out_of_bounds_dates:
|
||||
for unit in time_units:
|
||||
dt64 = np.datetime64(date_string, unit)
|
||||
msg = "Out of bounds"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp(dt64)
|
||||
|
||||
in_bounds_dates = ("1677-09-23", "2262-04-11")
|
||||
|
||||
for date_string in in_bounds_dates:
|
||||
for unit in time_units:
|
||||
dt64 = np.datetime64(date_string, unit)
|
||||
Timestamp(dt64)
|
||||
|
||||
@pytest.mark.parametrize("arg", ["001-01-01", "0001-01-01"])
|
||||
def test_out_of_bounds_string_consistency(self, arg):
|
||||
# GH 15829
|
||||
msg = "Out of bounds"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp(arg)
|
||||
|
||||
def test_min_valid(self):
|
||||
# Ensure that Timestamp.min is a valid Timestamp
|
||||
Timestamp(Timestamp.min)
|
||||
|
||||
def test_max_valid(self):
|
||||
# Ensure that Timestamp.max is a valid Timestamp
|
||||
Timestamp(Timestamp.max)
|
||||
|
||||
def test_now(self):
|
||||
# GH#9000
|
||||
ts_from_string = Timestamp("now")
|
||||
ts_from_method = Timestamp.now()
|
||||
ts_datetime = datetime.now()
|
||||
|
||||
ts_from_string_tz = Timestamp("now", tz="US/Eastern")
|
||||
ts_from_method_tz = Timestamp.now(tz="US/Eastern")
|
||||
|
||||
# Check that the delta between the times is less than 1s (arbitrarily
|
||||
# small)
|
||||
delta = Timedelta(seconds=1)
|
||||
assert abs(ts_from_method - ts_from_string) < delta
|
||||
assert abs(ts_datetime - ts_from_method) < delta
|
||||
assert abs(ts_from_method_tz - ts_from_string_tz) < delta
|
||||
assert (
|
||||
abs(
|
||||
ts_from_string_tz.tz_localize(None)
|
||||
- ts_from_method_tz.tz_localize(None)
|
||||
)
|
||||
< delta
|
||||
)
|
||||
|
||||
def test_today(self):
|
||||
ts_from_string = Timestamp("today")
|
||||
ts_from_method = Timestamp.today()
|
||||
ts_datetime = datetime.today()
|
||||
|
||||
ts_from_string_tz = Timestamp("today", tz="US/Eastern")
|
||||
ts_from_method_tz = Timestamp.today(tz="US/Eastern")
|
||||
|
||||
# Check that the delta between the times is less than 1s (arbitrarily
|
||||
# small)
|
||||
delta = Timedelta(seconds=1)
|
||||
assert abs(ts_from_method - ts_from_string) < delta
|
||||
assert abs(ts_datetime - ts_from_method) < delta
|
||||
assert abs(ts_from_method_tz - ts_from_string_tz) < delta
|
||||
assert (
|
||||
abs(
|
||||
ts_from_string_tz.tz_localize(None)
|
||||
- ts_from_method_tz.tz_localize(None)
|
||||
)
|
||||
< delta
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("tz", [None, pytz.timezone("US/Pacific")])
|
||||
def test_disallow_setting_tz(self, tz):
|
||||
# GH 3746
|
||||
ts = Timestamp("2010")
|
||||
msg = "Cannot directly set timezone"
|
||||
with pytest.raises(AttributeError, match=msg):
|
||||
ts.tz = tz
|
||||
|
||||
@pytest.mark.parametrize("offset", ["+0300", "+0200"])
|
||||
def test_construct_timestamp_near_dst(self, offset):
|
||||
# GH 20854
|
||||
expected = Timestamp(f"2016-10-30 03:00:00{offset}", tz="Europe/Helsinki")
|
||||
result = Timestamp(expected).tz_convert("Europe/Helsinki")
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"arg", ["2013/01/01 00:00:00+09:00", "2013-01-01 00:00:00+09:00"]
|
||||
)
|
||||
def test_construct_with_different_string_format(self, arg):
|
||||
# GH 12064
|
||||
result = Timestamp(arg)
|
||||
expected = Timestamp(datetime(2013, 1, 1), tz=pytz.FixedOffset(540))
|
||||
assert result == expected
|
||||
|
||||
def test_construct_timestamp_preserve_original_frequency(self):
|
||||
# GH 22311
|
||||
with tm.assert_produces_warning(FutureWarning, match="The 'freq' argument"):
|
||||
result = Timestamp(Timestamp("2010-08-08", freq="D")).freq
|
||||
expected = offsets.Day()
|
||||
assert result == expected
|
||||
|
||||
def test_constructor_invalid_frequency(self):
|
||||
# GH 22311
|
||||
msg = "Invalid frequency:"
|
||||
msg2 = "The 'freq' argument"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg2):
|
||||
Timestamp("2012-01-01", freq=[])
|
||||
|
||||
@pytest.mark.parametrize("box", [datetime, Timestamp])
|
||||
def test_raise_tz_and_tzinfo_in_datetime_input(self, box):
|
||||
# GH 23579
|
||||
kwargs = {"year": 2018, "month": 1, "day": 1, "tzinfo": pytz.utc}
|
||||
msg = "Cannot pass a datetime or Timestamp"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(box(**kwargs), tz="US/Pacific")
|
||||
msg = "Cannot pass a datetime or Timestamp"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timestamp(box(**kwargs), tzinfo=pytz.timezone("US/Pacific"))
|
||||
|
||||
def test_dont_convert_dateutil_utc_to_pytz_utc(self):
|
||||
result = Timestamp(datetime(2018, 1, 1), tz=tzutc())
|
||||
expected = Timestamp(datetime(2018, 1, 1)).tz_localize(tzutc())
|
||||
assert result == expected
|
||||
|
||||
def test_constructor_subclassed_datetime(self):
|
||||
# GH 25851
|
||||
# ensure that subclassed datetime works for
|
||||
# Timestamp creation
|
||||
class SubDatetime(datetime):
|
||||
pass
|
||||
|
||||
data = SubDatetime(2000, 1, 1)
|
||||
result = Timestamp(data)
|
||||
expected = Timestamp(2000, 1, 1)
|
||||
assert result == expected
|
||||
|
||||
def test_constructor_fromisocalendar(self):
|
||||
# GH 30395
|
||||
expected_timestamp = Timestamp("2000-01-03 00:00:00")
|
||||
expected_stdlib = datetime.fromisocalendar(2000, 1, 1)
|
||||
result = Timestamp.fromisocalendar(2000, 1, 1)
|
||||
assert result == expected_timestamp
|
||||
assert result == expected_stdlib
|
||||
assert isinstance(result, Timestamp)
|
||||
|
||||
|
||||
def test_constructor_ambigous_dst():
|
||||
# GH 24329
|
||||
# Make sure that calling Timestamp constructor
|
||||
# on Timestamp created from ambiguous time
|
||||
# doesn't change Timestamp.value
|
||||
ts = Timestamp(1382835600000000000, tz="dateutil/Europe/London")
|
||||
expected = ts.value
|
||||
result = Timestamp(ts).value
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("epoch", [1552211999999999872, 1552211999999999999])
|
||||
def test_constructor_before_dst_switch(epoch):
|
||||
# GH 31043
|
||||
# Make sure that calling Timestamp constructor
|
||||
# on time just before DST switch doesn't lead to
|
||||
# nonexistent time or value change
|
||||
ts = Timestamp(epoch, tz="dateutil/America/Los_Angeles")
|
||||
result = ts.tz.dst(ts)
|
||||
expected = timedelta(seconds=0)
|
||||
assert Timestamp(ts).value == epoch
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_timestamp_constructor_identity():
|
||||
# Test for #30543
|
||||
expected = Timestamp("2017-01-01T12")
|
||||
result = Timestamp(expected)
|
||||
assert result is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("kwargs", [{}, {"year": 2020}, {"year": 2020, "month": 1}])
|
||||
def test_constructor_missing_keyword(kwargs):
|
||||
# GH 31200
|
||||
|
||||
# The exact error message of datetime() depends on its version
|
||||
msg1 = r"function missing required argument '(year|month|day)' \(pos [123]\)"
|
||||
msg2 = r"Required argument '(year|month|day)' \(pos [123]\) not found"
|
||||
msg = "|".join([msg1, msg2])
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp(**kwargs)
|
||||
@@ -0,0 +1,71 @@
|
||||
import pytest
|
||||
|
||||
from pandas import Timestamp
|
||||
|
||||
ts_no_ns = Timestamp(
|
||||
year=2019,
|
||||
month=5,
|
||||
day=18,
|
||||
hour=15,
|
||||
minute=17,
|
||||
second=8,
|
||||
microsecond=132263,
|
||||
)
|
||||
ts_ns = Timestamp(
|
||||
year=2019,
|
||||
month=5,
|
||||
day=18,
|
||||
hour=15,
|
||||
minute=17,
|
||||
second=8,
|
||||
microsecond=132263,
|
||||
nanosecond=123,
|
||||
)
|
||||
ts_ns_tz = Timestamp(
|
||||
year=2019,
|
||||
month=5,
|
||||
day=18,
|
||||
hour=15,
|
||||
minute=17,
|
||||
second=8,
|
||||
microsecond=132263,
|
||||
nanosecond=123,
|
||||
tz="UTC",
|
||||
)
|
||||
ts_no_us = Timestamp(
|
||||
year=2019,
|
||||
month=5,
|
||||
day=18,
|
||||
hour=15,
|
||||
minute=17,
|
||||
second=8,
|
||||
microsecond=0,
|
||||
nanosecond=123,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"ts, timespec, expected_iso",
|
||||
[
|
||||
(ts_no_ns, "auto", "2019-05-18T15:17:08.132263"),
|
||||
(ts_no_ns, "seconds", "2019-05-18T15:17:08"),
|
||||
(ts_no_ns, "nanoseconds", "2019-05-18T15:17:08.132263000"),
|
||||
(ts_ns, "auto", "2019-05-18T15:17:08.132263123"),
|
||||
(ts_ns, "hours", "2019-05-18T15"),
|
||||
(ts_ns, "minutes", "2019-05-18T15:17"),
|
||||
(ts_ns, "seconds", "2019-05-18T15:17:08"),
|
||||
(ts_ns, "milliseconds", "2019-05-18T15:17:08.132"),
|
||||
(ts_ns, "microseconds", "2019-05-18T15:17:08.132263"),
|
||||
(ts_ns, "nanoseconds", "2019-05-18T15:17:08.132263123"),
|
||||
(ts_ns_tz, "auto", "2019-05-18T15:17:08.132263123+00:00"),
|
||||
(ts_ns_tz, "hours", "2019-05-18T15+00:00"),
|
||||
(ts_ns_tz, "minutes", "2019-05-18T15:17+00:00"),
|
||||
(ts_ns_tz, "seconds", "2019-05-18T15:17:08+00:00"),
|
||||
(ts_ns_tz, "milliseconds", "2019-05-18T15:17:08.132+00:00"),
|
||||
(ts_ns_tz, "microseconds", "2019-05-18T15:17:08.132263+00:00"),
|
||||
(ts_ns_tz, "nanoseconds", "2019-05-18T15:17:08.132263123+00:00"),
|
||||
(ts_no_us, "auto", "2019-05-18T15:17:08.000000123"),
|
||||
],
|
||||
)
|
||||
def test_isoformat(ts, timespec, expected_iso):
|
||||
assert ts.isoformat(timespec=timespec) == expected_iso
|
||||
@@ -0,0 +1,107 @@
|
||||
import pprint
|
||||
|
||||
import pytest
|
||||
import pytz # noqa # a test below uses pytz but only inside a `eval` call
|
||||
|
||||
from pandas import Timestamp
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampRendering:
|
||||
|
||||
timezones = ["UTC", "Asia/Tokyo", "US/Eastern", "dateutil/US/Pacific"]
|
||||
|
||||
@pytest.mark.parametrize("tz", timezones)
|
||||
@pytest.mark.parametrize("freq", ["D", "M", "S", "N"])
|
||||
@pytest.mark.parametrize(
|
||||
"date", ["2014-03-07", "2014-01-01 09:00", "2014-01-01 00:00:00.000000001"]
|
||||
)
|
||||
def test_repr(self, date, freq, tz):
|
||||
# avoid to match with timezone name
|
||||
freq_repr = f"'{freq}'"
|
||||
if tz.startswith("dateutil"):
|
||||
tz_repr = tz.replace("dateutil", "")
|
||||
else:
|
||||
tz_repr = tz
|
||||
|
||||
date_only = Timestamp(date)
|
||||
assert date in repr(date_only)
|
||||
assert tz_repr not in repr(date_only)
|
||||
assert freq_repr not in repr(date_only)
|
||||
assert date_only == eval(repr(date_only))
|
||||
|
||||
date_tz = Timestamp(date, tz=tz)
|
||||
assert date in repr(date_tz)
|
||||
assert tz_repr in repr(date_tz)
|
||||
assert freq_repr not in repr(date_tz)
|
||||
assert date_tz == eval(repr(date_tz))
|
||||
|
||||
msg = "The 'freq' argument in Timestamp"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
date_freq = Timestamp(date, freq=freq)
|
||||
assert date in repr(date_freq)
|
||||
assert tz_repr not in repr(date_freq)
|
||||
assert freq_repr in repr(date_freq)
|
||||
with tm.assert_produces_warning(
|
||||
FutureWarning, match=msg, check_stacklevel=False
|
||||
):
|
||||
assert date_freq == eval(repr(date_freq))
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
date_tz_freq = Timestamp(date, tz=tz, freq=freq)
|
||||
assert date in repr(date_tz_freq)
|
||||
assert tz_repr in repr(date_tz_freq)
|
||||
assert freq_repr in repr(date_tz_freq)
|
||||
with tm.assert_produces_warning(
|
||||
FutureWarning, match=msg, check_stacklevel=False
|
||||
):
|
||||
assert date_tz_freq == eval(repr(date_tz_freq))
|
||||
|
||||
def test_repr_utcoffset(self):
|
||||
# This can cause the tz field to be populated, but it's redundant to
|
||||
# include this information in the date-string.
|
||||
date_with_utc_offset = Timestamp("2014-03-13 00:00:00-0400", tz=None)
|
||||
assert "2014-03-13 00:00:00-0400" in repr(date_with_utc_offset)
|
||||
assert "tzoffset" not in repr(date_with_utc_offset)
|
||||
assert "pytz.FixedOffset(-240)" in repr(date_with_utc_offset)
|
||||
expr = repr(date_with_utc_offset).replace(
|
||||
"'pytz.FixedOffset(-240)'", "pytz.FixedOffset(-240)"
|
||||
)
|
||||
assert date_with_utc_offset == eval(expr)
|
||||
|
||||
def test_timestamp_repr_pre1900(self):
|
||||
# pre-1900
|
||||
stamp = Timestamp("1850-01-01", tz="US/Eastern")
|
||||
repr(stamp)
|
||||
|
||||
iso8601 = "1850-01-01 01:23:45.012345"
|
||||
stamp = Timestamp(iso8601, tz="US/Eastern")
|
||||
result = repr(stamp)
|
||||
assert iso8601 in result
|
||||
|
||||
def test_pprint(self):
|
||||
# GH#12622
|
||||
nested_obj = {"foo": 1, "bar": [{"w": {"a": Timestamp("2011-01-01")}}] * 10}
|
||||
result = pprint.pformat(nested_obj, width=50)
|
||||
expected = r"""{'bar': [{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}},
|
||||
{'w': {'a': Timestamp('2011-01-01 00:00:00')}}],
|
||||
'foo': 1}"""
|
||||
assert result == expected
|
||||
|
||||
def test_to_timestamp_repr_is_code(self):
|
||||
zs = [
|
||||
Timestamp("99-04-17 00:00:00", tz="UTC"),
|
||||
Timestamp("2001-04-17 00:00:00", tz="UTC"),
|
||||
Timestamp("2001-04-17 00:00:00", tz="America/Los_Angeles"),
|
||||
Timestamp("2001-04-17 00:00:00", tz=None),
|
||||
]
|
||||
for z in zs:
|
||||
assert eval(repr(z)) == z
|
||||
@@ -0,0 +1,694 @@
|
||||
""" test the scalar Timestamp """
|
||||
|
||||
import calendar
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
import locale
|
||||
import pickle
|
||||
import unicodedata
|
||||
|
||||
from dateutil.tz import tzutc
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
from pytz import (
|
||||
timezone,
|
||||
utc,
|
||||
)
|
||||
|
||||
from pandas._libs.tslibs.timezones import (
|
||||
dateutil_gettz as gettz,
|
||||
get_timezone,
|
||||
)
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
from pandas import (
|
||||
NaT,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
from pandas.tseries import offsets
|
||||
|
||||
|
||||
class TestTimestampProperties:
|
||||
def test_freq_deprecation(self):
|
||||
# GH#41586
|
||||
msg = "The 'freq' argument in Timestamp is deprecated"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
# warning issued at construction
|
||||
ts = Timestamp("2021-06-01", freq="D")
|
||||
ts2 = Timestamp("2021-06-01", freq="B")
|
||||
|
||||
msg = "Timestamp.freq is deprecated"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
# warning issued at attribute lookup
|
||||
ts.freq
|
||||
|
||||
for per in ["month", "quarter", "year"]:
|
||||
for side in ["start", "end"]:
|
||||
attr = f"is_{per}_{side}"
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
getattr(ts2, attr)
|
||||
|
||||
# is_(month|quarter|year)_(start|end) does _not_ issue a warning
|
||||
# with freq="D" bc the result will be unaffected by the deprecation
|
||||
with tm.assert_produces_warning(None):
|
||||
getattr(ts, attr)
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:The 'freq' argument:FutureWarning")
|
||||
@pytest.mark.filterwarnings("ignore:Timestamp.freq is deprecated:FutureWarning")
|
||||
def test_properties_business(self):
|
||||
ts = Timestamp("2017-10-01", freq="B")
|
||||
control = Timestamp("2017-10-01")
|
||||
assert ts.dayofweek == 6
|
||||
assert ts.day_of_week == 6
|
||||
assert not ts.is_month_start # not a weekday
|
||||
assert not ts.freq.is_month_start(ts)
|
||||
assert ts.freq.is_month_start(ts + Timedelta(days=1))
|
||||
assert not ts.is_quarter_start # not a weekday
|
||||
assert not ts.freq.is_quarter_start(ts)
|
||||
assert ts.freq.is_quarter_start(ts + Timedelta(days=1))
|
||||
# Control case: non-business is month/qtr start
|
||||
assert control.is_month_start
|
||||
assert control.is_quarter_start
|
||||
|
||||
ts = Timestamp("2017-09-30", freq="B")
|
||||
control = Timestamp("2017-09-30")
|
||||
assert ts.dayofweek == 5
|
||||
assert ts.day_of_week == 5
|
||||
assert not ts.is_month_end # not a weekday
|
||||
assert not ts.freq.is_month_end(ts)
|
||||
assert ts.freq.is_month_end(ts - Timedelta(days=1))
|
||||
assert not ts.is_quarter_end # not a weekday
|
||||
assert not ts.freq.is_quarter_end(ts)
|
||||
assert ts.freq.is_quarter_end(ts - Timedelta(days=1))
|
||||
# Control case: non-business is month/qtr start
|
||||
assert control.is_month_end
|
||||
assert control.is_quarter_end
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attr, expected",
|
||||
[
|
||||
["year", 2014],
|
||||
["month", 12],
|
||||
["day", 31],
|
||||
["hour", 23],
|
||||
["minute", 59],
|
||||
["second", 0],
|
||||
["microsecond", 0],
|
||||
["nanosecond", 0],
|
||||
["dayofweek", 2],
|
||||
["day_of_week", 2],
|
||||
["quarter", 4],
|
||||
["dayofyear", 365],
|
||||
["day_of_year", 365],
|
||||
["week", 1],
|
||||
["daysinmonth", 31],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
||||
def test_fields(self, attr, expected, tz):
|
||||
# GH 10050
|
||||
# GH 13303
|
||||
ts = Timestamp("2014-12-31 23:59:00", tz=tz)
|
||||
result = getattr(ts, attr)
|
||||
# that we are int like
|
||||
assert isinstance(result, int)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
||||
def test_millisecond_raises(self, tz):
|
||||
ts = Timestamp("2014-12-31 23:59:00", tz=tz)
|
||||
msg = "'Timestamp' object has no attribute 'millisecond'"
|
||||
with pytest.raises(AttributeError, match=msg):
|
||||
ts.millisecond
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"start", ["is_month_start", "is_quarter_start", "is_year_start"]
|
||||
)
|
||||
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
||||
def test_is_start(self, start, tz):
|
||||
ts = Timestamp("2014-01-01 00:00:00", tz=tz)
|
||||
assert getattr(ts, start)
|
||||
|
||||
@pytest.mark.parametrize("end", ["is_month_end", "is_year_end", "is_quarter_end"])
|
||||
@pytest.mark.parametrize("tz", [None, "US/Eastern"])
|
||||
def test_is_end(self, end, tz):
|
||||
ts = Timestamp("2014-12-31 23:59:59", tz=tz)
|
||||
assert getattr(ts, end)
|
||||
|
||||
# GH 12806
|
||||
@pytest.mark.parametrize(
|
||||
"data",
|
||||
[Timestamp("2017-08-28 23:00:00"), Timestamp("2017-08-28 23:00:00", tz="EST")],
|
||||
)
|
||||
# error: Unsupported operand types for + ("List[None]" and "List[str]")
|
||||
@pytest.mark.parametrize(
|
||||
"time_locale", [None] + (tm.get_locales() or []) # type: ignore[operator]
|
||||
)
|
||||
def test_names(self, data, time_locale):
|
||||
# GH 17354
|
||||
# Test .day_name(), .month_name
|
||||
if time_locale is None:
|
||||
expected_day = "Monday"
|
||||
expected_month = "August"
|
||||
else:
|
||||
with tm.set_locale(time_locale, locale.LC_TIME):
|
||||
expected_day = calendar.day_name[0].capitalize()
|
||||
expected_month = calendar.month_name[8].capitalize()
|
||||
|
||||
result_day = data.day_name(time_locale)
|
||||
result_month = data.month_name(time_locale)
|
||||
|
||||
# Work around https://github.com/pandas-dev/pandas/issues/22342
|
||||
# different normalizations
|
||||
expected_day = unicodedata.normalize("NFD", expected_day)
|
||||
expected_month = unicodedata.normalize("NFD", expected_month)
|
||||
|
||||
result_day = unicodedata.normalize("NFD", result_day)
|
||||
result_month = unicodedata.normalize("NFD", result_month)
|
||||
|
||||
assert result_day == expected_day
|
||||
assert result_month == expected_month
|
||||
|
||||
# Test NaT
|
||||
nan_ts = Timestamp(NaT)
|
||||
assert np.isnan(nan_ts.day_name(time_locale))
|
||||
assert np.isnan(nan_ts.month_name(time_locale))
|
||||
|
||||
def test_is_leap_year(self, tz_naive_fixture):
|
||||
tz = tz_naive_fixture
|
||||
# GH 13727
|
||||
dt = Timestamp("2000-01-01 00:00:00", tz=tz)
|
||||
assert dt.is_leap_year
|
||||
assert isinstance(dt.is_leap_year, bool)
|
||||
|
||||
dt = Timestamp("1999-01-01 00:00:00", tz=tz)
|
||||
assert not dt.is_leap_year
|
||||
|
||||
dt = Timestamp("2004-01-01 00:00:00", tz=tz)
|
||||
assert dt.is_leap_year
|
||||
|
||||
dt = Timestamp("2100-01-01 00:00:00", tz=tz)
|
||||
assert not dt.is_leap_year
|
||||
|
||||
def test_woy_boundary(self):
|
||||
# make sure weeks at year boundaries are correct
|
||||
d = datetime(2013, 12, 31)
|
||||
result = Timestamp(d).week
|
||||
expected = 1 # ISO standard
|
||||
assert result == expected
|
||||
|
||||
d = datetime(2008, 12, 28)
|
||||
result = Timestamp(d).week
|
||||
expected = 52 # ISO standard
|
||||
assert result == expected
|
||||
|
||||
d = datetime(2009, 12, 31)
|
||||
result = Timestamp(d).week
|
||||
expected = 53 # ISO standard
|
||||
assert result == expected
|
||||
|
||||
d = datetime(2010, 1, 1)
|
||||
result = Timestamp(d).week
|
||||
expected = 53 # ISO standard
|
||||
assert result == expected
|
||||
|
||||
d = datetime(2010, 1, 3)
|
||||
result = Timestamp(d).week
|
||||
expected = 53 # ISO standard
|
||||
assert result == expected
|
||||
|
||||
result = np.array(
|
||||
[
|
||||
Timestamp(datetime(*args)).week
|
||||
for args in [(2000, 1, 1), (2000, 1, 2), (2005, 1, 1), (2005, 1, 2)]
|
||||
]
|
||||
)
|
||||
assert (result == [52, 52, 53, 53]).all()
|
||||
|
||||
def test_resolution(self):
|
||||
# GH#21336, GH#21365
|
||||
dt = Timestamp("2100-01-01 00:00:00")
|
||||
assert dt.resolution == Timedelta(nanoseconds=1)
|
||||
|
||||
# Check that the attribute is available on the class, mirroring
|
||||
# the stdlib datetime behavior
|
||||
assert Timestamp.resolution == Timedelta(nanoseconds=1)
|
||||
|
||||
|
||||
class TestTimestamp:
|
||||
def test_tz(self):
|
||||
tstr = "2014-02-01 09:00"
|
||||
ts = Timestamp(tstr)
|
||||
local = ts.tz_localize("Asia/Tokyo")
|
||||
assert local.hour == 9
|
||||
assert local == Timestamp(tstr, tz="Asia/Tokyo")
|
||||
conv = local.tz_convert("US/Eastern")
|
||||
assert conv == Timestamp("2014-01-31 19:00", tz="US/Eastern")
|
||||
assert conv.hour == 19
|
||||
|
||||
# preserves nanosecond
|
||||
ts = Timestamp(tstr) + offsets.Nano(5)
|
||||
local = ts.tz_localize("Asia/Tokyo")
|
||||
assert local.hour == 9
|
||||
assert local.nanosecond == 5
|
||||
conv = local.tz_convert("US/Eastern")
|
||||
assert conv.nanosecond == 5
|
||||
assert conv.hour == 19
|
||||
|
||||
def test_utc_z_designator(self):
|
||||
assert get_timezone(Timestamp("2014-11-02 01:00Z").tzinfo) is utc
|
||||
|
||||
def test_asm8(self):
|
||||
np.random.seed(7_960_929)
|
||||
ns = [Timestamp.min.value, Timestamp.max.value, 1000]
|
||||
|
||||
for n in ns:
|
||||
assert (
|
||||
Timestamp(n).asm8.view("i8") == np.datetime64(n, "ns").view("i8") == n
|
||||
)
|
||||
|
||||
assert Timestamp("nat").asm8.view("i8") == np.datetime64("nat", "ns").view("i8")
|
||||
|
||||
def test_class_ops_pytz(self):
|
||||
def compare(x, y):
|
||||
assert int((Timestamp(x).value - Timestamp(y).value) / 1e9) == 0
|
||||
|
||||
compare(Timestamp.now(), datetime.now())
|
||||
compare(Timestamp.now("UTC"), datetime.now(timezone("UTC")))
|
||||
compare(Timestamp.utcnow(), datetime.utcnow())
|
||||
compare(Timestamp.today(), datetime.today())
|
||||
current_time = calendar.timegm(datetime.now().utctimetuple())
|
||||
msg = "timezone-aware Timestamp with UTC"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
# GH#22451
|
||||
ts_utc = Timestamp.utcfromtimestamp(current_time)
|
||||
compare(
|
||||
ts_utc,
|
||||
datetime.utcfromtimestamp(current_time),
|
||||
)
|
||||
compare(
|
||||
Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
|
||||
)
|
||||
compare(
|
||||
# Support tz kwarg in Timestamp.fromtimestamp
|
||||
Timestamp.fromtimestamp(current_time, "UTC"),
|
||||
datetime.fromtimestamp(current_time, utc),
|
||||
)
|
||||
compare(
|
||||
# Support tz kwarg in Timestamp.fromtimestamp
|
||||
Timestamp.fromtimestamp(current_time, tz="UTC"),
|
||||
datetime.fromtimestamp(current_time, utc),
|
||||
)
|
||||
|
||||
date_component = datetime.utcnow()
|
||||
time_component = (date_component + timedelta(minutes=10)).time()
|
||||
compare(
|
||||
Timestamp.combine(date_component, time_component),
|
||||
datetime.combine(date_component, time_component),
|
||||
)
|
||||
|
||||
def test_class_ops_dateutil(self):
|
||||
def compare(x, y):
|
||||
assert (
|
||||
int(
|
||||
np.round(Timestamp(x).value / 1e9)
|
||||
- np.round(Timestamp(y).value / 1e9)
|
||||
)
|
||||
== 0
|
||||
)
|
||||
|
||||
compare(Timestamp.now(), datetime.now())
|
||||
compare(Timestamp.now("UTC"), datetime.now(tzutc()))
|
||||
compare(Timestamp.utcnow(), datetime.utcnow())
|
||||
compare(Timestamp.today(), datetime.today())
|
||||
current_time = calendar.timegm(datetime.now().utctimetuple())
|
||||
|
||||
msg = "timezone-aware Timestamp with UTC"
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
# GH#22451
|
||||
ts_utc = Timestamp.utcfromtimestamp(current_time)
|
||||
|
||||
compare(
|
||||
ts_utc,
|
||||
datetime.utcfromtimestamp(current_time),
|
||||
)
|
||||
compare(
|
||||
Timestamp.fromtimestamp(current_time), datetime.fromtimestamp(current_time)
|
||||
)
|
||||
|
||||
date_component = datetime.utcnow()
|
||||
time_component = (date_component + timedelta(minutes=10)).time()
|
||||
compare(
|
||||
Timestamp.combine(date_component, time_component),
|
||||
datetime.combine(date_component, time_component),
|
||||
)
|
||||
|
||||
def test_basics_nanos(self):
|
||||
val = np.int64(946_684_800_000_000_000).view("M8[ns]")
|
||||
stamp = Timestamp(val.view("i8") + 500)
|
||||
assert stamp.year == 2000
|
||||
assert stamp.month == 1
|
||||
assert stamp.microsecond == 0
|
||||
assert stamp.nanosecond == 500
|
||||
|
||||
# GH 14415
|
||||
val = np.iinfo(np.int64).min + 80_000_000_000_000
|
||||
stamp = Timestamp(val)
|
||||
assert stamp.year == 1677
|
||||
assert stamp.month == 9
|
||||
assert stamp.day == 21
|
||||
assert stamp.microsecond == 145224
|
||||
assert stamp.nanosecond == 192
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value, check_kwargs",
|
||||
[
|
||||
[946688461000000000, {}],
|
||||
[946688461000000000 / 1000, {"unit": "us"}],
|
||||
[946688461000000000 / 1_000_000, {"unit": "ms"}],
|
||||
[946688461000000000 / 1_000_000_000, {"unit": "s"}],
|
||||
[10957, {"unit": "D", "h": 0}],
|
||||
[
|
||||
(946688461000000000 + 500000) / 1000000000,
|
||||
{"unit": "s", "us": 499, "ns": 964},
|
||||
],
|
||||
[
|
||||
(946688461000000000 + 500000000) / 1000000000,
|
||||
{"unit": "s", "us": 500000},
|
||||
],
|
||||
[(946688461000000000 + 500000) / 1000000, {"unit": "ms", "us": 500}],
|
||||
[(946688461000000000 + 500000) / 1000, {"unit": "us", "us": 500}],
|
||||
[(946688461000000000 + 500000000) / 1000000, {"unit": "ms", "us": 500000}],
|
||||
[946688461000000000 / 1000.0 + 5, {"unit": "us", "us": 5}],
|
||||
[946688461000000000 / 1000.0 + 5000, {"unit": "us", "us": 5000}],
|
||||
[946688461000000000 / 1000000.0 + 0.5, {"unit": "ms", "us": 500}],
|
||||
[946688461000000000 / 1000000.0 + 0.005, {"unit": "ms", "us": 5, "ns": 5}],
|
||||
[946688461000000000 / 1000000000.0 + 0.5, {"unit": "s", "us": 500000}],
|
||||
[10957 + 0.5, {"unit": "D", "h": 12}],
|
||||
],
|
||||
)
|
||||
def test_unit(self, value, check_kwargs):
|
||||
def check(value, unit=None, h=1, s=1, us=0, ns=0):
|
||||
stamp = Timestamp(value, unit=unit)
|
||||
assert stamp.year == 2000
|
||||
assert stamp.month == 1
|
||||
assert stamp.day == 1
|
||||
assert stamp.hour == h
|
||||
if unit != "D":
|
||||
assert stamp.minute == 1
|
||||
assert stamp.second == s
|
||||
assert stamp.microsecond == us
|
||||
else:
|
||||
assert stamp.minute == 0
|
||||
assert stamp.second == 0
|
||||
assert stamp.microsecond == 0
|
||||
assert stamp.nanosecond == ns
|
||||
|
||||
check(value, **check_kwargs)
|
||||
|
||||
def test_roundtrip(self):
|
||||
|
||||
# test value to string and back conversions
|
||||
# further test accessors
|
||||
base = Timestamp("20140101 00:00:00")
|
||||
|
||||
result = Timestamp(base.value + Timedelta("5ms").value)
|
||||
assert result == Timestamp(f"{base}.005000")
|
||||
assert result.microsecond == 5000
|
||||
|
||||
result = Timestamp(base.value + Timedelta("5us").value)
|
||||
assert result == Timestamp(f"{base}.000005")
|
||||
assert result.microsecond == 5
|
||||
|
||||
result = Timestamp(base.value + Timedelta("5ns").value)
|
||||
assert result == Timestamp(f"{base}.000000005")
|
||||
assert result.nanosecond == 5
|
||||
assert result.microsecond == 0
|
||||
|
||||
result = Timestamp(base.value + Timedelta("6ms 5us").value)
|
||||
assert result == Timestamp(f"{base}.006005")
|
||||
assert result.microsecond == 5 + 6 * 1000
|
||||
|
||||
result = Timestamp(base.value + Timedelta("200ms 5us").value)
|
||||
assert result == Timestamp(f"{base}.200005")
|
||||
assert result.microsecond == 5 + 200 * 1000
|
||||
|
||||
def test_hash_equivalent(self):
|
||||
d = {datetime(2011, 1, 1): 5}
|
||||
stamp = Timestamp(datetime(2011, 1, 1))
|
||||
assert d[stamp] == 5
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"timezone, year, month, day, hour",
|
||||
[["America/Chicago", 2013, 11, 3, 1], ["America/Santiago", 2021, 4, 3, 23]],
|
||||
)
|
||||
def test_hash_timestamp_with_fold(self, timezone, year, month, day, hour):
|
||||
# see gh-33931
|
||||
test_timezone = gettz(timezone)
|
||||
transition_1 = Timestamp(
|
||||
year=year,
|
||||
month=month,
|
||||
day=day,
|
||||
hour=hour,
|
||||
minute=0,
|
||||
fold=0,
|
||||
tzinfo=test_timezone,
|
||||
)
|
||||
transition_2 = Timestamp(
|
||||
year=year,
|
||||
month=month,
|
||||
day=day,
|
||||
hour=hour,
|
||||
minute=0,
|
||||
fold=1,
|
||||
tzinfo=test_timezone,
|
||||
)
|
||||
assert hash(transition_1) == hash(transition_2)
|
||||
|
||||
def test_tz_conversion_freq(self, tz_naive_fixture):
|
||||
# GH25241
|
||||
with tm.assert_produces_warning(FutureWarning, match="freq"):
|
||||
t1 = Timestamp("2019-01-01 10:00", freq="H")
|
||||
assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq
|
||||
with tm.assert_produces_warning(FutureWarning, match="freq"):
|
||||
t2 = Timestamp("2019-01-02 12:00", tz="UTC", freq="T")
|
||||
assert t2.tz_convert(tz="UTC").freq == t2.freq
|
||||
|
||||
def test_pickle_freq_no_warning(self):
|
||||
# GH#41949 we don't want a warning on unpickling
|
||||
with tm.assert_produces_warning(FutureWarning, match="freq"):
|
||||
ts = Timestamp("2019-01-01 10:00", freq="H")
|
||||
|
||||
out = pickle.dumps(ts)
|
||||
with tm.assert_produces_warning(None):
|
||||
res = pickle.loads(out)
|
||||
|
||||
assert res._freq == ts._freq
|
||||
|
||||
|
||||
class TestTimestampNsOperations:
|
||||
def test_nanosecond_string_parsing(self):
|
||||
ts = Timestamp("2013-05-01 07:15:45.123456789")
|
||||
# GH 7878
|
||||
expected_repr = "2013-05-01 07:15:45.123456789"
|
||||
expected_value = 1_367_392_545_123_456_789
|
||||
assert ts.value == expected_value
|
||||
assert expected_repr in repr(ts)
|
||||
|
||||
ts = Timestamp("2013-05-01 07:15:45.123456789+09:00", tz="Asia/Tokyo")
|
||||
assert ts.value == expected_value - 9 * 3600 * 1_000_000_000
|
||||
assert expected_repr in repr(ts)
|
||||
|
||||
ts = Timestamp("2013-05-01 07:15:45.123456789", tz="UTC")
|
||||
assert ts.value == expected_value
|
||||
assert expected_repr in repr(ts)
|
||||
|
||||
ts = Timestamp("2013-05-01 07:15:45.123456789", tz="US/Eastern")
|
||||
assert ts.value == expected_value + 4 * 3600 * 1_000_000_000
|
||||
assert expected_repr in repr(ts)
|
||||
|
||||
# GH 10041
|
||||
ts = Timestamp("20130501T071545.123456789")
|
||||
assert ts.value == expected_value
|
||||
assert expected_repr in repr(ts)
|
||||
|
||||
def test_nanosecond_timestamp(self):
|
||||
# GH 7610
|
||||
expected = 1_293_840_000_000_000_005
|
||||
t = Timestamp("2011-01-01") + offsets.Nano(5)
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 5
|
||||
|
||||
t = Timestamp(t)
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 5
|
||||
|
||||
t = Timestamp("2011-01-01 00:00:00.000000005")
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000005')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 5
|
||||
|
||||
expected = 1_293_840_000_000_000_010
|
||||
t = t + offsets.Nano(5)
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 10
|
||||
|
||||
t = Timestamp(t)
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 10
|
||||
|
||||
t = Timestamp("2011-01-01 00:00:00.000000010")
|
||||
assert repr(t) == "Timestamp('2011-01-01 00:00:00.000000010')"
|
||||
assert t.value == expected
|
||||
assert t.nanosecond == 10
|
||||
|
||||
|
||||
class TestTimestampToJulianDate:
|
||||
def test_compare_1700(self):
|
||||
r = Timestamp("1700-06-23").to_julian_date()
|
||||
assert r == 2_342_145.5
|
||||
|
||||
def test_compare_2000(self):
|
||||
r = Timestamp("2000-04-12").to_julian_date()
|
||||
assert r == 2_451_646.5
|
||||
|
||||
def test_compare_2100(self):
|
||||
r = Timestamp("2100-08-12").to_julian_date()
|
||||
assert r == 2_488_292.5
|
||||
|
||||
def test_compare_hour01(self):
|
||||
r = Timestamp("2000-08-12T01:00:00").to_julian_date()
|
||||
assert r == 2_451_768.5416666666666666
|
||||
|
||||
def test_compare_hour13(self):
|
||||
r = Timestamp("2000-08-12T13:00:00").to_julian_date()
|
||||
assert r == 2_451_769.0416666666666666
|
||||
|
||||
|
||||
class TestTimestampConversion:
|
||||
def test_conversion(self):
|
||||
# GH#9255
|
||||
ts = Timestamp("2000-01-01")
|
||||
|
||||
result = ts.to_pydatetime()
|
||||
expected = datetime(2000, 1, 1)
|
||||
assert result == expected
|
||||
assert type(result) == type(expected)
|
||||
|
||||
result = ts.to_datetime64()
|
||||
expected = np.datetime64(ts.value, "ns")
|
||||
assert result == expected
|
||||
assert type(result) == type(expected)
|
||||
assert result.dtype == expected.dtype
|
||||
|
||||
def test_to_pydatetime_fold(self):
|
||||
# GH#45087
|
||||
tzstr = "dateutil/usr/share/zoneinfo/America/Chicago"
|
||||
ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, tz=tzstr)
|
||||
dt = ts.to_pydatetime()
|
||||
assert dt.fold == 1
|
||||
|
||||
def test_to_pydatetime_nonzero_nano(self):
|
||||
ts = Timestamp("2011-01-01 9:00:00.123456789")
|
||||
|
||||
# Warn the user of data loss (nanoseconds).
|
||||
with tm.assert_produces_warning(UserWarning):
|
||||
expected = datetime(2011, 1, 1, 9, 0, 0, 123456)
|
||||
result = ts.to_pydatetime()
|
||||
assert result == expected
|
||||
|
||||
def test_timestamp_to_datetime(self):
|
||||
stamp = Timestamp("20090415", tz="US/Eastern")
|
||||
dtval = stamp.to_pydatetime()
|
||||
assert stamp == dtval
|
||||
assert stamp.tzinfo == dtval.tzinfo
|
||||
|
||||
def test_timestamp_to_datetime_dateutil(self):
|
||||
stamp = Timestamp("20090415", tz="dateutil/US/Eastern")
|
||||
dtval = stamp.to_pydatetime()
|
||||
assert stamp == dtval
|
||||
assert stamp.tzinfo == dtval.tzinfo
|
||||
|
||||
def test_timestamp_to_datetime_explicit_pytz(self):
|
||||
stamp = Timestamp("20090415", tz=pytz.timezone("US/Eastern"))
|
||||
dtval = stamp.to_pydatetime()
|
||||
assert stamp == dtval
|
||||
assert stamp.tzinfo == dtval.tzinfo
|
||||
|
||||
@td.skip_if_windows
|
||||
def test_timestamp_to_datetime_explicit_dateutil(self):
|
||||
stamp = Timestamp("20090415", tz=gettz("US/Eastern"))
|
||||
dtval = stamp.to_pydatetime()
|
||||
assert stamp == dtval
|
||||
assert stamp.tzinfo == dtval.tzinfo
|
||||
|
||||
def test_to_datetime_bijective(self):
|
||||
# Ensure that converting to datetime and back only loses precision
|
||||
# by going from nanoseconds to microseconds.
|
||||
exp_warning = None if Timestamp.max.nanosecond == 0 else UserWarning
|
||||
with tm.assert_produces_warning(exp_warning):
|
||||
pydt_max = Timestamp.max.to_pydatetime()
|
||||
|
||||
assert Timestamp(pydt_max).value / 1000 == Timestamp.max.value / 1000
|
||||
|
||||
exp_warning = None if Timestamp.min.nanosecond == 0 else UserWarning
|
||||
with tm.assert_produces_warning(exp_warning):
|
||||
pydt_min = Timestamp.min.to_pydatetime()
|
||||
|
||||
# The next assertion can be enabled once GH#39221 is merged
|
||||
# assert pydt_min < Timestamp.min # this is bc nanos are dropped
|
||||
tdus = timedelta(microseconds=1)
|
||||
assert pydt_min + tdus > Timestamp.min
|
||||
|
||||
assert Timestamp(pydt_min + tdus).value / 1000 == Timestamp.min.value / 1000
|
||||
|
||||
def test_to_period_tz_warning(self):
|
||||
# GH#21333 make sure a warning is issued when timezone
|
||||
# info is lost
|
||||
ts = Timestamp("2009-04-15 16:17:18", tz="US/Eastern")
|
||||
with tm.assert_produces_warning(UserWarning):
|
||||
# warning that timezone info will be lost
|
||||
ts.to_period("D")
|
||||
|
||||
def test_to_numpy_alias(self):
|
||||
# GH 24653: alias .to_numpy() for scalars
|
||||
ts = Timestamp(datetime.now())
|
||||
assert ts.to_datetime64() == ts.to_numpy()
|
||||
|
||||
# GH#44460
|
||||
msg = "dtype and copy arguments are ignored"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.to_numpy("M8[s]")
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.to_numpy(copy=True)
|
||||
|
||||
|
||||
class SubDatetime(datetime):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lh,rh",
|
||||
[
|
||||
(SubDatetime(2000, 1, 1), Timedelta(hours=1)),
|
||||
(Timedelta(hours=1), SubDatetime(2000, 1, 1)),
|
||||
],
|
||||
)
|
||||
def test_dt_subclass_add_timedelta(lh, rh):
|
||||
# GH#25851
|
||||
# ensure that subclassed datetime works for
|
||||
# Timedelta operations
|
||||
result = lh + rh
|
||||
expected = SubDatetime(2000, 1, 1, 1)
|
||||
assert result == expected
|
||||
@@ -0,0 +1,455 @@
|
||||
"""
|
||||
Tests for Timestamp timezone-related methods
|
||||
"""
|
||||
from datetime import (
|
||||
date,
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
|
||||
import dateutil
|
||||
from dateutil.tz import (
|
||||
gettz,
|
||||
tzoffset,
|
||||
)
|
||||
import pytest
|
||||
import pytz
|
||||
from pytz.exceptions import (
|
||||
AmbiguousTimeError,
|
||||
NonExistentTimeError,
|
||||
)
|
||||
|
||||
from pandas._libs.tslibs import timezones
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
from pandas import (
|
||||
NaT,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
class TestTimestampTZOperations:
|
||||
# --------------------------------------------------------------
|
||||
# Timestamp.tz_localize
|
||||
|
||||
def test_tz_localize_pushes_out_of_bounds(self):
|
||||
# GH#12677
|
||||
# tz_localize that pushes away from the boundary is OK
|
||||
msg = (
|
||||
f"Converting {Timestamp.min.strftime('%Y-%m-%d %H:%M:%S')} "
|
||||
f"underflows past {Timestamp.min}"
|
||||
)
|
||||
pac = Timestamp.min.tz_localize("US/Pacific")
|
||||
assert pac.value > Timestamp.min.value
|
||||
pac.tz_convert("Asia/Tokyo") # tz_convert doesn't change value
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.min.tz_localize("Asia/Tokyo")
|
||||
|
||||
# tz_localize that pushes away from the boundary is OK
|
||||
msg = (
|
||||
f"Converting {Timestamp.max.strftime('%Y-%m-%d %H:%M:%S')} "
|
||||
f"overflows past {Timestamp.max}"
|
||||
)
|
||||
tokyo = Timestamp.max.tz_localize("Asia/Tokyo")
|
||||
assert tokyo.value < Timestamp.max.value
|
||||
tokyo.tz_convert("US/Pacific") # tz_convert doesn't change value
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.max.tz_localize("US/Pacific")
|
||||
|
||||
def test_tz_localize_ambiguous_bool(self):
|
||||
# make sure that we are correctly accepting bool values as ambiguous
|
||||
# GH#14402
|
||||
ts = Timestamp("2015-11-01 01:00:03")
|
||||
expected0 = Timestamp("2015-11-01 01:00:03-0500", tz="US/Central")
|
||||
expected1 = Timestamp("2015-11-01 01:00:03-0600", tz="US/Central")
|
||||
|
||||
msg = "Cannot infer dst time from 2015-11-01 01:00:03"
|
||||
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
||||
ts.tz_localize("US/Central")
|
||||
|
||||
result = ts.tz_localize("US/Central", ambiguous=True)
|
||||
assert result == expected0
|
||||
|
||||
result = ts.tz_localize("US/Central", ambiguous=False)
|
||||
assert result == expected1
|
||||
|
||||
def test_tz_localize_ambiguous(self):
|
||||
ts = Timestamp("2014-11-02 01:00")
|
||||
ts_dst = ts.tz_localize("US/Eastern", ambiguous=True)
|
||||
ts_no_dst = ts.tz_localize("US/Eastern", ambiguous=False)
|
||||
|
||||
assert (ts_no_dst.value - ts_dst.value) / 1e9 == 3600
|
||||
msg = "Cannot infer offset with only one time"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.tz_localize("US/Eastern", ambiguous="infer")
|
||||
|
||||
# GH#8025
|
||||
msg = "Cannot localize tz-aware Timestamp, use tz_convert for conversions"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp("2011-01-01", tz="US/Eastern").tz_localize("Asia/Tokyo")
|
||||
|
||||
msg = "Cannot convert tz-naive Timestamp, use tz_localize to localize"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
Timestamp("2011-01-01").tz_convert("Asia/Tokyo")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"stamp, tz",
|
||||
[
|
||||
("2015-03-08 02:00", "US/Eastern"),
|
||||
("2015-03-08 02:30", "US/Pacific"),
|
||||
("2015-03-29 02:00", "Europe/Paris"),
|
||||
("2015-03-29 02:30", "Europe/Belgrade"),
|
||||
],
|
||||
)
|
||||
def test_tz_localize_nonexistent(self, stamp, tz):
|
||||
# GH#13057
|
||||
ts = Timestamp(stamp)
|
||||
with pytest.raises(NonExistentTimeError, match=stamp):
|
||||
ts.tz_localize(tz)
|
||||
# GH 22644
|
||||
with pytest.raises(NonExistentTimeError, match=stamp):
|
||||
ts.tz_localize(tz, nonexistent="raise")
|
||||
assert ts.tz_localize(tz, nonexistent="NaT") is NaT
|
||||
|
||||
def test_tz_localize_ambiguous_raise(self):
|
||||
# GH#13057
|
||||
ts = Timestamp("2015-11-1 01:00")
|
||||
msg = "Cannot infer dst time from 2015-11-01 01:00:00,"
|
||||
with pytest.raises(AmbiguousTimeError, match=msg):
|
||||
ts.tz_localize("US/Pacific", ambiguous="raise")
|
||||
|
||||
def test_tz_localize_nonexistent_invalid_arg(self):
|
||||
# GH 22644
|
||||
tz = "Europe/Warsaw"
|
||||
ts = Timestamp("2015-03-29 02:00:00")
|
||||
msg = (
|
||||
"The nonexistent argument must be one of 'raise', 'NaT', "
|
||||
"'shift_forward', 'shift_backward' or a timedelta object"
|
||||
)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.tz_localize(tz, nonexistent="foo")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"stamp",
|
||||
[
|
||||
"2014-02-01 09:00",
|
||||
"2014-07-08 09:00",
|
||||
"2014-11-01 17:00",
|
||||
"2014-11-05 00:00",
|
||||
],
|
||||
)
|
||||
def test_tz_localize_roundtrip(self, stamp, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
ts = Timestamp(stamp)
|
||||
localized = ts.tz_localize(tz)
|
||||
assert localized == Timestamp(stamp, tz=tz)
|
||||
|
||||
msg = "Cannot localize tz-aware Timestamp"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
localized.tz_localize(tz)
|
||||
|
||||
reset = localized.tz_localize(None)
|
||||
assert reset == ts
|
||||
assert reset.tzinfo is None
|
||||
|
||||
def test_tz_localize_ambiguous_compat(self):
|
||||
# validate that pytz and dateutil are compat for dst
|
||||
# when the transition happens
|
||||
naive = Timestamp("2013-10-27 01:00:00")
|
||||
|
||||
pytz_zone = "Europe/London"
|
||||
dateutil_zone = "dateutil/Europe/London"
|
||||
result_pytz = naive.tz_localize(pytz_zone, ambiguous=0)
|
||||
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=0)
|
||||
assert result_pytz.value == result_dateutil.value
|
||||
assert result_pytz.value == 1382835600000000000
|
||||
|
||||
# fixed ambiguous behavior
|
||||
# see gh-14621, GH#45087
|
||||
assert result_pytz.to_pydatetime().tzname() == "GMT"
|
||||
assert result_dateutil.to_pydatetime().tzname() == "GMT"
|
||||
assert str(result_pytz) == str(result_dateutil)
|
||||
|
||||
# 1 hour difference
|
||||
result_pytz = naive.tz_localize(pytz_zone, ambiguous=1)
|
||||
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=1)
|
||||
assert result_pytz.value == result_dateutil.value
|
||||
assert result_pytz.value == 1382832000000000000
|
||||
|
||||
# see gh-14621
|
||||
assert str(result_pytz) == str(result_dateutil)
|
||||
assert (
|
||||
result_pytz.to_pydatetime().tzname()
|
||||
== result_dateutil.to_pydatetime().tzname()
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tz",
|
||||
[
|
||||
pytz.timezone("US/Eastern"),
|
||||
gettz("US/Eastern"),
|
||||
"US/Eastern",
|
||||
"dateutil/US/Eastern",
|
||||
],
|
||||
)
|
||||
def test_timestamp_tz_localize(self, tz):
|
||||
stamp = Timestamp("3/11/2012 04:00")
|
||||
|
||||
result = stamp.tz_localize(tz)
|
||||
expected = Timestamp("3/11/2012 04:00", tz=tz)
|
||||
assert result.hour == expected.hour
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"start_ts, tz, end_ts, shift",
|
||||
[
|
||||
["2015-03-29 02:20:00", "Europe/Warsaw", "2015-03-29 03:00:00", "forward"],
|
||||
[
|
||||
"2015-03-29 02:20:00",
|
||||
"Europe/Warsaw",
|
||||
"2015-03-29 01:59:59.999999999",
|
||||
"backward",
|
||||
],
|
||||
[
|
||||
"2015-03-29 02:20:00",
|
||||
"Europe/Warsaw",
|
||||
"2015-03-29 03:20:00",
|
||||
timedelta(hours=1),
|
||||
],
|
||||
[
|
||||
"2015-03-29 02:20:00",
|
||||
"Europe/Warsaw",
|
||||
"2015-03-29 01:20:00",
|
||||
timedelta(hours=-1),
|
||||
],
|
||||
["2018-03-11 02:33:00", "US/Pacific", "2018-03-11 03:00:00", "forward"],
|
||||
[
|
||||
"2018-03-11 02:33:00",
|
||||
"US/Pacific",
|
||||
"2018-03-11 01:59:59.999999999",
|
||||
"backward",
|
||||
],
|
||||
[
|
||||
"2018-03-11 02:33:00",
|
||||
"US/Pacific",
|
||||
"2018-03-11 03:33:00",
|
||||
timedelta(hours=1),
|
||||
],
|
||||
[
|
||||
"2018-03-11 02:33:00",
|
||||
"US/Pacific",
|
||||
"2018-03-11 01:33:00",
|
||||
timedelta(hours=-1),
|
||||
],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("tz_type", ["", "dateutil/"])
|
||||
def test_timestamp_tz_localize_nonexistent_shift(
|
||||
self, start_ts, tz, end_ts, shift, tz_type
|
||||
):
|
||||
# GH 8917, 24466
|
||||
tz = tz_type + tz
|
||||
if isinstance(shift, str):
|
||||
shift = "shift_" + shift
|
||||
ts = Timestamp(start_ts)
|
||||
result = ts.tz_localize(tz, nonexistent=shift)
|
||||
expected = Timestamp(end_ts).tz_localize(tz)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("offset", [-1, 1])
|
||||
@pytest.mark.parametrize("tz_type", ["", "dateutil/"])
|
||||
def test_timestamp_tz_localize_nonexistent_shift_invalid(self, offset, tz_type):
|
||||
# GH 8917, 24466
|
||||
tz = tz_type + "Europe/Warsaw"
|
||||
ts = Timestamp("2015-03-29 02:20:00")
|
||||
msg = "The provided timedelta will relocalize on a nonexistent time"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.tz_localize(tz, nonexistent=timedelta(seconds=offset))
|
||||
|
||||
@pytest.mark.parametrize("tz", ["Europe/Warsaw", "dateutil/Europe/Warsaw"])
|
||||
def test_timestamp_tz_localize_nonexistent_NaT(self, tz):
|
||||
# GH 8917
|
||||
ts = Timestamp("2015-03-29 02:20:00")
|
||||
result = ts.tz_localize(tz, nonexistent="NaT")
|
||||
assert result is NaT
|
||||
|
||||
@pytest.mark.parametrize("tz", ["Europe/Warsaw", "dateutil/Europe/Warsaw"])
|
||||
def test_timestamp_tz_localize_nonexistent_raise(self, tz):
|
||||
# GH 8917
|
||||
ts = Timestamp("2015-03-29 02:20:00")
|
||||
msg = "2015-03-29 02:20:00"
|
||||
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
||||
ts.tz_localize(tz, nonexistent="raise")
|
||||
msg = (
|
||||
"The nonexistent argument must be one of 'raise', 'NaT', "
|
||||
"'shift_forward', 'shift_backward' or a timedelta object"
|
||||
)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.tz_localize(tz, nonexistent="foo")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Timestamp.tz_convert
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"stamp",
|
||||
[
|
||||
"2014-02-01 09:00",
|
||||
"2014-07-08 09:00",
|
||||
"2014-11-01 17:00",
|
||||
"2014-11-05 00:00",
|
||||
],
|
||||
)
|
||||
def test_tz_convert_roundtrip(self, stamp, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
|
||||
ts = Timestamp(stamp, tz="UTC")
|
||||
converted = ts.tz_convert(tz)
|
||||
|
||||
reset = converted.tz_convert(None)
|
||||
assert reset == Timestamp(stamp)
|
||||
assert reset.tzinfo is None
|
||||
assert reset == converted.tz_convert("UTC").tz_localize(None)
|
||||
|
||||
@pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
|
||||
def test_astimezone(self, tzstr):
|
||||
# astimezone is an alias for tz_convert, so keep it with
|
||||
# the tz_convert tests
|
||||
utcdate = Timestamp("3/11/2012 22:00", tz="UTC")
|
||||
expected = utcdate.tz_convert(tzstr)
|
||||
result = utcdate.astimezone(tzstr)
|
||||
assert expected == result
|
||||
assert isinstance(result, Timestamp)
|
||||
|
||||
@td.skip_if_windows
|
||||
def test_tz_convert_utc_with_system_utc(self):
|
||||
|
||||
# from system utc to real utc
|
||||
ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
|
||||
# check that the time hasn't changed.
|
||||
assert ts == ts.tz_convert(dateutil.tz.tzutc())
|
||||
|
||||
# from system utc to real utc
|
||||
ts = Timestamp("2001-01-05 11:56", tz=timezones.maybe_get_tz("dateutil/UTC"))
|
||||
# check that the time hasn't changed.
|
||||
assert ts == ts.tz_convert(dateutil.tz.tzutc())
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Timestamp.__init__ with tz str or tzinfo
|
||||
|
||||
def test_timestamp_constructor_tz_utc(self):
|
||||
utc_stamp = Timestamp("3/11/2012 05:00", tz="utc")
|
||||
assert utc_stamp.tzinfo is pytz.utc
|
||||
assert utc_stamp.hour == 5
|
||||
|
||||
utc_stamp = Timestamp("3/11/2012 05:00").tz_localize("utc")
|
||||
assert utc_stamp.hour == 5
|
||||
|
||||
def test_timestamp_to_datetime_tzoffset(self):
|
||||
tzinfo = tzoffset(None, 7200)
|
||||
expected = Timestamp("3/11/2012 04:00", tz=tzinfo)
|
||||
result = Timestamp(expected.to_pydatetime())
|
||||
assert expected == result
|
||||
|
||||
def test_timestamp_constructor_near_dst_boundary(self):
|
||||
# GH#11481 & GH#15777
|
||||
# Naive string timestamps were being localized incorrectly
|
||||
# with tz_convert_from_utc_single instead of tz_localize_to_utc
|
||||
|
||||
for tz in ["Europe/Brussels", "Europe/Prague"]:
|
||||
result = Timestamp("2015-10-25 01:00", tz=tz)
|
||||
expected = Timestamp("2015-10-25 01:00").tz_localize(tz)
|
||||
assert result == expected
|
||||
|
||||
msg = "Cannot infer dst time from 2015-10-25 02:00:00"
|
||||
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
||||
Timestamp("2015-10-25 02:00", tz=tz)
|
||||
|
||||
result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
|
||||
expected = Timestamp("2017-03-26 01:00").tz_localize("Europe/Paris")
|
||||
assert result == expected
|
||||
|
||||
msg = "2017-03-26 02:00"
|
||||
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
||||
Timestamp("2017-03-26 02:00", tz="Europe/Paris")
|
||||
|
||||
# GH#11708
|
||||
naive = Timestamp("2015-11-18 10:00:00")
|
||||
result = naive.tz_localize("UTC").tz_convert("Asia/Kolkata")
|
||||
expected = Timestamp("2015-11-18 15:30:00+0530", tz="Asia/Kolkata")
|
||||
assert result == expected
|
||||
|
||||
# GH#15823
|
||||
result = Timestamp("2017-03-26 00:00", tz="Europe/Paris")
|
||||
expected = Timestamp("2017-03-26 00:00:00+0100", tz="Europe/Paris")
|
||||
assert result == expected
|
||||
|
||||
result = Timestamp("2017-03-26 01:00", tz="Europe/Paris")
|
||||
expected = Timestamp("2017-03-26 01:00:00+0100", tz="Europe/Paris")
|
||||
assert result == expected
|
||||
|
||||
msg = "2017-03-26 02:00"
|
||||
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
||||
Timestamp("2017-03-26 02:00", tz="Europe/Paris")
|
||||
|
||||
result = Timestamp("2017-03-26 02:00:00+0100", tz="Europe/Paris")
|
||||
naive = Timestamp(result.value)
|
||||
expected = naive.tz_localize("UTC").tz_convert("Europe/Paris")
|
||||
assert result == expected
|
||||
|
||||
result = Timestamp("2017-03-26 03:00", tz="Europe/Paris")
|
||||
expected = Timestamp("2017-03-26 03:00:00+0200", tz="Europe/Paris")
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tz",
|
||||
[
|
||||
pytz.timezone("US/Eastern"),
|
||||
gettz("US/Eastern"),
|
||||
"US/Eastern",
|
||||
"dateutil/US/Eastern",
|
||||
],
|
||||
)
|
||||
def test_timestamp_constructed_by_date_and_tz(self, tz):
|
||||
# GH#2993, Timestamp cannot be constructed by datetime.date
|
||||
# and tz correctly
|
||||
|
||||
result = Timestamp(date(2012, 3, 11), tz=tz)
|
||||
|
||||
expected = Timestamp("3/11/2012", tz=tz)
|
||||
assert result.hour == expected.hour
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tz",
|
||||
[
|
||||
pytz.timezone("US/Eastern"),
|
||||
gettz("US/Eastern"),
|
||||
"US/Eastern",
|
||||
"dateutil/US/Eastern",
|
||||
],
|
||||
)
|
||||
def test_timestamp_add_timedelta_push_over_dst_boundary(self, tz):
|
||||
# GH#1389
|
||||
|
||||
# 4 hours before DST transition
|
||||
stamp = Timestamp("3/10/2012 22:00", tz=tz)
|
||||
|
||||
result = stamp + timedelta(hours=6)
|
||||
|
||||
# spring forward, + "7" hours
|
||||
expected = Timestamp("3/11/2012 05:00", tz=tz)
|
||||
|
||||
assert result == expected
|
||||
|
||||
def test_timestamp_timetz_equivalent_with_datetime_tz(self, tz_naive_fixture):
|
||||
# GH21358
|
||||
tz = timezones.maybe_get_tz(tz_naive_fixture)
|
||||
|
||||
stamp = Timestamp("2018-06-04 10:20:30", tz=tz)
|
||||
_datetime = datetime(2018, 6, 4, hour=10, minute=20, second=30, tzinfo=tz)
|
||||
|
||||
result = stamp.timetz()
|
||||
expected = _datetime.timetz()
|
||||
|
||||
assert result == expected
|
||||
@@ -0,0 +1,525 @@
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.tz import gettz
|
||||
from hypothesis import (
|
||||
given,
|
||||
strategies as st,
|
||||
)
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
from pytz import utc
|
||||
|
||||
from pandas._libs import lib
|
||||
from pandas._libs.tslibs import (
|
||||
NaT,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
conversion,
|
||||
iNaT,
|
||||
to_offset,
|
||||
)
|
||||
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampUnaryOps:
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Timestamp.round
|
||||
@pytest.mark.parametrize(
|
||||
"timestamp, freq, expected",
|
||||
[
|
||||
("20130101 09:10:11", "D", "20130101"),
|
||||
("20130101 19:10:11", "D", "20130102"),
|
||||
("20130201 12:00:00", "D", "20130202"),
|
||||
("20130104 12:00:00", "D", "20130105"),
|
||||
("2000-01-05 05:09:15.13", "D", "2000-01-05 00:00:00"),
|
||||
("2000-01-05 05:09:15.13", "H", "2000-01-05 05:00:00"),
|
||||
("2000-01-05 05:09:15.13", "S", "2000-01-05 05:09:15"),
|
||||
],
|
||||
)
|
||||
def test_round_frequencies(self, timestamp, freq, expected):
|
||||
dt = Timestamp(timestamp)
|
||||
result = dt.round(freq)
|
||||
expected = Timestamp(expected)
|
||||
assert result == expected
|
||||
|
||||
def test_round_tzaware(self):
|
||||
dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
|
||||
result = dt.round("D")
|
||||
expected = Timestamp("20130101", tz="US/Eastern")
|
||||
assert result == expected
|
||||
|
||||
dt = Timestamp("20130101 09:10:11", tz="US/Eastern")
|
||||
result = dt.round("s")
|
||||
assert result == dt
|
||||
|
||||
def test_round_30min(self):
|
||||
# round
|
||||
dt = Timestamp("20130104 12:32:00")
|
||||
result = dt.round("30Min")
|
||||
expected = Timestamp("20130104 12:30:00")
|
||||
assert result == expected
|
||||
|
||||
def test_round_subsecond(self):
|
||||
# GH#14440 & GH#15578
|
||||
result = Timestamp("2016-10-17 12:00:00.0015").round("ms")
|
||||
expected = Timestamp("2016-10-17 12:00:00.002000")
|
||||
assert result == expected
|
||||
|
||||
result = Timestamp("2016-10-17 12:00:00.00149").round("ms")
|
||||
expected = Timestamp("2016-10-17 12:00:00.001000")
|
||||
assert result == expected
|
||||
|
||||
ts = Timestamp("2016-10-17 12:00:00.0015")
|
||||
for freq in ["us", "ns"]:
|
||||
assert ts == ts.round(freq)
|
||||
|
||||
result = Timestamp("2016-10-17 12:00:00.001501031").round("10ns")
|
||||
expected = Timestamp("2016-10-17 12:00:00.001501030")
|
||||
assert result == expected
|
||||
|
||||
def test_round_nonstandard_freq(self):
|
||||
with tm.assert_produces_warning(False):
|
||||
Timestamp("2016-10-17 12:00:00.001501031").round("1010ns")
|
||||
|
||||
def test_round_invalid_arg(self):
|
||||
stamp = Timestamp("2000-01-05 05:09:15.13")
|
||||
with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
|
||||
stamp.round("foo")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input, rounder, freq, expected",
|
||||
[
|
||||
("2117-01-01 00:00:45", "floor", "15s", "2117-01-01 00:00:45"),
|
||||
("2117-01-01 00:00:45", "ceil", "15s", "2117-01-01 00:00:45"),
|
||||
(
|
||||
"2117-01-01 00:00:45.000000012",
|
||||
"floor",
|
||||
"10ns",
|
||||
"2117-01-01 00:00:45.000000010",
|
||||
),
|
||||
(
|
||||
"1823-01-01 00:00:01.000000012",
|
||||
"ceil",
|
||||
"10ns",
|
||||
"1823-01-01 00:00:01.000000020",
|
||||
),
|
||||
("1823-01-01 00:00:01", "floor", "1s", "1823-01-01 00:00:01"),
|
||||
("1823-01-01 00:00:01", "ceil", "1s", "1823-01-01 00:00:01"),
|
||||
("NaT", "floor", "1s", "NaT"),
|
||||
("NaT", "ceil", "1s", "NaT"),
|
||||
],
|
||||
)
|
||||
def test_ceil_floor_edge(self, test_input, rounder, freq, expected):
|
||||
dt = Timestamp(test_input)
|
||||
func = getattr(dt, rounder)
|
||||
result = func(freq)
|
||||
|
||||
if dt is NaT:
|
||||
assert result is NaT
|
||||
else:
|
||||
expected = Timestamp(expected)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input, freq, expected",
|
||||
[
|
||||
("2018-01-01 00:02:06", "2s", "2018-01-01 00:02:06"),
|
||||
("2018-01-01 00:02:00", "2T", "2018-01-01 00:02:00"),
|
||||
("2018-01-01 00:04:00", "4T", "2018-01-01 00:04:00"),
|
||||
("2018-01-01 00:15:00", "15T", "2018-01-01 00:15:00"),
|
||||
("2018-01-01 00:20:00", "20T", "2018-01-01 00:20:00"),
|
||||
("2018-01-01 03:00:00", "3H", "2018-01-01 03:00:00"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("rounder", ["ceil", "floor", "round"])
|
||||
def test_round_minute_freq(self, test_input, freq, expected, rounder):
|
||||
# Ensure timestamps that shouldn't round dont!
|
||||
# GH#21262
|
||||
|
||||
dt = Timestamp(test_input)
|
||||
expected = Timestamp(expected)
|
||||
func = getattr(dt, rounder)
|
||||
result = func(freq)
|
||||
assert result == expected
|
||||
|
||||
def test_ceil(self):
|
||||
dt = Timestamp("20130101 09:10:11")
|
||||
result = dt.ceil("D")
|
||||
expected = Timestamp("20130102")
|
||||
assert result == expected
|
||||
|
||||
def test_floor(self):
|
||||
dt = Timestamp("20130101 09:10:11")
|
||||
result = dt.floor("D")
|
||||
expected = Timestamp("20130101")
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("method", ["ceil", "round", "floor"])
|
||||
def test_round_dst_border_ambiguous(self, method):
|
||||
# GH 18946 round near "fall back" DST
|
||||
ts = Timestamp("2017-10-29 00:00:00", tz="UTC").tz_convert("Europe/Madrid")
|
||||
#
|
||||
result = getattr(ts, method)("H", ambiguous=True)
|
||||
assert result == ts
|
||||
|
||||
result = getattr(ts, method)("H", ambiguous=False)
|
||||
expected = Timestamp("2017-10-29 01:00:00", tz="UTC").tz_convert(
|
||||
"Europe/Madrid"
|
||||
)
|
||||
assert result == expected
|
||||
|
||||
result = getattr(ts, method)("H", ambiguous="NaT")
|
||||
assert result is NaT
|
||||
|
||||
msg = "Cannot infer dst time"
|
||||
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
||||
getattr(ts, method)("H", ambiguous="raise")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method, ts_str, freq",
|
||||
[
|
||||
["ceil", "2018-03-11 01:59:00-0600", "5min"],
|
||||
["round", "2018-03-11 01:59:00-0600", "5min"],
|
||||
["floor", "2018-03-11 03:01:00-0500", "2H"],
|
||||
],
|
||||
)
|
||||
def test_round_dst_border_nonexistent(self, method, ts_str, freq):
|
||||
# GH 23324 round near "spring forward" DST
|
||||
ts = Timestamp(ts_str, tz="America/Chicago")
|
||||
result = getattr(ts, method)(freq, nonexistent="shift_forward")
|
||||
expected = Timestamp("2018-03-11 03:00:00", tz="America/Chicago")
|
||||
assert result == expected
|
||||
|
||||
result = getattr(ts, method)(freq, nonexistent="NaT")
|
||||
assert result is NaT
|
||||
|
||||
msg = "2018-03-11 02:00:00"
|
||||
with pytest.raises(pytz.NonExistentTimeError, match=msg):
|
||||
getattr(ts, method)(freq, nonexistent="raise")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"timestamp",
|
||||
[
|
||||
"2018-01-01 0:0:0.124999360",
|
||||
"2018-01-01 0:0:0.125000367",
|
||||
"2018-01-01 0:0:0.125500",
|
||||
"2018-01-01 0:0:0.126500",
|
||||
"2018-01-01 12:00:00",
|
||||
"2019-01-01 12:00:00",
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"freq",
|
||||
[
|
||||
"2ns",
|
||||
"3ns",
|
||||
"4ns",
|
||||
"5ns",
|
||||
"6ns",
|
||||
"7ns",
|
||||
"250ns",
|
||||
"500ns",
|
||||
"750ns",
|
||||
"1us",
|
||||
"19us",
|
||||
"250us",
|
||||
"500us",
|
||||
"750us",
|
||||
"1s",
|
||||
"2s",
|
||||
"3s",
|
||||
"1D",
|
||||
],
|
||||
)
|
||||
def test_round_int64(self, timestamp, freq):
|
||||
# check that all rounding modes are accurate to int64 precision
|
||||
# see GH#22591
|
||||
dt = Timestamp(timestamp)
|
||||
unit = to_offset(freq).nanos
|
||||
|
||||
# test floor
|
||||
result = dt.floor(freq)
|
||||
assert result.value % unit == 0, f"floor not a {freq} multiple"
|
||||
assert 0 <= dt.value - result.value < unit, "floor error"
|
||||
|
||||
# test ceil
|
||||
result = dt.ceil(freq)
|
||||
assert result.value % unit == 0, f"ceil not a {freq} multiple"
|
||||
assert 0 <= result.value - dt.value < unit, "ceil error"
|
||||
|
||||
# test round
|
||||
result = dt.round(freq)
|
||||
assert result.value % unit == 0, f"round not a {freq} multiple"
|
||||
assert abs(result.value - dt.value) <= unit // 2, "round error"
|
||||
if unit % 2 == 0 and abs(result.value - dt.value) == unit // 2:
|
||||
# round half to even
|
||||
assert result.value // unit % 2 == 0, "round half to even error"
|
||||
|
||||
def test_round_implementation_bounds(self):
|
||||
# See also: analogous test for Timedelta
|
||||
result = Timestamp.min.ceil("s")
|
||||
expected = Timestamp(1677, 9, 21, 0, 12, 44)
|
||||
assert result == expected
|
||||
|
||||
result = Timestamp.max.floor("s")
|
||||
expected = Timestamp.max - Timedelta(854775807)
|
||||
assert result == expected
|
||||
|
||||
with pytest.raises(OverflowError, match="value too large"):
|
||||
Timestamp.min.floor("s")
|
||||
|
||||
# the second message here shows up in windows builds
|
||||
msg = "|".join(
|
||||
["Python int too large to convert to C long", "int too big to convert"]
|
||||
)
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
Timestamp.max.ceil("s")
|
||||
|
||||
@given(val=st.integers(iNaT + 1, lib.i8max))
|
||||
@pytest.mark.parametrize(
|
||||
"method", [Timestamp.round, Timestamp.floor, Timestamp.ceil]
|
||||
)
|
||||
def test_round_sanity(self, val, method):
|
||||
val = np.int64(val)
|
||||
ts = Timestamp(val)
|
||||
|
||||
def checker(res, ts, nanos):
|
||||
if method is Timestamp.round:
|
||||
diff = np.abs((res - ts).value)
|
||||
assert diff <= nanos / 2
|
||||
elif method is Timestamp.floor:
|
||||
assert res <= ts
|
||||
elif method is Timestamp.ceil:
|
||||
assert res >= ts
|
||||
|
||||
assert method(ts, "ns") == ts
|
||||
|
||||
res = method(ts, "us")
|
||||
nanos = 1000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
res = method(ts, "ms")
|
||||
nanos = 1_000_000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
res = method(ts, "s")
|
||||
nanos = 1_000_000_000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
res = method(ts, "min")
|
||||
nanos = 60 * 1_000_000_000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
res = method(ts, "h")
|
||||
nanos = 60 * 60 * 1_000_000_000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
res = method(ts, "D")
|
||||
nanos = 24 * 60 * 60 * 1_000_000_000
|
||||
assert np.abs((res - ts).value) < nanos
|
||||
assert res.value % nanos == 0
|
||||
checker(res, ts, nanos)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Timestamp.replace
|
||||
|
||||
def test_replace_naive(self):
|
||||
# GH#14621, GH#7825
|
||||
ts = Timestamp("2016-01-01 09:00:00")
|
||||
result = ts.replace(hour=0)
|
||||
expected = Timestamp("2016-01-01 00:00:00")
|
||||
assert result == expected
|
||||
|
||||
def test_replace_aware(self, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
# GH#14621, GH#7825
|
||||
# replacing datetime components with and w/o presence of a timezone
|
||||
ts = Timestamp("2016-01-01 09:00:00", tz=tz)
|
||||
result = ts.replace(hour=0)
|
||||
expected = Timestamp("2016-01-01 00:00:00", tz=tz)
|
||||
assert result == expected
|
||||
|
||||
def test_replace_preserves_nanos(self, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
# GH#14621, GH#7825
|
||||
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
||||
result = ts.replace(hour=0)
|
||||
expected = Timestamp("2016-01-01 00:00:00.000000123", tz=tz)
|
||||
assert result == expected
|
||||
|
||||
def test_replace_multiple(self, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
# GH#14621, GH#7825
|
||||
# replacing datetime components with and w/o presence of a timezone
|
||||
# test all
|
||||
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
||||
result = ts.replace(
|
||||
year=2015,
|
||||
month=2,
|
||||
day=2,
|
||||
hour=0,
|
||||
minute=5,
|
||||
second=5,
|
||||
microsecond=5,
|
||||
nanosecond=5,
|
||||
)
|
||||
expected = Timestamp("2015-02-02 00:05:05.000005005", tz=tz)
|
||||
assert result == expected
|
||||
|
||||
def test_replace_invalid_kwarg(self, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
# GH#14621, GH#7825
|
||||
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
||||
msg = r"replace\(\) got an unexpected keyword argument"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
ts.replace(foo=5)
|
||||
|
||||
def test_replace_integer_args(self, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
# GH#14621, GH#7825
|
||||
ts = Timestamp("2016-01-01 09:00:00.000000123", tz=tz)
|
||||
msg = "value must be an integer, received <class 'float'> for hour"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.replace(hour=0.1)
|
||||
|
||||
def test_replace_tzinfo_equiv_tz_localize_none(self):
|
||||
# GH#14621, GH#7825
|
||||
# assert conversion to naive is the same as replacing tzinfo with None
|
||||
ts = Timestamp("2013-11-03 01:59:59.999999-0400", tz="US/Eastern")
|
||||
assert ts.tz_localize(None) == ts.replace(tzinfo=None)
|
||||
|
||||
@td.skip_if_windows
|
||||
def test_replace_tzinfo(self):
|
||||
# GH#15683
|
||||
dt = datetime(2016, 3, 27, 1)
|
||||
tzinfo = pytz.timezone("CET").localize(dt, is_dst=False).tzinfo
|
||||
|
||||
result_dt = dt.replace(tzinfo=tzinfo)
|
||||
result_pd = Timestamp(dt).replace(tzinfo=tzinfo)
|
||||
|
||||
# datetime.timestamp() converts in the local timezone
|
||||
with tm.set_timezone("UTC"):
|
||||
assert result_dt.timestamp() == result_pd.timestamp()
|
||||
|
||||
assert result_dt == result_pd
|
||||
assert result_dt == result_pd.to_pydatetime()
|
||||
|
||||
result_dt = dt.replace(tzinfo=tzinfo).replace(tzinfo=None)
|
||||
result_pd = Timestamp(dt).replace(tzinfo=tzinfo).replace(tzinfo=None)
|
||||
|
||||
# datetime.timestamp() converts in the local timezone
|
||||
with tm.set_timezone("UTC"):
|
||||
assert result_dt.timestamp() == result_pd.timestamp()
|
||||
|
||||
assert result_dt == result_pd
|
||||
assert result_dt == result_pd.to_pydatetime()
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tz, normalize",
|
||||
[
|
||||
(pytz.timezone("US/Eastern"), lambda x: x.tzinfo.normalize(x)),
|
||||
(gettz("US/Eastern"), lambda x: x),
|
||||
],
|
||||
)
|
||||
def test_replace_across_dst(self, tz, normalize):
|
||||
# GH#18319 check that 1) timezone is correctly normalized and
|
||||
# 2) that hour is not incorrectly changed by this normalization
|
||||
ts_naive = Timestamp("2017-12-03 16:03:30")
|
||||
ts_aware = conversion.localize_pydatetime(ts_naive, tz)
|
||||
|
||||
# Preliminary sanity-check
|
||||
assert ts_aware == normalize(ts_aware)
|
||||
|
||||
# Replace across DST boundary
|
||||
ts2 = ts_aware.replace(month=6)
|
||||
|
||||
# Check that `replace` preserves hour literal
|
||||
assert (ts2.hour, ts2.minute) == (ts_aware.hour, ts_aware.minute)
|
||||
|
||||
# Check that post-replace object is appropriately normalized
|
||||
ts2b = normalize(ts2)
|
||||
assert ts2 == ts2b
|
||||
|
||||
def test_replace_dst_border(self):
|
||||
# Gh 7825
|
||||
t = Timestamp("2013-11-3", tz="America/Chicago")
|
||||
result = t.replace(hour=3)
|
||||
expected = Timestamp("2013-11-3 03:00:00", tz="America/Chicago")
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize("fold", [0, 1])
|
||||
@pytest.mark.parametrize("tz", ["dateutil/Europe/London", "Europe/London"])
|
||||
def test_replace_dst_fold(self, fold, tz):
|
||||
# GH 25017
|
||||
d = datetime(2019, 10, 27, 2, 30)
|
||||
ts = Timestamp(d, tz=tz)
|
||||
result = ts.replace(hour=1, fold=fold)
|
||||
expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize(
|
||||
tz, ambiguous=not fold
|
||||
)
|
||||
assert result == expected
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# Timestamp.normalize
|
||||
|
||||
@pytest.mark.parametrize("arg", ["2013-11-30", "2013-11-30 12:00:00"])
|
||||
def test_normalize(self, tz_naive_fixture, arg):
|
||||
tz = tz_naive_fixture
|
||||
ts = Timestamp(arg, tz=tz)
|
||||
result = ts.normalize()
|
||||
expected = Timestamp("2013-11-30", tz=tz)
|
||||
assert result == expected
|
||||
|
||||
def test_normalize_pre_epoch_dates(self):
|
||||
# GH: 36294
|
||||
result = Timestamp("1969-01-01 09:00:00").normalize()
|
||||
expected = Timestamp("1969-01-01 00:00:00")
|
||||
assert result == expected
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
@td.skip_if_windows
|
||||
def test_timestamp(self, fixed_now_ts):
|
||||
# GH#17329
|
||||
# tz-naive --> treat it as if it were UTC for purposes of timestamp()
|
||||
ts = fixed_now_ts
|
||||
uts = ts.replace(tzinfo=utc)
|
||||
assert ts.timestamp() == uts.timestamp()
|
||||
|
||||
tsc = Timestamp("2014-10-11 11:00:01.12345678", tz="US/Central")
|
||||
utsc = tsc.tz_convert("UTC")
|
||||
|
||||
# utsc is a different representation of the same time
|
||||
assert tsc.timestamp() == utsc.timestamp()
|
||||
|
||||
# datetime.timestamp() converts in the local timezone
|
||||
with tm.set_timezone("UTC"):
|
||||
# should agree with datetime.timestamp method
|
||||
dt = ts.to_pydatetime()
|
||||
assert dt.timestamp() == ts.timestamp()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fold", [0, 1])
|
||||
def test_replace_preserves_fold(fold):
|
||||
# GH 37610. Check that replace preserves Timestamp fold property
|
||||
tz = gettz("Europe/Moscow")
|
||||
|
||||
ts = Timestamp(year=2009, month=10, day=25, hour=2, minute=30, fold=fold, tzinfo=tz)
|
||||
ts_replaced = ts.replace(second=1)
|
||||
|
||||
assert ts_replaced.fold == fold
|
||||
Reference in New Issue
Block a user