forked from Alsan/Post_finder
venv
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,192 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestIntervalArithmetic:
|
||||
def test_interval_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_interval_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_interval_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_interval_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_interval_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"
|
||||
|
||||
@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(self, 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(self, 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_timedelta_add_timestamp_interval(self, 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
|
||||
|
||||
|
||||
class TestIntervalComparisons:
|
||||
def test_interval_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_interval_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_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,51 @@
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Period,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
class TestIntervalConstructors:
|
||||
@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_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 tz_left is None or tz_right is None:
|
||||
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)
|
@ -0,0 +1,73 @@
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
class TestContains:
|
||||
def test_contains(self):
|
||||
interval = Interval(0, 1)
|
||||
assert 0.5 in interval
|
||||
assert 1 in interval
|
||||
assert 0 not in interval
|
||||
|
||||
interval_both = Interval(0, 1, "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_contains_interval(self, inclusive_endpoints_fixture):
|
||||
interval1 = Interval(0, 1, "both")
|
||||
interval2 = Interval(0, 1, inclusive_endpoints_fixture)
|
||||
assert interval1 in interval1
|
||||
assert interval2 in interval2
|
||||
assert interval2 in interval1
|
||||
assert interval1 not in interval2 or inclusive_endpoints_fixture == "both"
|
||||
|
||||
def test_contains_infinite_length(self):
|
||||
interval1 = Interval(0, 1, "both")
|
||||
interval2 = Interval(float("-inf"), float("inf"), "neither")
|
||||
assert interval1 in interval2
|
||||
assert interval2 not in interval1
|
||||
|
||||
def test_contains_zero_length(self):
|
||||
interval1 = Interval(0, 1, "both")
|
||||
interval2 = Interval(-1, -1, "both")
|
||||
interval3 = Interval(0.5, 0.5, "both")
|
||||
assert interval2 not in interval1
|
||||
assert interval3 in interval1
|
||||
assert interval2 not in interval3 and interval3 not in interval2
|
||||
assert interval1 not in interval2 and interval1 not in interval3
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"type1",
|
||||
[
|
||||
(0, 1),
|
||||
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
|
||||
(Timedelta("0h"), Timedelta("1h")),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"type2",
|
||||
[
|
||||
(0, 1),
|
||||
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
|
||||
(Timedelta("0h"), Timedelta("1h")),
|
||||
],
|
||||
)
|
||||
def test_contains_mixed_types(self, type1, type2):
|
||||
interval1 = Interval(*type1)
|
||||
interval2 = Interval(*type2)
|
||||
if type1 == type2:
|
||||
assert interval1 in interval2
|
||||
else:
|
||||
msg = "^'<=' not supported between instances of"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
interval1 in interval2
|
@ -0,0 +1,11 @@
|
||||
from pandas import Interval
|
||||
|
||||
|
||||
def test_interval_repr():
|
||||
interval = Interval(0, 1)
|
||||
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)"
|
@ -0,0 +1,87 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas import (
|
||||
Interval,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
|
||||
@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_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
|
@ -0,0 +1,67 @@
|
||||
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.
Binary file not shown.
@ -0,0 +1,486 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs.period import IncompatibleFrequency
|
||||
|
||||
from pandas import (
|
||||
NaT,
|
||||
Period,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
offsets,
|
||||
)
|
||||
|
||||
|
||||
class TestPeriodArithmetic:
|
||||
def test_add_overflow_raises(self):
|
||||
# GH#55503
|
||||
per = Timestamp.max.to_period("ns")
|
||||
|
||||
msg = "|".join(
|
||||
[
|
||||
"Python int too large to convert to C long",
|
||||
# windows, 32bit linux builds
|
||||
"int too big to convert",
|
||||
]
|
||||
)
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
per + 1
|
||||
|
||||
msg = "value too large"
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
per + Timedelta(1)
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
per + offsets.Nano(1)
|
||||
|
||||
def test_period_add_integer(self):
|
||||
per1 = Period(freq="D", year=2008, month=1, day=1)
|
||||
per2 = Period(freq="D", year=2008, month=1, day=2)
|
||||
assert per1 + 1 == per2
|
||||
assert 1 + per1 == per2
|
||||
|
||||
def test_period_add_invalid(self):
|
||||
# GH#4731
|
||||
per1 = Period(freq="D", year=2008, month=1, day=1)
|
||||
per2 = Period(freq="D", year=2008, month=1, day=2)
|
||||
|
||||
msg = "|".join(
|
||||
[
|
||||
r"unsupported operand type\(s\)",
|
||||
"can only concatenate str",
|
||||
"must be str, not Period",
|
||||
]
|
||||
)
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
per1 + "str"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
"str" + per1
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
per1 + per2
|
||||
|
||||
def test_period_sub_period_annual(self):
|
||||
left, right = Period("2011", freq="Y"), Period("2007", freq="Y")
|
||||
result = left - right
|
||||
assert result == 4 * right.freq
|
||||
|
||||
msg = r"Input has different freq=M from Period\(freq=Y-DEC\)"
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
left - Period("2007-01", freq="M")
|
||||
|
||||
def test_period_sub_period(self):
|
||||
per1 = Period("2011-01-01", freq="D")
|
||||
per2 = Period("2011-01-15", freq="D")
|
||||
|
||||
off = per1.freq
|
||||
assert per1 - per2 == -14 * off
|
||||
assert per2 - per1 == 14 * off
|
||||
|
||||
msg = r"Input has different freq=M from Period\(freq=D\)"
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per1 - Period("2011-02", freq="M")
|
||||
|
||||
@pytest.mark.parametrize("n", [1, 2, 3, 4])
|
||||
def test_sub_n_gt_1_ticks(self, tick_classes, n):
|
||||
# GH#23878
|
||||
p1 = Period("19910905", freq=tick_classes(n))
|
||||
p2 = Period("19920406", freq=tick_classes(n))
|
||||
|
||||
expected = Period(str(p2), freq=p2.freq.base) - Period(
|
||||
str(p1), freq=p1.freq.base
|
||||
)
|
||||
|
||||
assert (p2 - p1) == expected
|
||||
|
||||
@pytest.mark.parametrize("normalize", [True, False])
|
||||
@pytest.mark.parametrize("n", [1, 2, 3, 4])
|
||||
@pytest.mark.parametrize(
|
||||
"offset, kwd_name",
|
||||
[
|
||||
(offsets.YearEnd, "month"),
|
||||
(offsets.QuarterEnd, "startingMonth"),
|
||||
(offsets.MonthEnd, None),
|
||||
(offsets.Week, "weekday"),
|
||||
],
|
||||
)
|
||||
def test_sub_n_gt_1_offsets(self, offset, kwd_name, n, normalize):
|
||||
# GH#23878
|
||||
kwds = {kwd_name: 3} if kwd_name is not None else {}
|
||||
p1_d = "19910905"
|
||||
p2_d = "19920406"
|
||||
p1 = Period(p1_d, freq=offset(n, normalize, **kwds))
|
||||
p2 = Period(p2_d, freq=offset(n, normalize, **kwds))
|
||||
|
||||
expected = Period(p2_d, freq=p2.freq.base) - Period(p1_d, freq=p1.freq.base)
|
||||
|
||||
assert (p2 - p1) == expected
|
||||
|
||||
def test_period_add_offset(self):
|
||||
# freq is DateOffset
|
||||
for freq in ["Y", "2Y", "3Y"]:
|
||||
per = Period("2011", freq=freq)
|
||||
exp = Period("2013", freq=freq)
|
||||
assert per + offsets.YearEnd(2) == exp
|
||||
assert offsets.YearEnd(2) + per == exp
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(365, "D"),
|
||||
timedelta(365),
|
||||
]:
|
||||
msg = "Input has different freq|Input cannot be converted to Period"
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per + off
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
off + per
|
||||
|
||||
for freq in ["M", "2M", "3M"]:
|
||||
per = Period("2011-03", freq=freq)
|
||||
exp = Period("2011-05", freq=freq)
|
||||
assert per + offsets.MonthEnd(2) == exp
|
||||
assert offsets.MonthEnd(2) + per == exp
|
||||
|
||||
exp = Period("2012-03", freq=freq)
|
||||
assert per + offsets.MonthEnd(12) == exp
|
||||
assert offsets.MonthEnd(12) + per == exp
|
||||
|
||||
msg = "|".join(
|
||||
[
|
||||
"Input has different freq",
|
||||
"Input cannot be converted to Period",
|
||||
]
|
||||
)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(365, "D"),
|
||||
timedelta(365),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per + off
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
off + per
|
||||
|
||||
# freq is Tick
|
||||
for freq in ["D", "2D", "3D"]:
|
||||
per = Period("2011-04-01", freq=freq)
|
||||
|
||||
exp = Period("2011-04-06", freq=freq)
|
||||
assert per + offsets.Day(5) == exp
|
||||
assert offsets.Day(5) + per == exp
|
||||
|
||||
exp = Period("2011-04-02", freq=freq)
|
||||
assert per + offsets.Hour(24) == exp
|
||||
assert offsets.Hour(24) + per == exp
|
||||
|
||||
exp = Period("2011-04-03", freq=freq)
|
||||
assert per + np.timedelta64(2, "D") == exp
|
||||
assert np.timedelta64(2, "D") + per == exp
|
||||
|
||||
exp = Period("2011-04-02", freq=freq)
|
||||
assert per + np.timedelta64(3600 * 24, "s") == exp
|
||||
assert np.timedelta64(3600 * 24, "s") + per == exp
|
||||
|
||||
exp = Period("2011-03-30", freq=freq)
|
||||
assert per + timedelta(-2) == exp
|
||||
assert timedelta(-2) + per == exp
|
||||
|
||||
exp = Period("2011-04-03", freq=freq)
|
||||
assert per + timedelta(hours=48) == exp
|
||||
assert timedelta(hours=48) + per == exp
|
||||
|
||||
msg = "|".join(
|
||||
[
|
||||
"Input has different freq",
|
||||
"Input cannot be converted to Period",
|
||||
]
|
||||
)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(4, "h"),
|
||||
timedelta(hours=23),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per + off
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
off + per
|
||||
|
||||
for freq in ["h", "2h", "3h"]:
|
||||
per = Period("2011-04-01 09:00", freq=freq)
|
||||
|
||||
exp = Period("2011-04-03 09:00", freq=freq)
|
||||
assert per + offsets.Day(2) == exp
|
||||
assert offsets.Day(2) + per == exp
|
||||
|
||||
exp = Period("2011-04-01 12:00", freq=freq)
|
||||
assert per + offsets.Hour(3) == exp
|
||||
assert offsets.Hour(3) + per == exp
|
||||
|
||||
msg = "cannot use operands with types"
|
||||
exp = Period("2011-04-01 12:00", freq=freq)
|
||||
assert per + np.timedelta64(3, "h") == exp
|
||||
assert np.timedelta64(3, "h") + per == exp
|
||||
|
||||
exp = Period("2011-04-01 10:00", freq=freq)
|
||||
assert per + np.timedelta64(3600, "s") == exp
|
||||
assert np.timedelta64(3600, "s") + per == exp
|
||||
|
||||
exp = Period("2011-04-01 11:00", freq=freq)
|
||||
assert per + timedelta(minutes=120) == exp
|
||||
assert timedelta(minutes=120) + per == exp
|
||||
|
||||
exp = Period("2011-04-05 12:00", freq=freq)
|
||||
assert per + timedelta(days=4, minutes=180) == exp
|
||||
assert timedelta(days=4, minutes=180) + per == exp
|
||||
|
||||
msg = "|".join(
|
||||
[
|
||||
"Input has different freq",
|
||||
"Input cannot be converted to Period",
|
||||
]
|
||||
)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(3200, "s"),
|
||||
timedelta(hours=23, minutes=30),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per + off
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
off + per
|
||||
|
||||
def test_period_sub_offset(self):
|
||||
# freq is DateOffset
|
||||
msg = "|".join(
|
||||
[
|
||||
"Input has different freq",
|
||||
"Input cannot be converted to Period",
|
||||
]
|
||||
)
|
||||
|
||||
for freq in ["Y", "2Y", "3Y"]:
|
||||
per = Period("2011", freq=freq)
|
||||
assert per - offsets.YearEnd(2) == Period("2009", freq=freq)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(365, "D"),
|
||||
timedelta(365),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per - off
|
||||
|
||||
for freq in ["M", "2M", "3M"]:
|
||||
per = Period("2011-03", freq=freq)
|
||||
assert per - offsets.MonthEnd(2) == Period("2011-01", freq=freq)
|
||||
assert per - offsets.MonthEnd(12) == Period("2010-03", freq=freq)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(365, "D"),
|
||||
timedelta(365),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per - off
|
||||
|
||||
# freq is Tick
|
||||
for freq in ["D", "2D", "3D"]:
|
||||
per = Period("2011-04-01", freq=freq)
|
||||
assert per - offsets.Day(5) == Period("2011-03-27", freq=freq)
|
||||
assert per - offsets.Hour(24) == Period("2011-03-31", freq=freq)
|
||||
assert per - np.timedelta64(2, "D") == Period("2011-03-30", freq=freq)
|
||||
assert per - np.timedelta64(3600 * 24, "s") == Period(
|
||||
"2011-03-31", freq=freq
|
||||
)
|
||||
assert per - timedelta(-2) == Period("2011-04-03", freq=freq)
|
||||
assert per - timedelta(hours=48) == Period("2011-03-30", freq=freq)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(4, "h"),
|
||||
timedelta(hours=23),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per - off
|
||||
|
||||
for freq in ["h", "2h", "3h"]:
|
||||
per = Period("2011-04-01 09:00", freq=freq)
|
||||
assert per - offsets.Day(2) == Period("2011-03-30 09:00", freq=freq)
|
||||
assert per - offsets.Hour(3) == Period("2011-04-01 06:00", freq=freq)
|
||||
assert per - np.timedelta64(3, "h") == Period("2011-04-01 06:00", freq=freq)
|
||||
assert per - np.timedelta64(3600, "s") == Period(
|
||||
"2011-04-01 08:00", freq=freq
|
||||
)
|
||||
assert per - timedelta(minutes=120) == Period("2011-04-01 07:00", freq=freq)
|
||||
assert per - timedelta(days=4, minutes=180) == Period(
|
||||
"2011-03-28 06:00", freq=freq
|
||||
)
|
||||
|
||||
for off in [
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(3200, "s"),
|
||||
timedelta(hours=23, minutes=30),
|
||||
]:
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per - off
|
||||
|
||||
@pytest.mark.parametrize("freq", ["M", "2M", "3M"])
|
||||
def test_period_addsub_nat(self, freq):
|
||||
# GH#13071
|
||||
per = Period("2011-01", freq=freq)
|
||||
|
||||
# For subtraction, NaT is treated as another Period object
|
||||
assert NaT - per is NaT
|
||||
assert per - NaT is NaT
|
||||
|
||||
# For addition, NaT is treated as offset-like
|
||||
assert NaT + per is NaT
|
||||
assert per + NaT is NaT
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s", "m"])
|
||||
def test_period_add_sub_td64_nat(self, unit):
|
||||
# GH#47196
|
||||
per = Period("2022-06-01", "D")
|
||||
nat = np.timedelta64("NaT", unit)
|
||||
|
||||
assert per + nat is NaT
|
||||
assert nat + per is NaT
|
||||
assert per - nat is NaT
|
||||
|
||||
with pytest.raises(TypeError, match="unsupported operand"):
|
||||
nat - per
|
||||
|
||||
def test_period_ops_offset(self):
|
||||
per = Period("2011-04-01", freq="D")
|
||||
result = per + offsets.Day()
|
||||
exp = Period("2011-04-02", freq="D")
|
||||
assert result == exp
|
||||
|
||||
result = per - offsets.Day(2)
|
||||
exp = Period("2011-03-30", freq="D")
|
||||
assert result == exp
|
||||
|
||||
msg = r"Input cannot be converted to Period\(freq=D\)"
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per + offsets.Hour(2)
|
||||
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
per - offsets.Hour(2)
|
||||
|
||||
def test_period_add_timestamp_raises(self):
|
||||
# GH#17983
|
||||
ts = Timestamp("2017")
|
||||
per = Period("2017", freq="M")
|
||||
|
||||
msg = r"unsupported operand type\(s\) for \+: 'Timestamp' and 'Period'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
ts + per
|
||||
|
||||
msg = r"unsupported operand type\(s\) for \+: 'Period' and 'Timestamp'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
per + ts
|
||||
|
||||
|
||||
class TestPeriodComparisons:
|
||||
def test_period_comparison_same_freq(self):
|
||||
jan = Period("2000-01", "M")
|
||||
feb = Period("2000-02", "M")
|
||||
|
||||
assert not jan == feb
|
||||
assert jan != feb
|
||||
assert jan < feb
|
||||
assert jan <= feb
|
||||
assert not jan > feb
|
||||
assert not jan >= feb
|
||||
|
||||
def test_period_comparison_same_period_different_object(self):
|
||||
# Separate Period objects for the same period
|
||||
left = Period("2000-01", "M")
|
||||
right = Period("2000-01", "M")
|
||||
|
||||
assert left == right
|
||||
assert left >= right
|
||||
assert left <= right
|
||||
assert not left < right
|
||||
assert not left > right
|
||||
|
||||
def test_period_comparison_mismatched_freq(self):
|
||||
jan = Period("2000-01", "M")
|
||||
day = Period("2012-01-01", "D")
|
||||
|
||||
assert not jan == day
|
||||
assert jan != day
|
||||
msg = r"Input has different freq=D from Period\(freq=M\)"
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
jan < day
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
jan <= day
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
jan > day
|
||||
with pytest.raises(IncompatibleFrequency, match=msg):
|
||||
jan >= day
|
||||
|
||||
def test_period_comparison_invalid_type(self):
|
||||
jan = Period("2000-01", "M")
|
||||
|
||||
assert not jan == 1
|
||||
assert jan != 1
|
||||
|
||||
int_or_per = "'(Period|int)'"
|
||||
msg = f"not supported between instances of {int_or_per} and {int_or_per}"
|
||||
for left, right in [(jan, 1), (1, jan)]:
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left > right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left >= right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left < right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left <= right
|
||||
|
||||
def test_period_comparison_nat(self):
|
||||
per = Period("2011-01-01", freq="D")
|
||||
|
||||
ts = Timestamp("2011-01-01")
|
||||
# confirm Period('NaT') work identical with Timestamp('NaT')
|
||||
for left, right in [
|
||||
(NaT, per),
|
||||
(per, NaT),
|
||||
(NaT, ts),
|
||||
(ts, NaT),
|
||||
]:
|
||||
assert not left < right
|
||||
assert not left > right
|
||||
assert not left == right
|
||||
assert left != right
|
||||
assert not left <= right
|
||||
assert not left >= right
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"zerodim_arr, expected",
|
||||
((np.array(0), False), (np.array(Period("2000-01", "M")), True)),
|
||||
)
|
||||
def test_period_comparison_numpy_zerodim_arr(self, zerodim_arr, expected):
|
||||
per = Period("2000-01", "M")
|
||||
|
||||
assert (per == zerodim_arr) is expected
|
||||
assert (zerodim_arr == per) is expected
|
@ -0,0 +1,828 @@
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
|
||||
from pandas import (
|
||||
Period,
|
||||
Timestamp,
|
||||
offsets,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
bday_msg = "Period with BDay freq is deprecated"
|
||||
|
||||
|
||||
class TestFreqConversion:
|
||||
"""Test frequency conversion of date objects"""
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Period with BDay:FutureWarning")
|
||||
@pytest.mark.parametrize("freq", ["Y", "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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
per = Period("0001-01-01", freq="B")
|
||||
msg = "Out of bounds nanosecond timestamp"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
per.to_timestamp()
|
||||
|
||||
def test_asfreq_corner(self):
|
||||
val = Period(freq="Y", year=2007)
|
||||
result1 = val.asfreq("5min")
|
||||
result2 = val.asfreq("min")
|
||||
expected = Period("2007-12-31 23:59", freq="min")
|
||||
assert result1.ordinal == expected.ordinal
|
||||
assert result1.freqstr == "5min"
|
||||
assert result2.ordinal == expected.ordinal
|
||||
assert result2.freqstr == "min"
|
||||
|
||||
def test_conv_annual(self):
|
||||
# frequency conversion tests: from Annual Frequency
|
||||
|
||||
ival_A = Period(freq="Y", year=2007)
|
||||
|
||||
ival_AJAN = Period(freq="Y-JAN", year=2007)
|
||||
ival_AJUN = Period(freq="Y-JUN", year=2007)
|
||||
ival_ANOV = Period(freq="Y-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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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
|
||||
msg = "'H' is deprecated and will be removed in a future version."
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
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
|
||||
msg = "'T' is deprecated and will be removed in a future version."
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
assert ival_A.asfreq("T", "s") == ival_A_to_T_start
|
||||
assert ival_A.asfreq("T", "E") == ival_A_to_T_end
|
||||
msg = "'S' is deprecated and will be removed in a future version."
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
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("Y") == 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="Y", 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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_Q_to_A
|
||||
assert ival_Q_end_of_year.asfreq("Y") == 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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_M_to_A
|
||||
assert ival_M_end_of_year.asfreq("Y") == 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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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="Y", year=2007)
|
||||
else:
|
||||
ival_W_to_A_end_of_year = Period(freq="Y", 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)
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_W_to_A
|
||||
assert ival_W_end_of_year.asfreq("Y") == 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
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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"
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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("Y") == ival_B_to_A
|
||||
assert ival_B_end_of_year.asfreq("Y") == 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
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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)
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", year=2007)
|
||||
|
||||
ival_Deoq_to_AJAN = Period(freq="Y-JAN", year=2008)
|
||||
ival_Deoq_to_AJUN = Period(freq="Y-JUN", year=2007)
|
||||
ival_Deoq_to_ADEC = Period(freq="Y-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("Y") == ival_D_to_A
|
||||
|
||||
assert ival_D_end_of_quarter.asfreq("Y-JAN") == ival_Deoq_to_AJAN
|
||||
assert ival_D_end_of_quarter.asfreq("Y-JUN") == ival_Deoq_to_AJUN
|
||||
assert ival_D_end_of_quarter.asfreq("Y-DEC") == ival_Deoq_to_ADEC
|
||||
|
||||
assert ival_D_end_of_year.asfreq("Y") == 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
|
||||
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_H_to_A
|
||||
assert ival_H_end_of_year.asfreq("Y") == 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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_T_to_A
|
||||
assert ival_T_end_of_year.asfreq("Y") == 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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="Y", 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)
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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("Y") == ival_S_to_A
|
||||
assert ival_S_end_of_year.asfreq("Y") == 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
|
||||
with tm.assert_produces_warning(FutureWarning, match=bday_msg):
|
||||
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="us")
|
||||
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="Y", year=2007)
|
||||
# ordinal will not change
|
||||
for freq in ["3Y", offsets.YearEnd(3)]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2007", freq="3Y")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
# ordinal will not change
|
||||
for freq in ["3Y", offsets.YearEnd(3)]:
|
||||
result = p.asfreq(freq, how="S")
|
||||
expected = Period("2007", freq="3Y")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
# mult freq to normal freq
|
||||
p = Period(freq="3Y", year=2007)
|
||||
# ordinal will change because how=E is the default
|
||||
for freq in ["Y", offsets.YearEnd()]:
|
||||
result = p.asfreq(freq)
|
||||
expected = Period("2009", freq="Y")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
# ordinal will not change
|
||||
for freq in ["Y", offsets.YearEnd()]:
|
||||
result = p.asfreq(freq, how="s")
|
||||
expected = Period("2007", freq="Y")
|
||||
|
||||
assert result == expected
|
||||
assert result.ordinal == expected.ordinal
|
||||
assert result.freq == expected.freq
|
||||
|
||||
p = Period(freq="Y", 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="3Y", 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 = "MS is not supported as period frequency"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
initial.asfreq(freq="MS", how="S")
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Period("2013-01", "MS")
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,316 @@
|
||||
from datetime import (
|
||||
date,
|
||||
time,
|
||||
timedelta,
|
||||
)
|
||||
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"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other", [NA, 1, 1.0, "a", b"a", np.int64(1), np.nan], ids=repr
|
||||
)
|
||||
def test_arithmetic_ops(all_arithmetic_functions, other):
|
||||
op = all_arithmetic_functions
|
||||
|
||||
if op.__name__ in ("pow", "rpow", "rmod") and isinstance(other, (str, bytes)):
|
||||
pytest.skip(reason=f"{op.__name__} with NA and {other} not defined.")
|
||||
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
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[
|
||||
NA,
|
||||
1,
|
||||
1.0,
|
||||
"a",
|
||||
b"a",
|
||||
np.int64(1),
|
||||
np.nan,
|
||||
np.bool_(True),
|
||||
time(0),
|
||||
date(1, 2, 3),
|
||||
timedelta(1),
|
||||
pd.NaT,
|
||||
],
|
||||
)
|
||||
def test_comparison_ops(comparison_op, other):
|
||||
assert comparison_op(NA, other) is NA
|
||||
assert comparison_op(other, NA) is NA
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
0,
|
||||
0.0,
|
||||
-0,
|
||||
-0.0,
|
||||
False,
|
||||
np.bool_(False),
|
||||
np.int_(0),
|
||||
np.float64(0),
|
||||
np.int_(-0),
|
||||
np.float64(-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.float64(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.float64, 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.float64(-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,709 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
import operator
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas._libs.tslibs import iNaT
|
||||
from pandas.compat.numpy import np_version_gte1p24p3
|
||||
|
||||
from pandas import (
|
||||
DatetimeIndex,
|
||||
DatetimeTZDtype,
|
||||
Index,
|
||||
NaT,
|
||||
Period,
|
||||
Series,
|
||||
Timedelta,
|
||||
TimedeltaIndex,
|
||||
Timestamp,
|
||||
isna,
|
||||
offsets,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
from pandas.core import roperator
|
||||
from pandas.core.arrays import (
|
||||
DatetimeArray,
|
||||
PeriodArray,
|
||||
TimedeltaArray,
|
||||
)
|
||||
|
||||
|
||||
class TestNaTFormatting:
|
||||
def test_repr(self):
|
||||
assert repr(NaT) == "NaT"
|
||||
|
||||
def test_str(self):
|
||||
assert str(NaT) == "NaT"
|
||||
|
||||
def test_isoformat(self):
|
||||
assert NaT.isoformat() == "NaT"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"nat,idx",
|
||||
[
|
||||
(Timestamp("NaT"), DatetimeArray),
|
||||
(Timedelta("NaT"), TimedeltaArray),
|
||||
(Period("NaT", freq="M"), PeriodArray),
|
||||
],
|
||||
)
|
||||
def test_nat_fields(nat, idx):
|
||||
for field in idx._field_ops:
|
||||
# weekday is a property of DTI, but a method
|
||||
# on NaT/Timestamp for compat with datetime
|
||||
if field == "weekday":
|
||||
continue
|
||||
|
||||
result = getattr(NaT, field)
|
||||
assert np.isnan(result)
|
||||
|
||||
result = getattr(nat, field)
|
||||
assert np.isnan(result)
|
||||
|
||||
for field in idx._bool_ops:
|
||||
result = getattr(NaT, field)
|
||||
assert result is False
|
||||
|
||||
result = getattr(nat, field)
|
||||
assert result is False
|
||||
|
||||
|
||||
def test_nat_vector_field_access():
|
||||
idx = DatetimeIndex(["1/1/2000", None, None, "1/4/2000"])
|
||||
|
||||
for field in DatetimeArray._field_ops:
|
||||
# weekday is a property of DTI, but a method
|
||||
# on NaT/Timestamp for compat with datetime
|
||||
if field == "weekday":
|
||||
continue
|
||||
|
||||
result = getattr(idx, field)
|
||||
expected = Index([getattr(x, field) for x in idx])
|
||||
tm.assert_index_equal(result, expected)
|
||||
|
||||
ser = Series(idx)
|
||||
|
||||
for field in DatetimeArray._field_ops:
|
||||
# weekday is a property of DTI, but a method
|
||||
# on NaT/Timestamp for compat with datetime
|
||||
if field == "weekday":
|
||||
continue
|
||||
|
||||
result = getattr(ser.dt, field)
|
||||
expected = [getattr(x, field) for x in idx]
|
||||
tm.assert_series_equal(result, Series(expected))
|
||||
|
||||
for field in DatetimeArray._bool_ops:
|
||||
result = getattr(ser.dt, field)
|
||||
expected = [getattr(x, field) for x in idx]
|
||||
tm.assert_series_equal(result, Series(expected))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("klass", [Timestamp, Timedelta, Period])
|
||||
@pytest.mark.parametrize(
|
||||
"value", [None, np.nan, iNaT, float("nan"), NaT, "NaT", "nat", "", "NAT"]
|
||||
)
|
||||
def test_identity(klass, value):
|
||||
assert klass(value) is NaT
|
||||
|
||||
|
||||
@pytest.mark.parametrize("klass", [Timestamp, Timedelta])
|
||||
@pytest.mark.parametrize("method", ["round", "floor", "ceil"])
|
||||
@pytest.mark.parametrize("freq", ["s", "5s", "min", "5min", "h", "5h"])
|
||||
def test_round_nat(klass, method, freq):
|
||||
# see gh-14940
|
||||
ts = klass("nat")
|
||||
|
||||
round_method = getattr(ts, method)
|
||||
assert round_method(freq) is ts
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method",
|
||||
[
|
||||
"astimezone",
|
||||
"combine",
|
||||
"ctime",
|
||||
"dst",
|
||||
"fromordinal",
|
||||
"fromtimestamp",
|
||||
"fromisocalendar",
|
||||
"isocalendar",
|
||||
"strftime",
|
||||
"strptime",
|
||||
"time",
|
||||
"timestamp",
|
||||
"timetuple",
|
||||
"timetz",
|
||||
"toordinal",
|
||||
"tzname",
|
||||
"utcfromtimestamp",
|
||||
"utcnow",
|
||||
"utcoffset",
|
||||
"utctimetuple",
|
||||
"timestamp",
|
||||
],
|
||||
)
|
||||
def test_nat_methods_raise(method):
|
||||
# see gh-9513, gh-17329
|
||||
msg = f"NaTType does not support {method}"
|
||||
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
getattr(NaT, method)()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("method", ["weekday", "isoweekday"])
|
||||
def test_nat_methods_nan(method):
|
||||
# see gh-9513, gh-17329
|
||||
assert np.isnan(getattr(NaT, method)())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"method", ["date", "now", "replace", "today", "tz_convert", "tz_localize"]
|
||||
)
|
||||
def test_nat_methods_nat(method):
|
||||
# see gh-8254, gh-9513, gh-17329
|
||||
assert getattr(NaT, method)() is NaT
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"get_nat", [lambda x: NaT, lambda x: Timedelta(x), lambda x: Timestamp(x)]
|
||||
)
|
||||
def test_nat_iso_format(get_nat):
|
||||
# see gh-12300
|
||||
assert get_nat("NaT").isoformat() == "NaT"
|
||||
assert get_nat("NaT").isoformat(timespec="nanoseconds") == "NaT"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"klass,expected",
|
||||
[
|
||||
(Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]),
|
||||
(
|
||||
Timedelta,
|
||||
[
|
||||
"components",
|
||||
"resolution_string",
|
||||
"to_pytimedelta",
|
||||
"to_timedelta64",
|
||||
"unit",
|
||||
"view",
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_missing_public_nat_methods(klass, expected):
|
||||
# see gh-17327
|
||||
#
|
||||
# NaT should have *most* of the Timestamp and Timedelta methods.
|
||||
# Here, we check which public methods NaT does not have. We
|
||||
# ignore any missing private methods.
|
||||
nat_names = dir(NaT)
|
||||
klass_names = dir(klass)
|
||||
|
||||
missing = [x for x in klass_names if x not in nat_names and not x.startswith("_")]
|
||||
missing.sort()
|
||||
|
||||
assert missing == expected
|
||||
|
||||
|
||||
def _get_overlap_public_nat_methods(klass, as_tuple=False):
|
||||
"""
|
||||
Get overlapping public methods between NaT and another class.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
klass : type
|
||||
The class to compare with NaT
|
||||
as_tuple : bool, default False
|
||||
Whether to return a list of tuples of the form (klass, method).
|
||||
|
||||
Returns
|
||||
-------
|
||||
overlap : list
|
||||
"""
|
||||
nat_names = dir(NaT)
|
||||
klass_names = dir(klass)
|
||||
|
||||
overlap = [
|
||||
x
|
||||
for x in nat_names
|
||||
if x in klass_names and not x.startswith("_") and callable(getattr(klass, x))
|
||||
]
|
||||
|
||||
# Timestamp takes precedence over Timedelta in terms of overlap.
|
||||
if klass is Timedelta:
|
||||
ts_names = dir(Timestamp)
|
||||
overlap = [x for x in overlap if x not in ts_names]
|
||||
|
||||
if as_tuple:
|
||||
overlap = [(klass, method) for method in overlap]
|
||||
|
||||
overlap.sort()
|
||||
return overlap
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"klass,expected",
|
||||
[
|
||||
(
|
||||
Timestamp,
|
||||
[
|
||||
"as_unit",
|
||||
"astimezone",
|
||||
"ceil",
|
||||
"combine",
|
||||
"ctime",
|
||||
"date",
|
||||
"day_name",
|
||||
"dst",
|
||||
"floor",
|
||||
"fromisocalendar",
|
||||
"fromisoformat",
|
||||
"fromordinal",
|
||||
"fromtimestamp",
|
||||
"isocalendar",
|
||||
"isoformat",
|
||||
"isoweekday",
|
||||
"month_name",
|
||||
"now",
|
||||
"replace",
|
||||
"round",
|
||||
"strftime",
|
||||
"strptime",
|
||||
"time",
|
||||
"timestamp",
|
||||
"timetuple",
|
||||
"timetz",
|
||||
"to_datetime64",
|
||||
"to_numpy",
|
||||
"to_pydatetime",
|
||||
"today",
|
||||
"toordinal",
|
||||
"tz_convert",
|
||||
"tz_localize",
|
||||
"tzname",
|
||||
"utcfromtimestamp",
|
||||
"utcnow",
|
||||
"utcoffset",
|
||||
"utctimetuple",
|
||||
"weekday",
|
||||
],
|
||||
),
|
||||
(Timedelta, ["total_seconds"]),
|
||||
],
|
||||
)
|
||||
def test_overlap_public_nat_methods(klass, expected):
|
||||
# see gh-17327
|
||||
#
|
||||
# NaT should have *most* of the Timestamp and Timedelta methods.
|
||||
# In case when Timestamp, Timedelta, and NaT are overlap, the overlap
|
||||
# is considered to be with Timestamp and NaT, not Timedelta.
|
||||
assert _get_overlap_public_nat_methods(klass) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compare",
|
||||
(
|
||||
_get_overlap_public_nat_methods(Timestamp, True)
|
||||
+ _get_overlap_public_nat_methods(Timedelta, True)
|
||||
),
|
||||
ids=lambda x: f"{x[0].__name__}.{x[1]}",
|
||||
)
|
||||
def test_nat_doc_strings(compare):
|
||||
# see gh-17327
|
||||
#
|
||||
# The docstrings for overlapping methods should match.
|
||||
klass, method = compare
|
||||
klass_doc = getattr(klass, method).__doc__
|
||||
|
||||
if klass == Timestamp and method == "isoformat":
|
||||
pytest.skip(
|
||||
"Ignore differences with Timestamp.isoformat() as they're intentional"
|
||||
)
|
||||
|
||||
if method == "to_numpy":
|
||||
# GH#44460 can return either dt64 or td64 depending on dtype,
|
||||
# different docstring is intentional
|
||||
pytest.skip(f"different docstring for {method} is intentional")
|
||||
|
||||
nat_doc = getattr(NaT, method).__doc__
|
||||
assert klass_doc == nat_doc
|
||||
|
||||
|
||||
_ops = {
|
||||
"left_plus_right": lambda a, b: a + b,
|
||||
"right_plus_left": lambda a, b: b + a,
|
||||
"left_minus_right": lambda a, b: a - b,
|
||||
"right_minus_left": lambda a, b: b - a,
|
||||
"left_times_right": lambda a, b: a * b,
|
||||
"right_times_left": lambda a, b: b * a,
|
||||
"left_div_right": lambda a, b: a / b,
|
||||
"right_div_left": lambda a, b: b / a,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op_name", list(_ops.keys()))
|
||||
@pytest.mark.parametrize(
|
||||
"value,val_type",
|
||||
[
|
||||
(2, "scalar"),
|
||||
(1.5, "floating"),
|
||||
(np.nan, "floating"),
|
||||
("foo", "str"),
|
||||
(timedelta(3600), "timedelta"),
|
||||
(Timedelta("5s"), "timedelta"),
|
||||
(datetime(2014, 1, 1), "timestamp"),
|
||||
(Timestamp("2014-01-01"), "timestamp"),
|
||||
(Timestamp("2014-01-01", tz="UTC"), "timestamp"),
|
||||
(Timestamp("2014-01-01", tz="US/Eastern"), "timestamp"),
|
||||
(pytz.timezone("Asia/Tokyo").localize(datetime(2014, 1, 1)), "timestamp"),
|
||||
],
|
||||
)
|
||||
def test_nat_arithmetic_scalar(op_name, value, val_type):
|
||||
# see gh-6873
|
||||
invalid_ops = {
|
||||
"scalar": {"right_div_left"},
|
||||
"floating": {
|
||||
"right_div_left",
|
||||
"left_minus_right",
|
||||
"right_minus_left",
|
||||
"left_plus_right",
|
||||
"right_plus_left",
|
||||
},
|
||||
"str": set(_ops.keys()),
|
||||
"timedelta": {"left_times_right", "right_times_left"},
|
||||
"timestamp": {
|
||||
"left_times_right",
|
||||
"right_times_left",
|
||||
"left_div_right",
|
||||
"right_div_left",
|
||||
},
|
||||
}
|
||||
|
||||
op = _ops[op_name]
|
||||
|
||||
if op_name in invalid_ops.get(val_type, set()):
|
||||
if (
|
||||
val_type == "timedelta"
|
||||
and "times" in op_name
|
||||
and isinstance(value, Timedelta)
|
||||
):
|
||||
typs = "(Timedelta|NaTType)"
|
||||
msg = rf"unsupported operand type\(s\) for \*: '{typs}' and '{typs}'"
|
||||
elif val_type == "str":
|
||||
# un-specific check here because the message comes from str
|
||||
# and varies by method
|
||||
msg = "|".join(
|
||||
[
|
||||
"can only concatenate str",
|
||||
"unsupported operand type",
|
||||
"can't multiply sequence",
|
||||
"Can't convert 'NaTType'",
|
||||
"must be str, not NaTType",
|
||||
]
|
||||
)
|
||||
else:
|
||||
msg = "unsupported operand type"
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
op(NaT, value)
|
||||
else:
|
||||
if val_type == "timedelta" and "div" in op_name:
|
||||
expected = np.nan
|
||||
else:
|
||||
expected = NaT
|
||||
|
||||
assert op(NaT, value) is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val,expected", [(np.nan, NaT), (NaT, np.nan), (np.timedelta64("NaT"), np.nan)]
|
||||
)
|
||||
def test_nat_rfloordiv_timedelta(val, expected):
|
||||
# see gh-#18846
|
||||
#
|
||||
# See also test_timedelta.TestTimedeltaArithmetic.test_floordiv
|
||||
td = Timedelta(hours=3, minutes=4)
|
||||
assert td // val is expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op_name",
|
||||
["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
DatetimeIndex(["2011-01-01", "2011-01-02"], name="x"),
|
||||
DatetimeIndex(["2011-01-01", "2011-01-02"], tz="US/Eastern", name="x"),
|
||||
DatetimeArray._from_sequence(["2011-01-01", "2011-01-02"], dtype="M8[ns]"),
|
||||
DatetimeArray._from_sequence(
|
||||
["2011-01-01", "2011-01-02"], dtype=DatetimeTZDtype(tz="US/Pacific")
|
||||
),
|
||||
TimedeltaIndex(["1 day", "2 day"], name="x"),
|
||||
],
|
||||
)
|
||||
def test_nat_arithmetic_index(op_name, value):
|
||||
# see gh-11718
|
||||
exp_name = "x"
|
||||
exp_data = [NaT] * 2
|
||||
|
||||
if value.dtype.kind == "M" and "plus" in op_name:
|
||||
expected = DatetimeIndex(exp_data, tz=value.tz, name=exp_name)
|
||||
else:
|
||||
expected = TimedeltaIndex(exp_data, name=exp_name)
|
||||
expected = expected.as_unit(value.unit)
|
||||
|
||||
if not isinstance(value, Index):
|
||||
expected = expected.array
|
||||
|
||||
op = _ops[op_name]
|
||||
result = op(NaT, value)
|
||||
tm.assert_equal(result, expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op_name",
|
||||
["left_plus_right", "right_plus_left", "left_minus_right", "right_minus_left"],
|
||||
)
|
||||
@pytest.mark.parametrize("box", [TimedeltaIndex, Series, TimedeltaArray._from_sequence])
|
||||
def test_nat_arithmetic_td64_vector(op_name, box):
|
||||
# see gh-19124
|
||||
vec = box(["1 day", "2 day"], dtype="timedelta64[ns]")
|
||||
box_nat = box([NaT, NaT], dtype="timedelta64[ns]")
|
||||
tm.assert_equal(_ops[op_name](vec, NaT), box_nat)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dtype,op,out_dtype",
|
||||
[
|
||||
("datetime64[ns]", operator.add, "datetime64[ns]"),
|
||||
("datetime64[ns]", roperator.radd, "datetime64[ns]"),
|
||||
("datetime64[ns]", operator.sub, "timedelta64[ns]"),
|
||||
("datetime64[ns]", roperator.rsub, "timedelta64[ns]"),
|
||||
("timedelta64[ns]", operator.add, "datetime64[ns]"),
|
||||
("timedelta64[ns]", roperator.radd, "datetime64[ns]"),
|
||||
("timedelta64[ns]", operator.sub, "datetime64[ns]"),
|
||||
("timedelta64[ns]", roperator.rsub, "timedelta64[ns]"),
|
||||
],
|
||||
)
|
||||
def test_nat_arithmetic_ndarray(dtype, op, out_dtype):
|
||||
other = np.arange(10).astype(dtype)
|
||||
result = op(NaT, other)
|
||||
|
||||
expected = np.empty(other.shape, dtype=out_dtype)
|
||||
expected.fill("NaT")
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
|
||||
def test_nat_pinned_docstrings():
|
||||
# see gh-17327
|
||||
assert NaT.ctime.__doc__ == Timestamp.ctime.__doc__
|
||||
|
||||
|
||||
def test_to_numpy_alias():
|
||||
# GH 24653: alias .to_numpy() for scalars
|
||||
expected = NaT.to_datetime64()
|
||||
result = NaT.to_numpy()
|
||||
|
||||
assert isna(expected) and isna(result)
|
||||
|
||||
# GH#44460
|
||||
result = NaT.to_numpy("M8[s]")
|
||||
assert isinstance(result, np.datetime64)
|
||||
assert result.dtype == "M8[s]"
|
||||
|
||||
result = NaT.to_numpy("m8[ns]")
|
||||
assert isinstance(result, np.timedelta64)
|
||||
assert result.dtype == "m8[ns]"
|
||||
|
||||
result = NaT.to_numpy("m8[s]")
|
||||
assert isinstance(result, np.timedelta64)
|
||||
assert result.dtype == "m8[s]"
|
||||
|
||||
with pytest.raises(ValueError, match="NaT.to_numpy dtype must be a "):
|
||||
NaT.to_numpy(np.int64)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[
|
||||
Timedelta(0),
|
||||
Timedelta(0).to_pytimedelta(),
|
||||
pytest.param(
|
||||
Timedelta(0).to_timedelta64(),
|
||||
marks=pytest.mark.xfail(
|
||||
not np_version_gte1p24p3,
|
||||
reason="td64 doesn't return NotImplemented, see numpy#17017",
|
||||
# When this xfail is fixed, test_nat_comparisons_numpy
|
||||
# can be removed.
|
||||
),
|
||||
),
|
||||
Timestamp(0),
|
||||
Timestamp(0).to_pydatetime(),
|
||||
pytest.param(
|
||||
Timestamp(0).to_datetime64(),
|
||||
marks=pytest.mark.xfail(
|
||||
not np_version_gte1p24p3,
|
||||
reason="dt64 doesn't return NotImplemented, see numpy#17017",
|
||||
),
|
||||
),
|
||||
Timestamp(0).tz_localize("UTC"),
|
||||
NaT,
|
||||
],
|
||||
)
|
||||
def test_nat_comparisons(compare_operators_no_eq_ne, other):
|
||||
# GH 26039
|
||||
opname = compare_operators_no_eq_ne
|
||||
|
||||
assert getattr(NaT, opname)(other) is False
|
||||
|
||||
op = getattr(operator, opname.strip("_"))
|
||||
assert op(NaT, other) is False
|
||||
assert op(other, NaT) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("other", [np.timedelta64(0, "ns"), np.datetime64("now", "ns")])
|
||||
def test_nat_comparisons_numpy(other):
|
||||
# Once numpy#17017 is fixed and the xfailed cases in test_nat_comparisons
|
||||
# pass, this test can be removed
|
||||
assert not NaT == other
|
||||
assert NaT != other
|
||||
assert not NaT < other
|
||||
assert not NaT > other
|
||||
assert not NaT <= other
|
||||
assert not NaT >= other
|
||||
|
||||
|
||||
@pytest.mark.parametrize("other_and_type", [("foo", "str"), (2, "int"), (2.0, "float")])
|
||||
@pytest.mark.parametrize(
|
||||
"symbol_and_op",
|
||||
[("<=", operator.le), ("<", operator.lt), (">=", operator.ge), (">", operator.gt)],
|
||||
)
|
||||
def test_nat_comparisons_invalid(other_and_type, symbol_and_op):
|
||||
# GH#35585
|
||||
other, other_type = other_and_type
|
||||
symbol, op = symbol_and_op
|
||||
|
||||
assert not NaT == other
|
||||
assert not other == NaT
|
||||
|
||||
assert NaT != other
|
||||
assert other != NaT
|
||||
|
||||
msg = f"'{symbol}' not supported between instances of 'NaTType' and '{other_type}'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
op(NaT, other)
|
||||
|
||||
msg = f"'{symbol}' not supported between instances of '{other_type}' and 'NaTType'"
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
op(other, NaT)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[
|
||||
np.array(["foo"] * 2, dtype=object),
|
||||
np.array([2, 3], dtype="int64"),
|
||||
np.array([2.0, 3.5], dtype="float64"),
|
||||
],
|
||||
ids=["str", "int", "float"],
|
||||
)
|
||||
def test_nat_comparisons_invalid_ndarray(other):
|
||||
# GH#40722
|
||||
expected = np.array([False, False])
|
||||
result = NaT == other
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
result = other == NaT
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
expected = np.array([True, True])
|
||||
result = NaT != other
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
result = other != NaT
|
||||
tm.assert_numpy_array_equal(result, expected)
|
||||
|
||||
for symbol, op in [
|
||||
("<=", operator.le),
|
||||
("<", operator.lt),
|
||||
(">=", operator.ge),
|
||||
(">", operator.gt),
|
||||
]:
|
||||
msg = f"'{symbol}' not supported between"
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
op(NaT, other)
|
||||
|
||||
if other.dtype == np.dtype("object"):
|
||||
# uses the reverse operator, so symbol changes
|
||||
msg = None
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
op(other, NaT)
|
||||
|
||||
|
||||
def test_compare_date(fixed_now_ts):
|
||||
# GH#39151 comparing NaT with date object is deprecated
|
||||
# See also: tests.scalar.timestamps.test_comparisons::test_compare_date
|
||||
|
||||
dt = fixed_now_ts.to_pydatetime().date()
|
||||
|
||||
msg = "Cannot compare NaT with datetime.date object"
|
||||
for left, right in [(NaT, dt), (dt, NaT)]:
|
||||
assert not left == right
|
||||
assert left != right
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left < right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left <= right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left > right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left >= right
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"obj",
|
||||
[
|
||||
offsets.YearEnd(2),
|
||||
offsets.YearBegin(2),
|
||||
offsets.MonthBegin(1),
|
||||
offsets.MonthEnd(2),
|
||||
offsets.MonthEnd(12),
|
||||
offsets.Day(2),
|
||||
offsets.Day(5),
|
||||
offsets.Hour(24),
|
||||
offsets.Hour(3),
|
||||
offsets.Minute(),
|
||||
np.timedelta64(3, "h"),
|
||||
np.timedelta64(4, "h"),
|
||||
np.timedelta64(3200, "s"),
|
||||
np.timedelta64(3600, "s"),
|
||||
np.timedelta64(3600 * 24, "s"),
|
||||
np.timedelta64(2, "D"),
|
||||
np.timedelta64(365, "D"),
|
||||
timedelta(-2),
|
||||
timedelta(365),
|
||||
timedelta(minutes=120),
|
||||
timedelta(days=4, minutes=180),
|
||||
timedelta(hours=23),
|
||||
timedelta(hours=23, minutes=30),
|
||||
timedelta(hours=48),
|
||||
],
|
||||
)
|
||||
def test_nat_addsub_tdlike_scalar(obj):
|
||||
assert NaT + obj is NaT
|
||||
assert obj + NaT is NaT
|
||||
assert NaT - obj is NaT
|
||||
|
||||
|
||||
def test_pickle():
|
||||
# GH#4606
|
||||
p = tm.round_trip_pickle(NaT)
|
||||
assert p is NaT
|
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,80 @@
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas.errors import OutOfBoundsTimedelta
|
||||
|
||||
from pandas import Timedelta
|
||||
|
||||
|
||||
class TestAsUnit:
|
||||
def test_as_unit(self):
|
||||
td = Timedelta(days=1)
|
||||
|
||||
assert td.as_unit("ns") is td
|
||||
|
||||
res = td.as_unit("us")
|
||||
assert res._value == td._value // 1000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == td._value
|
||||
assert rt._creso == td._creso
|
||||
|
||||
res = td.as_unit("ms")
|
||||
assert res._value == td._value // 1_000_000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == td._value
|
||||
assert rt._creso == td._creso
|
||||
|
||||
res = td.as_unit("s")
|
||||
assert res._value == td._value // 1_000_000_000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == td._value
|
||||
assert rt._creso == td._creso
|
||||
|
||||
def test_as_unit_overflows(self):
|
||||
# microsecond that would be just out of bounds for nano
|
||||
us = 9223372800000000
|
||||
td = Timedelta._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value)
|
||||
|
||||
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td.as_unit("ns")
|
||||
|
||||
res = td.as_unit("ms")
|
||||
assert res._value == us // 1000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
def test_as_unit_rounding(self):
|
||||
td = Timedelta(microseconds=1500)
|
||||
res = td.as_unit("ms")
|
||||
|
||||
expected = Timedelta(milliseconds=1)
|
||||
assert res == expected
|
||||
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
assert res._value == 1
|
||||
|
||||
with pytest.raises(ValueError, match="Cannot losslessly convert units"):
|
||||
td.as_unit("ms", round_ok=False)
|
||||
|
||||
def test_as_unit_non_nano(self):
|
||||
# case where we are going neither to nor from nano
|
||||
td = Timedelta(days=1).as_unit("ms")
|
||||
assert td.days == 1
|
||||
assert td._value == 86_400_000
|
||||
assert td.components.days == 1
|
||||
assert td._d == 1
|
||||
assert td.total_seconds() == 86400
|
||||
|
||||
res = td.as_unit("us")
|
||||
assert res._value == 86_400_000_000
|
||||
assert res.components.days == 1
|
||||
assert res.components.hours == 0
|
||||
assert res._d == 1
|
||||
assert res._h == 0
|
||||
assert res.total_seconds() == 86400
|
@ -0,0 +1,187 @@
|
||||
from hypothesis import (
|
||||
given,
|
||||
strategies as st,
|
||||
)
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs import lib
|
||||
from pandas._libs.tslibs import iNaT
|
||||
from pandas.errors import OutOfBoundsTimedelta
|
||||
|
||||
from pandas import Timedelta
|
||||
|
||||
|
||||
class TestTimedeltaRound:
|
||||
@pytest.mark.parametrize(
|
||||
"freq,s1,s2",
|
||||
[
|
||||
# This first case has s1, s2 being the same as t1,t2 below
|
||||
(
|
||||
"ns",
|
||||
Timedelta("1 days 02:34:56.789123456"),
|
||||
Timedelta("-1 days 02:34:56.789123456"),
|
||||
),
|
||||
(
|
||||
"us",
|
||||
Timedelta("1 days 02:34:56.789123000"),
|
||||
Timedelta("-1 days 02:34:56.789123000"),
|
||||
),
|
||||
(
|
||||
"ms",
|
||||
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")),
|
||||
("min", Timedelta("1 days 02:35:00"), Timedelta("-1 days 02:35:00")),
|
||||
("12min", 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 [
|
||||
("YE", "<YearEnd: month=12> is a non-fixed frequency"),
|
||||
("ME", "<MonthEnd> is a non-fixed frequency"),
|
||||
("foobar", "Invalid frequency: foobar"),
|
||||
]:
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
t1.round(freq)
|
||||
|
||||
@pytest.mark.skip_ubsan
|
||||
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
|
||||
|
||||
msg = (
|
||||
r"Cannot round -106752 days \+00:12:43.145224193 to freq=s without overflow"
|
||||
)
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta.min.floor("s")
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta.min.round("s")
|
||||
|
||||
msg = "Cannot round 106751 days 23:47:16.854775807 to freq=s without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta.max.ceil("s")
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta.max.round("s")
|
||||
|
||||
@pytest.mark.skip_ubsan
|
||||
@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):
|
||||
cls = Timedelta
|
||||
err_cls = OutOfBoundsTimedelta
|
||||
|
||||
val = np.int64(val)
|
||||
td = cls(val)
|
||||
|
||||
def checker(ts, nanos, unit):
|
||||
# First check that we do raise in cases where we should
|
||||
if nanos == 1:
|
||||
pass
|
||||
else:
|
||||
div, mod = divmod(ts._value, nanos)
|
||||
diff = int(nanos - mod)
|
||||
lb = ts._value - mod
|
||||
assert lb <= ts._value # i.e. no overflows with python ints
|
||||
ub = ts._value + diff
|
||||
assert ub > ts._value # i.e. no overflows with python ints
|
||||
|
||||
msg = "without overflow"
|
||||
if mod == 0:
|
||||
# We should never be raising in this
|
||||
pass
|
||||
elif method is cls.ceil:
|
||||
if ub > cls.max._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif method is cls.floor:
|
||||
if lb < cls.min._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif mod >= diff:
|
||||
if ub > cls.max._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif lb < cls.min._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
|
||||
res = method(ts, unit)
|
||||
|
||||
td = res - ts
|
||||
diff = abs(td._value)
|
||||
assert diff < nanos
|
||||
assert res._value % nanos == 0
|
||||
|
||||
if method is cls.round:
|
||||
assert diff <= nanos / 2
|
||||
elif method is cls.floor:
|
||||
assert res <= ts
|
||||
elif method is cls.ceil:
|
||||
assert res >= ts
|
||||
|
||||
nanos = 1
|
||||
checker(td, nanos, "ns")
|
||||
|
||||
nanos = 1000
|
||||
checker(td, nanos, "us")
|
||||
|
||||
nanos = 1_000_000
|
||||
checker(td, nanos, "ms")
|
||||
|
||||
nanos = 1_000_000_000
|
||||
checker(td, nanos, "s")
|
||||
|
||||
nanos = 60 * 1_000_000_000
|
||||
checker(td, nanos, "min")
|
||||
|
||||
nanos = 60 * 60 * 1_000_000_000
|
||||
checker(td, nanos, "h")
|
||||
|
||||
nanos = 24 * 60 * 60 * 1_000_000_000
|
||||
checker(td, nanos, "D")
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_round_non_nano(self, unit):
|
||||
td = Timedelta("1 days 02:34:57").as_unit(unit)
|
||||
|
||||
res = td.round("min")
|
||||
assert res == Timedelta("1 days 02:35:00")
|
||||
assert res._creso == td._creso
|
||||
|
||||
res = td.floor("min")
|
||||
assert res == Timedelta("1 days 02:34:00")
|
||||
assert res._creso == td._creso
|
||||
|
||||
res = td.ceil("min")
|
||||
assert res == Timedelta("1 days 02:35:00")
|
||||
assert res._creso == td._creso
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,698 @@
|
||||
from datetime import timedelta
|
||||
from itertools import product
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs import OutOfBoundsTimedelta
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
|
||||
from pandas import (
|
||||
Index,
|
||||
NaT,
|
||||
Timedelta,
|
||||
TimedeltaIndex,
|
||||
offsets,
|
||||
to_timedelta,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimedeltaConstructorUnitKeyword:
|
||||
@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)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"unit,unit_depr",
|
||||
[
|
||||
("h", "H"),
|
||||
("min", "T"),
|
||||
("s", "S"),
|
||||
("ms", "L"),
|
||||
("ns", "N"),
|
||||
("us", "U"),
|
||||
],
|
||||
)
|
||||
def test_units_H_T_S_L_N_U_deprecated(self, unit, unit_depr):
|
||||
# GH#52536
|
||||
msg = f"'{unit_depr}' is deprecated and will be removed in a future version."
|
||||
|
||||
expected = Timedelta(1, unit=unit)
|
||||
with tm.assert_produces_warning(FutureWarning, match=msg):
|
||||
result = Timedelta(1, unit=unit_depr)
|
||||
tm.assert_equal(result, expected)
|
||||
|
||||
@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",
|
||||
"Minute",
|
||||
"Min",
|
||||
"Minutes",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "s")
|
||||
for value in [
|
||||
"s",
|
||||
"seconds",
|
||||
"sec",
|
||||
"second",
|
||||
"Seconds",
|
||||
"Sec",
|
||||
"Second",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(value, "ms")
|
||||
for value in [
|
||||
"ms",
|
||||
"milliseconds",
|
||||
"millisecond",
|
||||
"milli",
|
||||
"millis",
|
||||
"MS",
|
||||
"Milliseconds",
|
||||
"Millisecond",
|
||||
"Milli",
|
||||
"Millis",
|
||||
]
|
||||
]
|
||||
+ [
|
||||
(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, 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()],
|
||||
dtype="m8[ns]",
|
||||
)
|
||||
# TODO(2.0): the desired output dtype may have non-nano resolution
|
||||
msg = f"'{unit}' is deprecated and will be removed in a future version."
|
||||
|
||||
if (unit, np_unit) in (("u", "us"), ("U", "us"), ("n", "ns"), ("N", "ns")):
|
||||
warn = FutureWarning
|
||||
else:
|
||||
warn = FutureWarning
|
||||
msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated"
|
||||
with tm.assert_produces_warning(warn, match=msg):
|
||||
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
|
||||
|
||||
|
||||
def test_construct_from_kwargs_overflow():
|
||||
# GH#55503
|
||||
msg = "seconds=86400000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(days=10**6)
|
||||
msg = "seconds=60000000000000000000, milliseconds=0, microseconds=0, nanoseconds=0"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(minutes=10**9)
|
||||
|
||||
|
||||
def test_construct_with_weeks_unit_overflow():
|
||||
# GH#47268 don't silently wrap around
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="without overflow"):
|
||||
Timedelta(1000000000000000000, unit="W")
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="without overflow"):
|
||||
Timedelta(1000000000000000000.0, unit="W")
|
||||
|
||||
|
||||
def test_construct_from_td64_with_unit():
|
||||
# ignore the unit, as it may cause silently overflows leading to incorrect
|
||||
# results, and in non-overflow cases is irrelevant GH#46827
|
||||
obj = np.timedelta64(123456789000000000, "h")
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
||||
Timedelta(obj, unit="ps")
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
||||
Timedelta(obj, unit="ns")
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match="123456789000000000 hours"):
|
||||
Timedelta(obj)
|
||||
|
||||
|
||||
def test_from_td64_retain_resolution():
|
||||
# case where we retain millisecond resolution
|
||||
obj = np.timedelta64(12345, "ms")
|
||||
|
||||
td = Timedelta(obj)
|
||||
assert td._value == obj.view("i8")
|
||||
assert td._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
# Case where we cast to nearest-supported reso
|
||||
obj2 = np.timedelta64(1234, "D")
|
||||
td2 = Timedelta(obj2)
|
||||
assert td2._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
assert td2 == obj2
|
||||
assert td2.days == 1234
|
||||
|
||||
# Case that _would_ overflow if we didn't support non-nano
|
||||
obj3 = np.timedelta64(1000000000000000000, "us")
|
||||
td3 = Timedelta(obj3)
|
||||
assert td3.total_seconds() == 1000000000000
|
||||
assert td3._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
|
||||
def test_from_pytimedelta_us_reso():
|
||||
# pytimedelta has microsecond resolution, so Timedelta(pytd) inherits that
|
||||
td = timedelta(days=4, minutes=3)
|
||||
result = Timedelta(td)
|
||||
assert result.to_pytimedelta() == td
|
||||
assert result._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
|
||||
def test_from_tick_reso():
|
||||
tick = offsets.Nano()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
tick = offsets.Micro()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
tick = offsets.Milli()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
tick = offsets.Second()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
# everything above Second gets cast to the closest supported reso: second
|
||||
tick = offsets.Minute()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
tick = offsets.Hour()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
tick = offsets.Day()
|
||||
assert Timedelta(tick)._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
|
||||
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():
|
||||
# GH#3374
|
||||
value = Timedelta("1day")._value * 20169940
|
||||
msg = "Cannot cast 1742682816000000000000 from ns to 'ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(value)
|
||||
|
||||
# xref GH#17637
|
||||
msg = "Cannot cast 139993 from D to 'ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(7 * 19999, unit="D")
|
||||
|
||||
# used to overflow before non-ns support
|
||||
td = Timedelta(timedelta(days=13 * 19999))
|
||||
assert td._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
assert td.days == 13 * 19999
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val, unit",
|
||||
[
|
||||
(15251, "W"), # 1
|
||||
(106752, "D"), # change from previous:
|
||||
(2562048, "h"), # 0 hours
|
||||
(153722868, "m"), # 13 minutes
|
||||
(9223372037, "s"), # 44 seconds
|
||||
],
|
||||
)
|
||||
def test_construction_out_of_bounds_td64ns(val, unit):
|
||||
# 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
|
||||
|
||||
td = Timedelta(td64)
|
||||
if unit != "M":
|
||||
# with unit="M" the conversion to "s" is poorly defined
|
||||
# (and numpy issues DeprecationWarning)
|
||||
assert td.asm8 == td64
|
||||
assert td.asm8.dtype == "m8[s]"
|
||||
msg = r"Cannot cast 1067\d\d days .* to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td.as_unit("ns")
|
||||
|
||||
# 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
|
||||
|
||||
td2 = Timedelta(td64)
|
||||
msg = r"Cannot cast -1067\d\d days .* to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td2.as_unit("ns")
|
||||
|
||||
# But just back in bounds and we are OK
|
||||
assert Timedelta(td64 + 1) == td64 + 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"val, unit",
|
||||
[
|
||||
(15251 * 10**9, "W"),
|
||||
(106752 * 10**9, "D"),
|
||||
(2562048 * 10**9, "h"),
|
||||
(153722868 * 10**9, "m"),
|
||||
],
|
||||
)
|
||||
def test_construction_out_of_bounds_td64s(val, unit):
|
||||
td64 = np.timedelta64(val, unit)
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=str(td64)):
|
||||
Timedelta(td64)
|
||||
|
||||
# But just back in bounds and we are OK
|
||||
assert Timedelta(td64 - 10**9) == td64 - 10**9
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
||||
def test_timedelta_pass_td_and_kwargs_raises():
|
||||
# don't silently ignore the kwargs GH#48898
|
||||
td = Timedelta(days=1)
|
||||
msg = (
|
||||
"Cannot pass both a Timedelta input and timedelta keyword arguments, "
|
||||
r"got \['days'\]"
|
||||
)
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
Timedelta(td, days=2)
|
||||
|
||||
|
||||
@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)
|
||||
|
||||
|
||||
def test_timedelta_new_npnat():
|
||||
# GH#48898
|
||||
nat = np.timedelta64("NaT", "h")
|
||||
assert Timedelta(nat) is NaT
|
||||
|
||||
|
||||
def test_subclass_respected():
|
||||
# GH#49579
|
||||
class MyCustomTimedelta(Timedelta):
|
||||
pass
|
||||
|
||||
td = MyCustomTimedelta("1 minute")
|
||||
assert isinstance(td, MyCustomTimedelta)
|
||||
|
||||
|
||||
def test_non_nano_value():
|
||||
# https://github.com/pandas-dev/pandas/issues/49076
|
||||
result = Timedelta(10, unit="D").as_unit("s").value
|
||||
# `.value` shows nanoseconds, even though unit is 's'
|
||||
assert result == 864000000000000
|
||||
|
||||
# out-of-nanoseconds-bounds `.value` raises informative message
|
||||
msg = (
|
||||
r"Cannot convert Timedelta to nanoseconds without overflow. "
|
||||
r"Use `.asm8.view\('i8'\)` to cast represent Timedelta in its "
|
||||
r"own unit \(here, s\).$"
|
||||
)
|
||||
td = Timedelta(1_000, "D").as_unit("s") * 1_000
|
||||
with pytest.raises(OverflowError, match=msg):
|
||||
td.value
|
||||
# check that the suggested workaround actually works
|
||||
result = td.asm8.view("i8")
|
||||
assert result == 86400000000
|
@ -0,0 +1,109 @@
|
||||
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
|
||||
|
||||
|
||||
class TestReprBase:
|
||||
def test_none(self):
|
||||
delta_1d = Timedelta(1, unit="D")
|
||||
delta_0d = Timedelta(0, unit="D")
|
||||
delta_1s = Timedelta(1, unit="s")
|
||||
delta_500ms = Timedelta(500, unit="ms")
|
||||
|
||||
drepr = lambda x: x._repr_base()
|
||||
assert drepr(delta_1d) == "1 days"
|
||||
assert drepr(-delta_1d) == "-1 days"
|
||||
assert drepr(delta_0d) == "0 days"
|
||||
assert drepr(delta_1s) == "0 days 00:00:01"
|
||||
assert drepr(delta_500ms) == "0 days 00:00:00.500000"
|
||||
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
||||
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
||||
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
||||
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
||||
|
||||
def test_sub_day(self):
|
||||
delta_1d = Timedelta(1, unit="D")
|
||||
delta_0d = Timedelta(0, unit="D")
|
||||
delta_1s = Timedelta(1, unit="s")
|
||||
delta_500ms = Timedelta(500, unit="ms")
|
||||
|
||||
drepr = lambda x: x._repr_base(format="sub_day")
|
||||
assert drepr(delta_1d) == "1 days"
|
||||
assert drepr(-delta_1d) == "-1 days"
|
||||
assert drepr(delta_0d) == "00:00:00"
|
||||
assert drepr(delta_1s) == "00:00:01"
|
||||
assert drepr(delta_500ms) == "00:00:00.500000"
|
||||
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
||||
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
||||
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
||||
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
||||
|
||||
def test_long(self):
|
||||
delta_1d = Timedelta(1, unit="D")
|
||||
delta_0d = Timedelta(0, unit="D")
|
||||
delta_1s = Timedelta(1, unit="s")
|
||||
delta_500ms = Timedelta(500, unit="ms")
|
||||
|
||||
drepr = lambda x: x._repr_base(format="long")
|
||||
assert drepr(delta_1d) == "1 days 00:00:00"
|
||||
assert drepr(-delta_1d) == "-1 days +00:00:00"
|
||||
assert drepr(delta_0d) == "0 days 00:00:00"
|
||||
assert drepr(delta_1s) == "0 days 00:00:01"
|
||||
assert drepr(delta_500ms) == "0 days 00:00:00.500000"
|
||||
assert drepr(delta_1d + delta_1s) == "1 days 00:00:01"
|
||||
assert drepr(-delta_1d + delta_1s) == "-1 days +00:00:01"
|
||||
assert drepr(delta_1d + delta_500ms) == "1 days 00:00:00.500000"
|
||||
assert drepr(-delta_1d + delta_500ms) == "-1 days +00:00:00.500000"
|
||||
|
||||
def test_all(self):
|
||||
delta_1d = Timedelta(1, unit="D")
|
||||
delta_0d = Timedelta(0, unit="D")
|
||||
delta_1ns = Timedelta(1, unit="ns")
|
||||
|
||||
drepr = lambda x: x._repr_base(format="all")
|
||||
assert drepr(delta_1d) == "1 days 00:00:00.000000000"
|
||||
assert drepr(-delta_1d) == "-1 days +00:00:00.000000000"
|
||||
assert drepr(delta_0d) == "0 days 00:00:00.000000000"
|
||||
assert drepr(delta_1ns) == "0 days 00:00:00.000000001"
|
||||
assert drepr(-delta_1d + delta_1ns) == "-1 days +00:00:00.000000001"
|
@ -0,0 +1,666 @@
|
||||
""" test the scalar Timedelta """
|
||||
from datetime import timedelta
|
||||
import sys
|
||||
|
||||
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,
|
||||
)
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas.errors import OutOfBoundsTimedelta
|
||||
|
||||
from pandas import (
|
||||
Timedelta,
|
||||
to_timedelta,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestNonNano:
|
||||
@pytest.fixture(params=["s", "ms", "us"])
|
||||
def unit_str(self, request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture
|
||||
def unit(self, unit_str):
|
||||
# 7, 8, 9 correspond to second, millisecond, and microsecond, respectively
|
||||
attr = f"NPY_FR_{unit_str}"
|
||||
return getattr(NpyDatetimeUnit, attr).value
|
||||
|
||||
@pytest.fixture
|
||||
def val(self, unit):
|
||||
# microsecond that would be just out of bounds for nano
|
||||
us = 9223372800000000
|
||||
if unit == NpyDatetimeUnit.NPY_FR_us.value:
|
||||
value = us
|
||||
elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
|
||||
value = us // 1000
|
||||
else:
|
||||
value = us // 1_000_000
|
||||
return value
|
||||
|
||||
@pytest.fixture
|
||||
def td(self, unit, val):
|
||||
return Timedelta._from_value_and_reso(val, unit)
|
||||
|
||||
def test_from_value_and_reso(self, unit, val):
|
||||
# Just checking that the fixture is giving us what we asked for
|
||||
td = Timedelta._from_value_and_reso(val, unit)
|
||||
assert td._value == val
|
||||
assert td._creso == unit
|
||||
assert td.days == 106752
|
||||
|
||||
def test_unary_non_nano(self, td, unit):
|
||||
assert abs(td)._creso == unit
|
||||
assert (-td)._creso == unit
|
||||
assert (+td)._creso == unit
|
||||
|
||||
def test_sub_preserves_reso(self, td, unit):
|
||||
res = td - td
|
||||
expected = Timedelta._from_value_and_reso(0, unit)
|
||||
assert res == expected
|
||||
assert res._creso == unit
|
||||
|
||||
def test_mul_preserves_reso(self, td, unit):
|
||||
# The td fixture should always be far from the implementation
|
||||
# bound, so doubling does not risk overflow.
|
||||
res = td * 2
|
||||
assert res._value == td._value * 2
|
||||
assert res._creso == unit
|
||||
|
||||
def test_cmp_cross_reso(self, td):
|
||||
# numpy gets this wrong because of silent overflow
|
||||
other = Timedelta(days=106751, unit="ns")
|
||||
assert other < td
|
||||
assert td > other
|
||||
assert not other == td
|
||||
assert td != other
|
||||
|
||||
def test_to_pytimedelta(self, td):
|
||||
res = td.to_pytimedelta()
|
||||
expected = timedelta(days=106752)
|
||||
assert type(res) is timedelta
|
||||
assert res == expected
|
||||
|
||||
def test_to_timedelta64(self, td, unit):
|
||||
for res in [td.to_timedelta64(), td.to_numpy(), td.asm8]:
|
||||
assert isinstance(res, np.timedelta64)
|
||||
assert res.view("i8") == td._value
|
||||
if unit == NpyDatetimeUnit.NPY_FR_s.value:
|
||||
assert res.dtype == "m8[s]"
|
||||
elif unit == NpyDatetimeUnit.NPY_FR_ms.value:
|
||||
assert res.dtype == "m8[ms]"
|
||||
elif unit == NpyDatetimeUnit.NPY_FR_us.value:
|
||||
assert res.dtype == "m8[us]"
|
||||
|
||||
def test_truediv_timedeltalike(self, td):
|
||||
assert td / td == 1
|
||||
assert (2.5 * td) / td == 2.5
|
||||
|
||||
other = Timedelta(td._value)
|
||||
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow."
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td / other
|
||||
|
||||
# Timedelta(other.to_pytimedelta()) has microsecond resolution,
|
||||
# so the division doesn't require casting all the way to nanos,
|
||||
# so succeeds
|
||||
res = other.to_pytimedelta() / td
|
||||
expected = other.to_pytimedelta() / td.to_pytimedelta()
|
||||
assert res == expected
|
||||
|
||||
# if there's no overflow, we cast to the higher reso
|
||||
left = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_us.value)
|
||||
right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
|
||||
result = left / right
|
||||
assert result == 0.001
|
||||
|
||||
result = right / left
|
||||
assert result == 1000
|
||||
|
||||
def test_truediv_numeric(self, td):
|
||||
assert td / np.nan is NaT
|
||||
|
||||
res = td / 2
|
||||
assert res._value == td._value / 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
res = td / 2.0
|
||||
assert res._value == td._value / 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
def test_floordiv_timedeltalike(self, td):
|
||||
assert td // td == 1
|
||||
assert (2.5 * td) // td == 2
|
||||
|
||||
other = Timedelta(td._value)
|
||||
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td // other
|
||||
|
||||
# Timedelta(other.to_pytimedelta()) has microsecond resolution,
|
||||
# so the floordiv doesn't require casting all the way to nanos,
|
||||
# so succeeds
|
||||
res = other.to_pytimedelta() // td
|
||||
assert res == 0
|
||||
|
||||
# if there's no overflow, we cast to the higher reso
|
||||
left = Timedelta._from_value_and_reso(50050, NpyDatetimeUnit.NPY_FR_us.value)
|
||||
right = Timedelta._from_value_and_reso(50, NpyDatetimeUnit.NPY_FR_ms.value)
|
||||
result = left // right
|
||||
assert result == 1
|
||||
result = right // left
|
||||
assert result == 0
|
||||
|
||||
def test_floordiv_numeric(self, td):
|
||||
assert td // np.nan is NaT
|
||||
|
||||
res = td // 2
|
||||
assert res._value == td._value // 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
res = td // 2.0
|
||||
assert res._value == td._value // 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
assert td // np.array(np.nan) is NaT
|
||||
|
||||
res = td // np.array(2)
|
||||
assert res._value == td._value // 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
res = td // np.array(2.0)
|
||||
assert res._value == td._value // 2
|
||||
assert res._creso == td._creso
|
||||
|
||||
def test_addsub_mismatched_reso(self, td):
|
||||
# need to cast to since td is out of bounds for ns, so
|
||||
# so we would raise OverflowError without casting
|
||||
other = Timedelta(days=1).as_unit("us")
|
||||
|
||||
# td is out of bounds for ns
|
||||
result = td + other
|
||||
assert result._creso == other._creso
|
||||
assert result.days == td.days + 1
|
||||
|
||||
result = other + td
|
||||
assert result._creso == other._creso
|
||||
assert result.days == td.days + 1
|
||||
|
||||
result = td - other
|
||||
assert result._creso == other._creso
|
||||
assert result.days == td.days - 1
|
||||
|
||||
result = other - td
|
||||
assert result._creso == other._creso
|
||||
assert result.days == 1 - td.days
|
||||
|
||||
other2 = Timedelta(500)
|
||||
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td + other2
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
other2 + td
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
td - other2
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
other2 - td
|
||||
|
||||
def test_min(self, td):
|
||||
assert td.min <= td
|
||||
assert td.min._creso == td._creso
|
||||
assert td.min._value == NaT._value + 1
|
||||
|
||||
def test_max(self, td):
|
||||
assert td.max >= td
|
||||
assert td.max._creso == td._creso
|
||||
assert td.max._value == np.iinfo(np.int64).max
|
||||
|
||||
def test_resolution(self, td):
|
||||
expected = Timedelta._from_value_and_reso(1, td._creso)
|
||||
result = td.resolution
|
||||
assert result == expected
|
||||
assert result._creso == expected._creso
|
||||
|
||||
def test_hash(self) -> None:
|
||||
# GH#54037
|
||||
second_resolution_max = Timedelta(0).as_unit("s").max
|
||||
|
||||
assert hash(second_resolution_max)
|
||||
|
||||
|
||||
def test_timedelta_class_min_max_resolution():
|
||||
# when accessed on the class (as opposed to an instance), we default
|
||||
# to nanoseconds
|
||||
assert Timedelta.min == Timedelta(NaT._value + 1)
|
||||
assert Timedelta.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
assert Timedelta.max == Timedelta(np.iinfo(np.int64).max)
|
||||
assert Timedelta.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
assert Timedelta.resolution == Timedelta(1)
|
||||
assert Timedelta.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
|
||||
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
|
||||
|
||||
# TODO: this is a test of to_timedelta string parsing
|
||||
def test_iso_conversion(self):
|
||||
# GH #21877
|
||||
expected = Timedelta(1, unit="s")
|
||||
assert to_timedelta("P0DT0H0M1S") == expected
|
||||
|
||||
# TODO: this is a test of to_timedelta returning NaT
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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())
|
||||
|
||||
@pytest.mark.skip_ubsan
|
||||
@pytest.mark.xfail(
|
||||
reason="pd.Timedelta violates the Python hash invariant (GH#44504).",
|
||||
)
|
||||
@given(
|
||||
st.integers(
|
||||
min_value=(-sys.maxsize - 1) // 500,
|
||||
max_value=sys.maxsize // 500,
|
||||
)
|
||||
)
|
||||
def test_hash_equality_invariance(self, half_microseconds: int) -> None:
|
||||
# GH#44504
|
||||
|
||||
nanoseconds = half_microseconds * 500
|
||||
|
||||
pandas_timedelta = Timedelta(nanoseconds)
|
||||
numpy_timedelta = np.timedelta64(nanoseconds)
|
||||
|
||||
# See: https://docs.python.org/3/glossary.html#term-hashable
|
||||
# Hashable objects which compare equal must have the same hash value.
|
||||
assert pandas_timedelta != numpy_timedelta or hash(pandas_timedelta) == hash(
|
||||
numpy_timedelta
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
msg = "Cannot cast -9223372036854775809 from ns to 'ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=msg):
|
||||
Timedelta(min_td._value - 2, "ns")
|
||||
|
||||
msg = "Cannot cast 9223372036854775808 from ns to 'ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsTimedelta, 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 == "min"
|
||||
assert Timedelta(days=1, seconds=6).resolution_string == "s"
|
||||
assert Timedelta(days=1, milliseconds=6).resolution_string == "ms"
|
||||
assert Timedelta(days=1, microseconds=6).resolution_string == "us"
|
||||
assert Timedelta(days=1, nanoseconds=6).resolution_string == "ns"
|
||||
|
||||
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.
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,86 @@
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
|
||||
from pandas import Timestamp
|
||||
|
||||
|
||||
class TestTimestampAsUnit:
|
||||
def test_as_unit(self):
|
||||
ts = Timestamp("1970-01-01").as_unit("ns")
|
||||
assert ts.unit == "ns"
|
||||
|
||||
assert ts.as_unit("ns") is ts
|
||||
|
||||
res = ts.as_unit("us")
|
||||
assert res._value == ts._value // 1000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == ts._value
|
||||
assert rt._creso == ts._creso
|
||||
|
||||
res = ts.as_unit("ms")
|
||||
assert res._value == ts._value // 1_000_000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == ts._value
|
||||
assert rt._creso == ts._creso
|
||||
|
||||
res = ts.as_unit("s")
|
||||
assert res._value == ts._value // 1_000_000_000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
|
||||
rt = res.as_unit("ns")
|
||||
assert rt._value == ts._value
|
||||
assert rt._creso == ts._creso
|
||||
|
||||
def test_as_unit_overflows(self):
|
||||
# microsecond that would be just out of bounds for nano
|
||||
us = 9223372800000000
|
||||
ts = Timestamp._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value, None)
|
||||
|
||||
msg = "Cannot cast 2262-04-12 00:00:00 to unit='ns' without overflow"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
ts.as_unit("ns")
|
||||
|
||||
res = ts.as_unit("ms")
|
||||
assert res._value == us // 1000
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
|
||||
def test_as_unit_rounding(self):
|
||||
ts = Timestamp(1_500_000) # i.e. 1500 microseconds
|
||||
res = ts.as_unit("ms")
|
||||
|
||||
expected = Timestamp(1_000_000) # i.e. 1 millisecond
|
||||
assert res == expected
|
||||
|
||||
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
assert res._value == 1
|
||||
|
||||
with pytest.raises(ValueError, match="Cannot losslessly convert units"):
|
||||
ts.as_unit("ms", round_ok=False)
|
||||
|
||||
def test_as_unit_non_nano(self):
|
||||
# case where we are going neither to nor from nano
|
||||
ts = Timestamp("1970-01-02").as_unit("ms")
|
||||
assert ts.year == 1970
|
||||
assert ts.month == 1
|
||||
assert ts.day == 2
|
||||
assert ts.hour == ts.minute == ts.second == ts.microsecond == ts.nanosecond == 0
|
||||
|
||||
res = ts.as_unit("s")
|
||||
assert res._value == 24 * 3600
|
||||
assert res.year == 1970
|
||||
assert res.month == 1
|
||||
assert res.day == 2
|
||||
assert (
|
||||
res.hour
|
||||
== res.minute
|
||||
== res.second
|
||||
== res.microsecond
|
||||
== res.nanosecond
|
||||
== 0
|
||||
)
|
@ -0,0 +1,22 @@
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs import Timestamp
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
|
||||
|
||||
class TestTimestampNormalize:
|
||||
@pytest.mark.parametrize("arg", ["2013-11-30", "2013-11-30 12:00:00"])
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_normalize(self, tz_naive_fixture, arg, unit):
|
||||
tz = tz_naive_fixture
|
||||
ts = Timestamp(arg, tz=tz).as_unit(unit)
|
||||
result = ts.normalize()
|
||||
expected = Timestamp("2013-11-30", tz=tz)
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
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
|
@ -0,0 +1,193 @@
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.tz import gettz
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas._libs.tslibs import (
|
||||
OutOfBoundsDatetime,
|
||||
Timestamp,
|
||||
conversion,
|
||||
)
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampReplace:
|
||||
def test_replace_out_of_pydatetime_bounds(self):
|
||||
# GH#50348
|
||||
ts = Timestamp("2016-01-01").as_unit("ns")
|
||||
|
||||
msg = "Out of bounds timestamp: 99999-01-01 00:00:00 with frequency 'ns'"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
ts.replace(year=99_999)
|
||||
|
||||
ts = ts.as_unit("ms")
|
||||
result = ts.replace(year=99_999)
|
||||
assert result.year == 99_999
|
||||
assert result._value == Timestamp(np.datetime64("99999-01-01", "ms"))._value
|
||||
|
||||
def test_replace_non_nano(self):
|
||||
ts = Timestamp._from_value_and_reso(
|
||||
91514880000000000, NpyDatetimeUnit.NPY_FR_us.value, None
|
||||
)
|
||||
assert ts.to_pydatetime() == datetime(4869, 12, 28)
|
||||
|
||||
result = ts.replace(year=4900)
|
||||
assert result._creso == ts._creso
|
||||
assert result.to_pydatetime() == datetime(4900, 12, 28)
|
||||
|
||||
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
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_replace_dst_border(self, unit):
|
||||
# Gh 7825
|
||||
t = Timestamp("2013-11-3", tz="America/Chicago").as_unit(unit)
|
||||
result = t.replace(hour=3)
|
||||
expected = Timestamp("2013-11-3 03:00:00", tz="America/Chicago")
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
@pytest.mark.parametrize("fold", [0, 1])
|
||||
@pytest.mark.parametrize("tz", ["dateutil/Europe/London", "Europe/London"])
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_replace_dst_fold(self, fold, tz, unit):
|
||||
# GH 25017
|
||||
d = datetime(2019, 10, 27, 2, 30)
|
||||
ts = Timestamp(d, tz=tz).as_unit(unit)
|
||||
result = ts.replace(hour=1, fold=fold)
|
||||
expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize(
|
||||
tz, ambiguous=not fold
|
||||
)
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
@pytest.mark.parametrize("fold", [0, 1])
|
||||
def test_replace_preserves_fold(self, 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
|
@ -0,0 +1,383 @@
|
||||
from hypothesis import (
|
||||
given,
|
||||
strategies as st,
|
||||
)
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas._libs import lib
|
||||
from pandas._libs.tslibs import (
|
||||
NaT,
|
||||
OutOfBoundsDatetime,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
iNaT,
|
||||
to_offset,
|
||||
)
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas._libs.tslibs.period import INVALID_FREQ_ERR_MSG
|
||||
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampRound:
|
||||
def test_round_division_by_zero_raises(self):
|
||||
ts = Timestamp("2016-01-01")
|
||||
|
||||
msg = "Division by zero in rounding"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
ts.round("0ns")
|
||||
|
||||
@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", "2min", "2018-01-01 00:02:00"),
|
||||
("2018-01-01 00:04:00", "4min", "2018-01-01 00:04:00"),
|
||||
("2018-01-01 00:15:00", "15min", "2018-01-01 00:15:00"),
|
||||
("2018-01-01 00:20:00", "20min", "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
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_ceil(self, unit):
|
||||
dt = Timestamp("20130101 09:10:11").as_unit(unit)
|
||||
result = dt.ceil("D")
|
||||
expected = Timestamp("20130102")
|
||||
assert result == expected
|
||||
assert result._creso == dt._creso
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_floor(self, unit):
|
||||
dt = Timestamp("20130101 09:10:11").as_unit(unit)
|
||||
result = dt.floor("D")
|
||||
expected = Timestamp("20130101")
|
||||
assert result == expected
|
||||
assert result._creso == dt._creso
|
||||
|
||||
@pytest.mark.parametrize("method", ["ceil", "round", "floor"])
|
||||
@pytest.mark.parametrize(
|
||||
"unit",
|
||||
["ns", "us", "ms", "s"],
|
||||
)
|
||||
def test_round_dst_border_ambiguous(self, method, unit):
|
||||
# GH 18946 round near "fall back" DST
|
||||
ts = Timestamp("2017-10-29 00:00:00", tz="UTC").tz_convert("Europe/Madrid")
|
||||
ts = ts.as_unit(unit)
|
||||
#
|
||||
result = getattr(ts, method)("h", ambiguous=True)
|
||||
assert result == ts
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
result = getattr(ts, method)("h", ambiguous=False)
|
||||
expected = Timestamp("2017-10-29 01:00:00", tz="UTC").tz_convert(
|
||||
"Europe/Madrid"
|
||||
)
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
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"],
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"unit",
|
||||
["ns", "us", "ms", "s"],
|
||||
)
|
||||
def test_round_dst_border_nonexistent(self, method, ts_str, freq, unit):
|
||||
# GH 23324 round near "spring forward" DST
|
||||
ts = Timestamp(ts_str, tz="America/Chicago").as_unit(unit)
|
||||
result = getattr(ts, method)(freq, nonexistent="shift_forward")
|
||||
expected = Timestamp("2018-03-11 03:00:00", tz="America/Chicago")
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
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).as_unit("ns")
|
||||
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
|
||||
|
||||
msg = "Cannot round 1677-09-21 00:12:43.145224193 to freq=<Second>"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.min.floor("s")
|
||||
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.min.round("s")
|
||||
|
||||
msg = "Cannot round 2262-04-11 23:47:16.854775807 to freq=<Second>"
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.max.ceil("s")
|
||||
|
||||
with pytest.raises(OutOfBoundsDatetime, match=msg):
|
||||
Timestamp.max.round("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):
|
||||
cls = Timestamp
|
||||
err_cls = OutOfBoundsDatetime
|
||||
|
||||
val = np.int64(val)
|
||||
ts = cls(val)
|
||||
|
||||
def checker(ts, nanos, unit):
|
||||
# First check that we do raise in cases where we should
|
||||
if nanos == 1:
|
||||
pass
|
||||
else:
|
||||
div, mod = divmod(ts._value, nanos)
|
||||
diff = int(nanos - mod)
|
||||
lb = ts._value - mod
|
||||
assert lb <= ts._value # i.e. no overflows with python ints
|
||||
ub = ts._value + diff
|
||||
assert ub > ts._value # i.e. no overflows with python ints
|
||||
|
||||
msg = "without overflow"
|
||||
if mod == 0:
|
||||
# We should never be raising in this
|
||||
pass
|
||||
elif method is cls.ceil:
|
||||
if ub > cls.max._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif method is cls.floor:
|
||||
if lb < cls.min._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif mod >= diff:
|
||||
if ub > cls.max._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
elif lb < cls.min._value:
|
||||
with pytest.raises(err_cls, match=msg):
|
||||
method(ts, unit)
|
||||
return
|
||||
|
||||
res = method(ts, unit)
|
||||
|
||||
td = res - ts
|
||||
diff = abs(td._value)
|
||||
assert diff < nanos
|
||||
assert res._value % nanos == 0
|
||||
|
||||
if method is cls.round:
|
||||
assert diff <= nanos / 2
|
||||
elif method is cls.floor:
|
||||
assert res <= ts
|
||||
elif method is cls.ceil:
|
||||
assert res >= ts
|
||||
|
||||
nanos = 1
|
||||
checker(ts, nanos, "ns")
|
||||
|
||||
nanos = 1000
|
||||
checker(ts, nanos, "us")
|
||||
|
||||
nanos = 1_000_000
|
||||
checker(ts, nanos, "ms")
|
||||
|
||||
nanos = 1_000_000_000
|
||||
checker(ts, nanos, "s")
|
||||
|
||||
nanos = 60 * 1_000_000_000
|
||||
checker(ts, nanos, "min")
|
||||
|
||||
nanos = 60 * 60 * 1_000_000_000
|
||||
checker(ts, nanos, "h")
|
||||
|
||||
nanos = 24 * 60 * 60 * 1_000_000_000
|
||||
checker(ts, nanos, "D")
|
@ -0,0 +1,31 @@
|
||||
# NB: This is for the Timestamp.timestamp *method* specifically, not
|
||||
# the Timestamp class in general.
|
||||
|
||||
from pytz import utc
|
||||
|
||||
from pandas._libs.tslibs import Timestamp
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampMethod:
|
||||
@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()
|
@ -0,0 +1,28 @@
|
||||
from pandas import Timestamp
|
||||
|
||||
|
||||
class TestTimestampToJulianDate:
|
||||
def test_compare_1700(self):
|
||||
ts = Timestamp("1700-06-23")
|
||||
res = ts.to_julian_date()
|
||||
assert res == 2_342_145.5
|
||||
|
||||
def test_compare_2000(self):
|
||||
ts = Timestamp("2000-04-12")
|
||||
res = ts.to_julian_date()
|
||||
assert res == 2_451_646.5
|
||||
|
||||
def test_compare_2100(self):
|
||||
ts = Timestamp("2100-08-12")
|
||||
res = ts.to_julian_date()
|
||||
assert res == 2_488_292.5
|
||||
|
||||
def test_compare_hour01(self):
|
||||
ts = Timestamp("2000-08-12T01:00:00")
|
||||
res = ts.to_julian_date()
|
||||
assert res == 2_451_768.5416666666666666
|
||||
|
||||
def test_compare_hour13(self):
|
||||
ts = Timestamp("2000-08-12T13:00:00")
|
||||
res = ts.to_julian_date()
|
||||
assert res == 2_451_769.0416666666666666
|
@ -0,0 +1,81 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
)
|
||||
|
||||
import pytz
|
||||
|
||||
from pandas._libs.tslibs.timezones import dateutil_gettz as gettz
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
from pandas import Timestamp
|
||||
import pandas._testing as tm
|
||||
|
||||
|
||||
class TestTimestampToPyDatetime:
|
||||
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_pydatetime_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_pydatetime_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_pydatetime_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_pydatetime_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).as_unit("ns")._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).as_unit("ns")._value / 1000
|
||||
== Timestamp.min._value / 1000
|
||||
)
|
@ -0,0 +1,51 @@
|
||||
import dateutil
|
||||
import pytest
|
||||
|
||||
from pandas._libs.tslibs import timezones
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
from pandas import Timestamp
|
||||
|
||||
|
||||
class TestTimestampTZConvert:
|
||||
@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)
|
||||
|
||||
@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)
|
||||
|
||||
@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())
|
@ -0,0 +1,351 @@
|
||||
from datetime import timedelta
|
||||
import re
|
||||
|
||||
from dateutil.tz import gettz
|
||||
import pytest
|
||||
import pytz
|
||||
from pytz.exceptions import (
|
||||
AmbiguousTimeError,
|
||||
NonExistentTimeError,
|
||||
)
|
||||
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas.errors import OutOfBoundsDatetime
|
||||
|
||||
from pandas import (
|
||||
NaT,
|
||||
Timestamp,
|
||||
)
|
||||
|
||||
try:
|
||||
from zoneinfo import ZoneInfo
|
||||
except ImportError:
|
||||
# Cannot assign to a type
|
||||
ZoneInfo = None # type: ignore[misc, assignment]
|
||||
|
||||
|
||||
class TestTimestampTZLocalize:
|
||||
@pytest.mark.skip_ubsan
|
||||
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")
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_tz_localize_ambiguous_bool(self, unit):
|
||||
# make sure that we are correctly accepting bool values as ambiguous
|
||||
# GH#14402
|
||||
ts = Timestamp("2015-11-01 01:00:03").as_unit(unit)
|
||||
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")
|
||||
|
||||
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
||||
ts.tz_localize("dateutil/US/Central")
|
||||
|
||||
if ZoneInfo is not None:
|
||||
try:
|
||||
tz = ZoneInfo("US/Central")
|
||||
except KeyError:
|
||||
# no tzdata
|
||||
pass
|
||||
else:
|
||||
with pytest.raises(pytz.AmbiguousTimeError, match=msg):
|
||||
ts.tz_localize(tz)
|
||||
|
||||
result = ts.tz_localize("US/Central", ambiguous=True)
|
||||
assert result == expected0
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
result = ts.tz_localize("US/Central", ambiguous=False)
|
||||
assert result == expected1
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
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 == 3600
|
||||
msg = re.escape(
|
||||
"'ambiguous' parameter must be one of: "
|
||||
"True, False, 'NaT', 'raise' (default)"
|
||||
)
|
||||
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
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"stamp, tz, forward_expected, backward_expected",
|
||||
[
|
||||
(
|
||||
"2015-03-29 02:00:00",
|
||||
"Europe/Warsaw",
|
||||
"2015-03-29 03:00:00",
|
||||
"2015-03-29 01:59:59",
|
||||
), # utc+1 -> utc+2
|
||||
(
|
||||
"2023-03-12 02:00:00",
|
||||
"America/Los_Angeles",
|
||||
"2023-03-12 03:00:00",
|
||||
"2023-03-12 01:59:59",
|
||||
), # utc-8 -> utc-7
|
||||
(
|
||||
"2023-03-26 01:00:00",
|
||||
"Europe/London",
|
||||
"2023-03-26 02:00:00",
|
||||
"2023-03-26 00:59:59",
|
||||
), # utc+0 -> utc+1
|
||||
(
|
||||
"2023-03-26 00:00:00",
|
||||
"Atlantic/Azores",
|
||||
"2023-03-26 01:00:00",
|
||||
"2023-03-25 23:59:59",
|
||||
), # utc-1 -> utc+0
|
||||
],
|
||||
)
|
||||
def test_tz_localize_nonexistent_shift(
|
||||
self, stamp, tz, forward_expected, backward_expected
|
||||
):
|
||||
ts = Timestamp(stamp)
|
||||
forward_ts = ts.tz_localize(tz, nonexistent="shift_forward")
|
||||
assert forward_ts == Timestamp(forward_expected, tz=tz)
|
||||
backward_ts = ts.tz_localize(tz, nonexistent="shift_backward")
|
||||
assert backward_ts == Timestamp(backward_expected, tz=tz)
|
||||
|
||||
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, warsaw):
|
||||
# GH 22644
|
||||
tz = 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=False)
|
||||
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=False)
|
||||
assert result_pytz._value == result_dateutil._value
|
||||
assert result_pytz._value == 1382835600
|
||||
|
||||
# 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=True)
|
||||
result_dateutil = naive.tz_localize(dateutil_zone, ambiguous=True)
|
||||
assert result_pytz._value == result_dateutil._value
|
||||
assert result_pytz._value == 1382832000
|
||||
|
||||
# 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/"])
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_timestamp_tz_localize_nonexistent_shift(
|
||||
self, start_ts, tz, end_ts, shift, tz_type, unit
|
||||
):
|
||||
# GH 8917, 24466
|
||||
tz = tz_type + tz
|
||||
if isinstance(shift, str):
|
||||
shift = "shift_" + shift
|
||||
ts = Timestamp(start_ts).as_unit(unit)
|
||||
result = ts.tz_localize(tz, nonexistent=shift)
|
||||
expected = Timestamp(end_ts).tz_localize(tz)
|
||||
|
||||
if unit == "us":
|
||||
assert result == expected.replace(nanosecond=0)
|
||||
elif unit == "ms":
|
||||
micros = expected.microsecond - expected.microsecond % 1000
|
||||
assert result == expected.replace(microsecond=micros, nanosecond=0)
|
||||
elif unit == "s":
|
||||
assert result == expected.replace(microsecond=0, nanosecond=0)
|
||||
else:
|
||||
assert result == expected
|
||||
assert result._creso == getattr(NpyDatetimeUnit, f"NPY_FR_{unit}").value
|
||||
|
||||
@pytest.mark.parametrize("offset", [-1, 1])
|
||||
def test_timestamp_tz_localize_nonexistent_shift_invalid(self, offset, warsaw):
|
||||
# GH 8917, 24466
|
||||
tz = 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("unit", ["ns", "us", "ms", "s"])
|
||||
def test_timestamp_tz_localize_nonexistent_NaT(self, warsaw, unit):
|
||||
# GH 8917
|
||||
tz = warsaw
|
||||
ts = Timestamp("2015-03-29 02:20:00").as_unit(unit)
|
||||
result = ts.tz_localize(tz, nonexistent="NaT")
|
||||
assert result is NaT
|
||||
|
||||
@pytest.mark.parametrize("unit", ["ns", "us", "ms", "s"])
|
||||
def test_timestamp_tz_localize_nonexistent_raise(self, warsaw, unit):
|
||||
# GH 8917
|
||||
tz = warsaw
|
||||
ts = Timestamp("2015-03-29 02:20:00").as_unit(unit)
|
||||
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")
|
@ -0,0 +1,334 @@
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
timezone,
|
||||
)
|
||||
|
||||
from dateutil.tz import gettz
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
|
||||
from pandas._libs.tslibs import (
|
||||
OutOfBoundsDatetime,
|
||||
OutOfBoundsTimedelta,
|
||||
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").as_unit("ns")
|
||||
offset_overflow = 20169940 * offsets.Day(1)
|
||||
lmsg2 = r"Cannot cast -?20169940 days \+?00:00:00 to unit='ns' without overflow"
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
||||
stamp + offset_overflow
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
||||
offset_overflow + stamp
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg2):
|
||||
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").as_unit("ns")
|
||||
offset_overflow = to_offset("D") * 100**5
|
||||
|
||||
lmsg3 = (
|
||||
r"Cannot cast -?10000000000 days \+?00:00:00 to unit='ns' without overflow"
|
||||
)
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
||||
stamp + offset_overflow
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
||||
offset_overflow + stamp
|
||||
|
||||
with pytest.raises(OutOfBoundsTimedelta, match=lmsg3):
|
||||
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").as_unit("ns")
|
||||
b = Timestamp("1688-01-01 00:00:00").as_unit("ns")
|
||||
|
||||
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)
|
||||
ts = Timestamp(dt)
|
||||
|
||||
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(
|
||||
"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):
|
||||
now = datetime.now(timezone.utc)
|
||||
ts = Timestamp(now).as_unit("ns")
|
||||
result = ts + other
|
||||
valdiff = result._value - ts._value
|
||||
assert valdiff == expected_difference
|
||||
|
||||
ts2 = Timestamp(now)
|
||||
assert ts2 + other == result
|
||||
|
||||
@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").as_unit("ns")
|
||||
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
|
||||
|
||||
@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
|
||||
|
||||
|
||||
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,313 @@
|
||||
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_compare_non_nano_dt64(self):
|
||||
# don't raise when converting dt64 to Timestamp in __richcmp__
|
||||
dt = np.datetime64("1066-10-14")
|
||||
ts = Timestamp(dt)
|
||||
|
||||
assert dt == ts
|
||||
|
||||
def test_comparison_dt64_ndarray(self):
|
||||
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()
|
||||
# in 2.0 we disallow comparing pydate objects with Timestamps,
|
||||
# following the stdlib datetime behavior.
|
||||
|
||||
msg = "Cannot compare Timestamp with datetime.date"
|
||||
for left, right in [(ts, dt), (dt, ts)]:
|
||||
assert not left == right
|
||||
assert left != right
|
||||
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left < right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left <= right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left > right
|
||||
with pytest.raises(TypeError, match=msg):
|
||||
left >= right
|
||||
|
||||
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 # pylint: disable=unneeded-not
|
||||
assert left != right
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,201 @@
|
||||
from datetime import datetime
|
||||
import pprint
|
||||
|
||||
import dateutil.tz
|
||||
import pytest
|
||||
import pytz # a test below uses pytz but only inside a `eval` call
|
||||
|
||||
from pandas import Timestamp
|
||||
|
||||
ts_no_ns = Timestamp(
|
||||
year=2019,
|
||||
month=5,
|
||||
day=18,
|
||||
hour=15,
|
||||
minute=17,
|
||||
second=8,
|
||||
microsecond=132263,
|
||||
)
|
||||
ts_no_ns_year1 = Timestamp(
|
||||
year=1,
|
||||
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_no_ns_year1, "seconds", "0001-05-18T15:17:08"),
|
||||
(ts_no_ns_year1, "nanoseconds", "0001-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
|
||||
|
||||
|
||||
class TestTimestampRendering:
|
||||
timezones = ["UTC", "Asia/Tokyo", "US/Eastern", "dateutil/America/Los_Angeles"]
|
||||
|
||||
@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))
|
||||
|
||||
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 "UTC-04:00" in repr(date_with_utc_offset)
|
||||
expr = repr(date_with_utc_offset)
|
||||
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
|
||||
|
||||
def test_repr_matches_pydatetime_no_tz(self):
|
||||
dt_date = datetime(2013, 1, 2)
|
||||
assert str(dt_date) == str(Timestamp(dt_date))
|
||||
|
||||
dt_datetime = datetime(2013, 1, 2, 12, 1, 3)
|
||||
assert str(dt_datetime) == str(Timestamp(dt_datetime))
|
||||
|
||||
dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45)
|
||||
assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
|
||||
|
||||
ts_nanos_only = Timestamp(200)
|
||||
assert str(ts_nanos_only) == "1970-01-01 00:00:00.000000200"
|
||||
|
||||
ts_nanos_micros = Timestamp(1200)
|
||||
assert str(ts_nanos_micros) == "1970-01-01 00:00:00.000001200"
|
||||
|
||||
def test_repr_matches_pydatetime_tz_pytz(self):
|
||||
dt_date = datetime(2013, 1, 2, tzinfo=pytz.utc)
|
||||
assert str(dt_date) == str(Timestamp(dt_date))
|
||||
|
||||
dt_datetime = datetime(2013, 1, 2, 12, 1, 3, tzinfo=pytz.utc)
|
||||
assert str(dt_datetime) == str(Timestamp(dt_datetime))
|
||||
|
||||
dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45, tzinfo=pytz.utc)
|
||||
assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
|
||||
|
||||
def test_repr_matches_pydatetime_tz_dateutil(self):
|
||||
utc = dateutil.tz.tzutc()
|
||||
|
||||
dt_date = datetime(2013, 1, 2, tzinfo=utc)
|
||||
assert str(dt_date) == str(Timestamp(dt_date))
|
||||
|
||||
dt_datetime = datetime(2013, 1, 2, 12, 1, 3, tzinfo=utc)
|
||||
assert str(dt_datetime) == str(Timestamp(dt_datetime))
|
||||
|
||||
dt_datetime_us = datetime(2013, 1, 2, 12, 1, 3, 45, tzinfo=utc)
|
||||
assert str(dt_datetime_us) == str(Timestamp(dt_datetime_us))
|
@ -0,0 +1,928 @@
|
||||
""" test the scalar Timestamp """
|
||||
|
||||
import calendar
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta,
|
||||
timezone,
|
||||
)
|
||||
import locale
|
||||
import time
|
||||
import unicodedata
|
||||
|
||||
from dateutil.tz import (
|
||||
tzlocal,
|
||||
tzutc,
|
||||
)
|
||||
from hypothesis import (
|
||||
given,
|
||||
strategies as st,
|
||||
)
|
||||
import numpy as np
|
||||
import pytest
|
||||
import pytz
|
||||
from pytz import utc
|
||||
|
||||
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
|
||||
from pandas._libs.tslibs.timezones import (
|
||||
dateutil_gettz as gettz,
|
||||
get_timezone,
|
||||
maybe_get_tz,
|
||||
tz_compare,
|
||||
)
|
||||
from pandas.compat import IS64
|
||||
|
||||
from pandas import (
|
||||
NaT,
|
||||
Timedelta,
|
||||
Timestamp,
|
||||
)
|
||||
import pandas._testing as tm
|
||||
|
||||
from pandas.tseries import offsets
|
||||
from pandas.tseries.frequencies import to_offset
|
||||
|
||||
|
||||
class TestTimestampProperties:
|
||||
def test_properties_business(self):
|
||||
freq = to_offset("B")
|
||||
|
||||
ts = Timestamp("2017-10-01")
|
||||
assert ts.dayofweek == 6
|
||||
assert ts.day_of_week == 6
|
||||
assert ts.is_month_start # not a weekday
|
||||
assert not freq.is_month_start(ts)
|
||||
assert freq.is_month_start(ts + Timedelta(days=1))
|
||||
assert not freq.is_quarter_start(ts)
|
||||
assert freq.is_quarter_start(ts + Timedelta(days=1))
|
||||
|
||||
ts = Timestamp("2017-09-30")
|
||||
assert ts.dayofweek == 5
|
||||
assert ts.day_of_week == 5
|
||||
assert ts.is_month_end
|
||||
assert not freq.is_month_end(ts)
|
||||
assert freq.is_month_end(ts - Timedelta(days=1))
|
||||
assert ts.is_quarter_end
|
||||
assert not freq.is_quarter_end(ts)
|
||||
assert freq.is_quarter_end(ts - Timedelta(days=1))
|
||||
|
||||
@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() # 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
|
||||
if not IS64 and tz == tzlocal():
|
||||
# https://github.com/dateutil/dateutil/issues/197
|
||||
pytest.skip(
|
||||
"tzlocal() on a 32 bit platform causes internal overflow errors"
|
||||
)
|
||||
# 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.000000000")
|
||||
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)
|
||||
|
||||
assert dt.as_unit("us").resolution == Timedelta(microseconds=1)
|
||||
assert dt.as_unit("ms").resolution == Timedelta(milliseconds=1)
|
||||
assert dt.as_unit("s").resolution == Timedelta(seconds=1)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"date_string, expected",
|
||||
[
|
||||
("0000-2-29", 1),
|
||||
("0000-3-1", 2),
|
||||
("1582-10-14", 3),
|
||||
("-0040-1-1", 4),
|
||||
("2023-06-18", 6),
|
||||
],
|
||||
)
|
||||
def test_dow_historic(self, date_string, expected):
|
||||
# GH 53738
|
||||
ts = Timestamp(date_string)
|
||||
dow = ts.weekday()
|
||||
assert dow == expected
|
||||
|
||||
@given(
|
||||
ts=st.datetimes(),
|
||||
sign=st.sampled_from(["-", ""]),
|
||||
)
|
||||
def test_dow_parametric(self, ts, sign):
|
||||
# GH 53738
|
||||
ts = (
|
||||
f"{sign}{str(ts.year).zfill(4)}"
|
||||
f"-{str(ts.month).zfill(2)}"
|
||||
f"-{str(ts.day).zfill(2)}"
|
||||
)
|
||||
result = Timestamp(ts).weekday()
|
||||
expected = (
|
||||
(np.datetime64(ts) - np.datetime64("1970-01-01")).astype("int64") - 4
|
||||
) % 7
|
||||
assert result == expected
|
||||
|
||||
|
||||
class TestTimestamp:
|
||||
@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
|
||||
|
||||
def test_default_to_stdlib_utc(self):
|
||||
assert Timestamp.utcnow().tz is timezone.utc
|
||||
assert Timestamp.now("UTC").tz is timezone.utc
|
||||
assert Timestamp("2016-01-01", tz="UTC").tz is timezone.utc
|
||||
|
||||
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 timezone.utc
|
||||
|
||||
def test_asm8(self):
|
||||
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(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(pytz.timezone("UTC")))
|
||||
compare(Timestamp.now("UTC"), datetime.now(tzutc()))
|
||||
compare(Timestamp.utcnow(), datetime.now(timezone.utc))
|
||||
compare(Timestamp.today(), datetime.today())
|
||||
current_time = calendar.timegm(datetime.now().utctimetuple())
|
||||
|
||||
ts_utc = Timestamp.utcfromtimestamp(current_time)
|
||||
assert ts_utc.timestamp() == 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.now(timezone.utc)
|
||||
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
|
||||
|
||||
def test_roundtrip(self):
|
||||
# test value to string and back conversions
|
||||
# further test accessors
|
||||
base = Timestamp("20140101 00:00:00").as_unit("ns")
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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 TestTimestampConversion:
|
||||
def test_conversion(self):
|
||||
# GH#9255
|
||||
ts = Timestamp("2000-01-01").as_unit("ns")
|
||||
|
||||
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_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 TestNonNano:
|
||||
@pytest.fixture(params=["s", "ms", "us"])
|
||||
def reso(self, request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture
|
||||
def dt64(self, reso):
|
||||
# cases that are in-bounds for nanosecond, so we can compare against
|
||||
# the existing implementation.
|
||||
return np.datetime64("2016-01-01", reso)
|
||||
|
||||
@pytest.fixture
|
||||
def ts(self, dt64):
|
||||
return Timestamp._from_dt64(dt64)
|
||||
|
||||
@pytest.fixture
|
||||
def ts_tz(self, ts, tz_aware_fixture):
|
||||
tz = maybe_get_tz(tz_aware_fixture)
|
||||
return Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
|
||||
|
||||
def test_non_nano_construction(self, dt64, ts, reso):
|
||||
assert ts._value == dt64.view("i8")
|
||||
|
||||
if reso == "s":
|
||||
assert ts._creso == NpyDatetimeUnit.NPY_FR_s.value
|
||||
elif reso == "ms":
|
||||
assert ts._creso == NpyDatetimeUnit.NPY_FR_ms.value
|
||||
elif reso == "us":
|
||||
assert ts._creso == NpyDatetimeUnit.NPY_FR_us.value
|
||||
|
||||
def test_non_nano_fields(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
|
||||
assert ts.year == alt.year
|
||||
assert ts.month == alt.month
|
||||
assert ts.day == alt.day
|
||||
assert ts.hour == ts.minute == ts.second == ts.microsecond == 0
|
||||
assert ts.nanosecond == 0
|
||||
|
||||
assert ts.to_julian_date() == alt.to_julian_date()
|
||||
assert ts.weekday() == alt.weekday()
|
||||
assert ts.isoweekday() == alt.isoweekday()
|
||||
|
||||
def test_start_end_fields(self, ts):
|
||||
assert ts.is_year_start
|
||||
assert ts.is_quarter_start
|
||||
assert ts.is_month_start
|
||||
assert not ts.is_year_end
|
||||
assert not ts.is_month_end
|
||||
assert not ts.is_month_end
|
||||
|
||||
# 2016-01-01 is a Friday, so is year/quarter/month start with this freq
|
||||
assert ts.is_year_start
|
||||
assert ts.is_quarter_start
|
||||
assert ts.is_month_start
|
||||
assert not ts.is_year_end
|
||||
assert not ts.is_month_end
|
||||
assert not ts.is_month_end
|
||||
|
||||
def test_day_name(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
assert ts.day_name() == alt.day_name()
|
||||
|
||||
def test_month_name(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
assert ts.month_name() == alt.month_name()
|
||||
|
||||
def test_tz_convert(self, ts):
|
||||
ts = Timestamp._from_value_and_reso(ts._value, ts._creso, utc)
|
||||
|
||||
tz = pytz.timezone("US/Pacific")
|
||||
result = ts.tz_convert(tz)
|
||||
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == ts._creso
|
||||
assert tz_compare(result.tz, tz)
|
||||
|
||||
def test_repr(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
|
||||
assert str(ts) == str(alt)
|
||||
assert repr(ts) == repr(alt)
|
||||
|
||||
def test_comparison(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
|
||||
assert ts == dt64
|
||||
assert dt64 == ts
|
||||
assert ts == alt
|
||||
assert alt == ts
|
||||
|
||||
assert not ts != dt64
|
||||
assert not dt64 != ts
|
||||
assert not ts != alt
|
||||
assert not alt != ts
|
||||
|
||||
assert not ts < dt64
|
||||
assert not dt64 < ts
|
||||
assert not ts < alt
|
||||
assert not alt < ts
|
||||
|
||||
assert not ts > dt64
|
||||
assert not dt64 > ts
|
||||
assert not ts > alt
|
||||
assert not alt > ts
|
||||
|
||||
assert ts >= dt64
|
||||
assert dt64 >= ts
|
||||
assert ts >= alt
|
||||
assert alt >= ts
|
||||
|
||||
assert ts <= dt64
|
||||
assert dt64 <= ts
|
||||
assert ts <= alt
|
||||
assert alt <= ts
|
||||
|
||||
def test_cmp_cross_reso(self):
|
||||
# numpy gets this wrong because of silent overflow
|
||||
dt64 = np.datetime64(9223372800, "s") # won't fit in M8[ns]
|
||||
ts = Timestamp._from_dt64(dt64)
|
||||
|
||||
# subtracting 3600*24 gives a datetime64 that _can_ fit inside the
|
||||
# nanosecond implementation bounds.
|
||||
other = Timestamp(dt64 - 3600 * 24).as_unit("ns")
|
||||
assert other < ts
|
||||
assert other.asm8 > ts.asm8 # <- numpy gets this wrong
|
||||
assert ts > other
|
||||
assert ts.asm8 < other.asm8 # <- numpy gets this wrong
|
||||
assert not other == ts
|
||||
assert ts != other
|
||||
|
||||
@pytest.mark.xfail(reason="Dispatches to np.datetime64 which is wrong")
|
||||
def test_cmp_cross_reso_reversed_dt64(self):
|
||||
dt64 = np.datetime64(106752, "D") # won't fit in M8[ns]
|
||||
ts = Timestamp._from_dt64(dt64)
|
||||
other = Timestamp(dt64 - 1)
|
||||
|
||||
assert other.asm8 < ts
|
||||
|
||||
def test_pickle(self, ts, tz_aware_fixture):
|
||||
tz = tz_aware_fixture
|
||||
tz = maybe_get_tz(tz)
|
||||
ts = Timestamp._from_value_and_reso(ts._value, ts._creso, tz)
|
||||
rt = tm.round_trip_pickle(ts)
|
||||
assert rt._creso == ts._creso
|
||||
assert rt == ts
|
||||
|
||||
def test_normalize(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
result = ts.normalize()
|
||||
assert result._creso == ts._creso
|
||||
assert result == alt.normalize()
|
||||
|
||||
def test_asm8(self, dt64, ts):
|
||||
rt = ts.asm8
|
||||
assert rt == dt64
|
||||
assert rt.dtype == dt64.dtype
|
||||
|
||||
def test_to_numpy(self, dt64, ts):
|
||||
res = ts.to_numpy()
|
||||
assert res == dt64
|
||||
assert res.dtype == dt64.dtype
|
||||
|
||||
def test_to_datetime64(self, dt64, ts):
|
||||
res = ts.to_datetime64()
|
||||
assert res == dt64
|
||||
assert res.dtype == dt64.dtype
|
||||
|
||||
def test_timestamp(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
assert ts.timestamp() == alt.timestamp()
|
||||
|
||||
def test_to_period(self, dt64, ts):
|
||||
alt = Timestamp(dt64)
|
||||
assert ts.to_period("D") == alt.to_period("D")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"td", [timedelta(days=4), Timedelta(days=4), np.timedelta64(4, "D")]
|
||||
)
|
||||
def test_addsub_timedeltalike_non_nano(self, dt64, ts, td):
|
||||
exp_reso = max(ts._creso, Timedelta(td)._creso)
|
||||
|
||||
result = ts - td
|
||||
expected = Timestamp(dt64) - td
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == exp_reso
|
||||
assert result == expected
|
||||
|
||||
result = ts + td
|
||||
expected = Timestamp(dt64) + td
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == exp_reso
|
||||
assert result == expected
|
||||
|
||||
result = td + ts
|
||||
expected = td + Timestamp(dt64)
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == exp_reso
|
||||
assert result == expected
|
||||
|
||||
def test_addsub_offset(self, ts_tz):
|
||||
# specifically non-Tick offset
|
||||
off = offsets.YearEnd(1)
|
||||
result = ts_tz + off
|
||||
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == ts_tz._creso
|
||||
if ts_tz.month == 12 and ts_tz.day == 31:
|
||||
assert result.year == ts_tz.year + 1
|
||||
else:
|
||||
assert result.year == ts_tz.year
|
||||
assert result.day == 31
|
||||
assert result.month == 12
|
||||
assert tz_compare(result.tz, ts_tz.tz)
|
||||
|
||||
result = ts_tz - off
|
||||
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result._creso == ts_tz._creso
|
||||
assert result.year == ts_tz.year - 1
|
||||
assert result.day == 31
|
||||
assert result.month == 12
|
||||
assert tz_compare(result.tz, ts_tz.tz)
|
||||
|
||||
def test_sub_datetimelike_mismatched_reso(self, ts_tz):
|
||||
# case with non-lossy rounding
|
||||
ts = ts_tz
|
||||
|
||||
# choose a unit for `other` that doesn't match ts_tz's;
|
||||
# this construction ensures we get cases with other._creso < ts._creso
|
||||
# and cases with other._creso > ts._creso
|
||||
unit = {
|
||||
NpyDatetimeUnit.NPY_FR_us.value: "ms",
|
||||
NpyDatetimeUnit.NPY_FR_ms.value: "s",
|
||||
NpyDatetimeUnit.NPY_FR_s.value: "us",
|
||||
}[ts._creso]
|
||||
other = ts.as_unit(unit)
|
||||
assert other._creso != ts._creso
|
||||
|
||||
result = ts - other
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result._value == 0
|
||||
assert result._creso == max(ts._creso, other._creso)
|
||||
|
||||
result = other - ts
|
||||
assert isinstance(result, Timedelta)
|
||||
assert result._value == 0
|
||||
assert result._creso == max(ts._creso, other._creso)
|
||||
|
||||
if ts._creso < other._creso:
|
||||
# Case where rounding is lossy
|
||||
other2 = other + Timedelta._from_value_and_reso(1, other._creso)
|
||||
exp = ts.as_unit(other.unit) - other2
|
||||
|
||||
res = ts - other2
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
|
||||
res = other2 - ts
|
||||
assert res == -exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
else:
|
||||
ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
|
||||
exp = ts2 - other.as_unit(ts2.unit)
|
||||
|
||||
res = ts2 - other
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
res = other - ts2
|
||||
assert res == -exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
|
||||
def test_sub_timedeltalike_mismatched_reso(self, ts_tz):
|
||||
# case with non-lossy rounding
|
||||
ts = ts_tz
|
||||
|
||||
# choose a unit for `other` that doesn't match ts_tz's;
|
||||
# this construction ensures we get cases with other._creso < ts._creso
|
||||
# and cases with other._creso > ts._creso
|
||||
unit = {
|
||||
NpyDatetimeUnit.NPY_FR_us.value: "ms",
|
||||
NpyDatetimeUnit.NPY_FR_ms.value: "s",
|
||||
NpyDatetimeUnit.NPY_FR_s.value: "us",
|
||||
}[ts._creso]
|
||||
other = Timedelta(0).as_unit(unit)
|
||||
assert other._creso != ts._creso
|
||||
|
||||
result = ts + other
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result == ts
|
||||
assert result._creso == max(ts._creso, other._creso)
|
||||
|
||||
result = other + ts
|
||||
assert isinstance(result, Timestamp)
|
||||
assert result == ts
|
||||
assert result._creso == max(ts._creso, other._creso)
|
||||
|
||||
if ts._creso < other._creso:
|
||||
# Case where rounding is lossy
|
||||
other2 = other + Timedelta._from_value_and_reso(1, other._creso)
|
||||
exp = ts.as_unit(other.unit) + other2
|
||||
res = ts + other2
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
res = other2 + ts
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
else:
|
||||
ts2 = ts + Timedelta._from_value_and_reso(1, ts._creso)
|
||||
exp = ts2 + other.as_unit(ts2.unit)
|
||||
|
||||
res = ts2 + other
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
res = other + ts2
|
||||
assert res == exp
|
||||
assert res._creso == max(ts._creso, other._creso)
|
||||
|
||||
def test_addition_doesnt_downcast_reso(self):
|
||||
# https://github.com/pandas-dev/pandas/pull/48748#pullrequestreview-1122635413
|
||||
ts = Timestamp(year=2022, month=1, day=1, microsecond=999999).as_unit("us")
|
||||
td = Timedelta(microseconds=1).as_unit("us")
|
||||
res = ts + td
|
||||
assert res._creso == ts._creso
|
||||
|
||||
def test_sub_timedelta64_mismatched_reso(self, ts_tz):
|
||||
ts = ts_tz
|
||||
|
||||
res = ts + np.timedelta64(1, "ns")
|
||||
exp = ts.as_unit("ns") + np.timedelta64(1, "ns")
|
||||
assert exp == res
|
||||
assert exp._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
def test_min(self, ts):
|
||||
assert ts.min <= ts
|
||||
assert ts.min._creso == ts._creso
|
||||
assert ts.min._value == NaT._value + 1
|
||||
|
||||
def test_max(self, ts):
|
||||
assert ts.max >= ts
|
||||
assert ts.max._creso == ts._creso
|
||||
assert ts.max._value == np.iinfo(np.int64).max
|
||||
|
||||
def test_resolution(self, ts):
|
||||
expected = Timedelta._from_value_and_reso(1, ts._creso)
|
||||
result = ts.resolution
|
||||
assert result == expected
|
||||
assert result._creso == expected._creso
|
||||
|
||||
def test_out_of_ns_bounds(self):
|
||||
# https://github.com/pandas-dev/pandas/issues/51060
|
||||
result = Timestamp(-52700112000, unit="s")
|
||||
assert result == Timestamp("0300-01-01")
|
||||
assert result.to_numpy() == np.datetime64("0300-01-01T00:00:00", "s")
|
||||
|
||||
|
||||
def test_timestamp_class_min_max_resolution():
|
||||
# when accessed on the class (as opposed to an instance), we default
|
||||
# to nanoseconds
|
||||
assert Timestamp.min == Timestamp(NaT._value + 1)
|
||||
assert Timestamp.min._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
assert Timestamp.max == Timestamp(np.iinfo(np.int64).max)
|
||||
assert Timestamp.max._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
assert Timestamp.resolution == Timedelta(1)
|
||||
assert Timestamp.resolution._creso == NpyDatetimeUnit.NPY_FR_ns.value
|
||||
|
||||
|
||||
def test_delimited_date():
|
||||
# https://github.com/pandas-dev/pandas/issues/50231
|
||||
with tm.assert_produces_warning(None):
|
||||
result = Timestamp("13-01-2000")
|
||||
expected = Timestamp(2000, 1, 13)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_utctimetuple():
|
||||
# GH 32174
|
||||
ts = Timestamp("2000-01-01", tz="UTC")
|
||||
result = ts.utctimetuple()
|
||||
expected = time.struct_time((2000, 1, 1, 0, 0, 0, 5, 1, 0))
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_negative_dates():
|
||||
# https://github.com/pandas-dev/pandas/issues/50787
|
||||
ts = Timestamp("-2000-01-01")
|
||||
msg = (
|
||||
" not yet supported on Timestamps which are outside the range of "
|
||||
"Python's standard library. For now, please call the components you need "
|
||||
r"\(such as `.year` and `.month`\) and construct your string from there.$"
|
||||
)
|
||||
func = "^strftime"
|
||||
with pytest.raises(NotImplementedError, match=func + msg):
|
||||
ts.strftime("%Y")
|
||||
|
||||
msg = (
|
||||
" not yet supported on Timestamps which "
|
||||
"are outside the range of Python's standard library. "
|
||||
)
|
||||
func = "^date"
|
||||
with pytest.raises(NotImplementedError, match=func + msg):
|
||||
ts.date()
|
||||
func = "^isocalendar"
|
||||
with pytest.raises(NotImplementedError, match=func + msg):
|
||||
ts.isocalendar()
|
||||
func = "^timetuple"
|
||||
with pytest.raises(NotImplementedError, match=func + msg):
|
||||
ts.timetuple()
|
||||
func = "^toordinal"
|
||||
with pytest.raises(NotImplementedError, match=func + msg):
|
||||
ts.toordinal()
|
@ -0,0 +1,24 @@
|
||||
"""
|
||||
Tests for Timestamp timezone-related methods
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from pandas._libs.tslibs import timezones
|
||||
|
||||
from pandas import Timestamp
|
||||
|
||||
|
||||
class TestTimestampTZOperations:
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
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
|
Reference in New Issue
Block a user