394 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			394 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
Tests for offsets.Tick and subclasses
 | 
						|
"""
 | 
						|
from datetime import (
 | 
						|
    datetime,
 | 
						|
    timedelta,
 | 
						|
)
 | 
						|
 | 
						|
from hypothesis import (
 | 
						|
    assume,
 | 
						|
    example,
 | 
						|
    given,
 | 
						|
)
 | 
						|
import numpy as np
 | 
						|
import pytest
 | 
						|
 | 
						|
from pandas._libs.tslibs.offsets import delta_to_tick
 | 
						|
 | 
						|
from pandas import (
 | 
						|
    Timedelta,
 | 
						|
    Timestamp,
 | 
						|
)
 | 
						|
import pandas._testing as tm
 | 
						|
from pandas._testing._hypothesis import INT_NEG_999_TO_POS_999
 | 
						|
from pandas.tests.tseries.offsets.common import assert_offset_equal
 | 
						|
 | 
						|
from pandas.tseries import offsets
 | 
						|
from pandas.tseries.offsets import (
 | 
						|
    Hour,
 | 
						|
    Micro,
 | 
						|
    Milli,
 | 
						|
    Minute,
 | 
						|
    Nano,
 | 
						|
    Second,
 | 
						|
)
 | 
						|
 | 
						|
# ---------------------------------------------------------------------
 | 
						|
# Test Helpers
 | 
						|
 | 
						|
tick_classes = [Hour, Minute, Second, Milli, Micro, Nano]
 | 
						|
 | 
						|
 | 
						|
# ---------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
def test_apply_ticks():
 | 
						|
    result = offsets.Hour(3)._apply(offsets.Hour(4))
 | 
						|
    exp = offsets.Hour(7)
 | 
						|
    assert result == exp
 | 
						|
 | 
						|
 | 
						|
def test_delta_to_tick():
 | 
						|
    delta = timedelta(3)
 | 
						|
 | 
						|
    tick = delta_to_tick(delta)
 | 
						|
    assert tick == offsets.Day(3)
 | 
						|
 | 
						|
    td = Timedelta(nanoseconds=5)
 | 
						|
    tick = delta_to_tick(td)
 | 
						|
    assert tick == Nano(5)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
@example(n=2, m=3)
 | 
						|
@example(n=800, m=300)
 | 
						|
@example(n=1000, m=5)
 | 
						|
@given(n=INT_NEG_999_TO_POS_999, m=INT_NEG_999_TO_POS_999)
 | 
						|
def test_tick_add_sub(cls, n, m):
 | 
						|
    # For all Tick subclasses and all integers n, m, we should have
 | 
						|
    # tick(n) + tick(m) == tick(n+m)
 | 
						|
    # tick(n) - tick(m) == tick(n-m)
 | 
						|
    left = cls(n)
 | 
						|
    right = cls(m)
 | 
						|
    expected = cls(n + m)
 | 
						|
 | 
						|
    assert left + right == expected
 | 
						|
    assert left._apply(right) == expected
 | 
						|
 | 
						|
    expected = cls(n - m)
 | 
						|
    assert left - right == expected
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.arm_slow
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
@example(n=2, m=3)
 | 
						|
@given(n=INT_NEG_999_TO_POS_999, m=INT_NEG_999_TO_POS_999)
 | 
						|
def test_tick_equality(cls, n, m):
 | 
						|
    assume(m != n)
 | 
						|
    # tick == tock iff tick.n == tock.n
 | 
						|
    left = cls(n)
 | 
						|
    right = cls(m)
 | 
						|
    assert left != right
 | 
						|
    assert not (left == right)
 | 
						|
 | 
						|
    right = cls(n)
 | 
						|
    assert left == right
 | 
						|
    assert not (left != right)
 | 
						|
 | 
						|
    if n != 0:
 | 
						|
        assert cls(n) != cls(-n)
 | 
						|
 | 
						|
 | 
						|
# ---------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
def test_Hour():
 | 
						|
    assert_offset_equal(Hour(), datetime(2010, 1, 1), datetime(2010, 1, 1, 1))
 | 
						|
    assert_offset_equal(Hour(-1), datetime(2010, 1, 1, 1), datetime(2010, 1, 1))
 | 
						|
    assert_offset_equal(2 * Hour(), datetime(2010, 1, 1), datetime(2010, 1, 1, 2))
 | 
						|
    assert_offset_equal(-1 * Hour(), datetime(2010, 1, 1, 1), datetime(2010, 1, 1))
 | 
						|
 | 
						|
    assert Hour(3) + Hour(2) == Hour(5)
 | 
						|
    assert Hour(3) - Hour(2) == Hour()
 | 
						|
 | 
						|
    assert Hour(4) != Hour(1)
 | 
						|
 | 
						|
 | 
						|
def test_Minute():
 | 
						|
    assert_offset_equal(Minute(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 1))
 | 
						|
    assert_offset_equal(Minute(-1), datetime(2010, 1, 1, 0, 1), datetime(2010, 1, 1))
 | 
						|
    assert_offset_equal(2 * Minute(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 2))
 | 
						|
    assert_offset_equal(-1 * Minute(), datetime(2010, 1, 1, 0, 1), datetime(2010, 1, 1))
 | 
						|
 | 
						|
    assert Minute(3) + Minute(2) == Minute(5)
 | 
						|
    assert Minute(3) - Minute(2) == Minute()
 | 
						|
    assert Minute(5) != Minute()
 | 
						|
 | 
						|
 | 
						|
def test_Second():
 | 
						|
    assert_offset_equal(Second(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 1))
 | 
						|
    assert_offset_equal(Second(-1), datetime(2010, 1, 1, 0, 0, 1), datetime(2010, 1, 1))
 | 
						|
    assert_offset_equal(
 | 
						|
        2 * Second(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 2)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        -1 * Second(), datetime(2010, 1, 1, 0, 0, 1), datetime(2010, 1, 1)
 | 
						|
    )
 | 
						|
 | 
						|
    assert Second(3) + Second(2) == Second(5)
 | 
						|
    assert Second(3) - Second(2) == Second()
 | 
						|
 | 
						|
 | 
						|
def test_Millisecond():
 | 
						|
    assert_offset_equal(
 | 
						|
        Milli(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 1000)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        Milli(-1), datetime(2010, 1, 1, 0, 0, 0, 1000), datetime(2010, 1, 1)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        Milli(2), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2000)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        2 * Milli(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2000)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        -1 * Milli(), datetime(2010, 1, 1, 0, 0, 0, 1000), datetime(2010, 1, 1)
 | 
						|
    )
 | 
						|
 | 
						|
    assert Milli(3) + Milli(2) == Milli(5)
 | 
						|
    assert Milli(3) - Milli(2) == Milli()
 | 
						|
 | 
						|
 | 
						|
def test_MillisecondTimestampArithmetic():
 | 
						|
    assert_offset_equal(
 | 
						|
        Milli(), Timestamp("2010-01-01"), Timestamp("2010-01-01 00:00:00.001")
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        Milli(-1), Timestamp("2010-01-01 00:00:00.001"), Timestamp("2010-01-01")
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def test_Microsecond():
 | 
						|
    assert_offset_equal(Micro(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 1))
 | 
						|
    assert_offset_equal(
 | 
						|
        Micro(-1), datetime(2010, 1, 1, 0, 0, 0, 1), datetime(2010, 1, 1)
 | 
						|
    )
 | 
						|
 | 
						|
    assert_offset_equal(
 | 
						|
        2 * Micro(), datetime(2010, 1, 1), datetime(2010, 1, 1, 0, 0, 0, 2)
 | 
						|
    )
 | 
						|
    assert_offset_equal(
 | 
						|
        -1 * Micro(), datetime(2010, 1, 1, 0, 0, 0, 1), datetime(2010, 1, 1)
 | 
						|
    )
 | 
						|
 | 
						|
    assert Micro(3) + Micro(2) == Micro(5)
 | 
						|
    assert Micro(3) - Micro(2) == Micro()
 | 
						|
 | 
						|
 | 
						|
def test_NanosecondGeneric():
 | 
						|
    timestamp = Timestamp(datetime(2010, 1, 1))
 | 
						|
    assert timestamp.nanosecond == 0
 | 
						|
 | 
						|
    result = timestamp + Nano(10)
 | 
						|
    assert result.nanosecond == 10
 | 
						|
 | 
						|
    reverse_result = Nano(10) + timestamp
 | 
						|
    assert reverse_result.nanosecond == 10
 | 
						|
 | 
						|
 | 
						|
def test_Nanosecond():
 | 
						|
    timestamp = Timestamp(datetime(2010, 1, 1))
 | 
						|
    assert_offset_equal(Nano(), timestamp, timestamp + np.timedelta64(1, "ns"))
 | 
						|
    assert_offset_equal(Nano(-1), timestamp + np.timedelta64(1, "ns"), timestamp)
 | 
						|
    assert_offset_equal(2 * Nano(), timestamp, timestamp + np.timedelta64(2, "ns"))
 | 
						|
    assert_offset_equal(-1 * Nano(), timestamp + np.timedelta64(1, "ns"), timestamp)
 | 
						|
 | 
						|
    assert Nano(3) + Nano(2) == Nano(5)
 | 
						|
    assert Nano(3) - Nano(2) == Nano()
 | 
						|
 | 
						|
    # GH9284
 | 
						|
    assert Nano(1) + Nano(10) == Nano(11)
 | 
						|
    assert Nano(5) + Micro(1) == Nano(1005)
 | 
						|
    assert Micro(5) + Nano(1) == Nano(5001)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize(
 | 
						|
    "kls, expected",
 | 
						|
    [
 | 
						|
        (Hour, Timedelta(hours=5)),
 | 
						|
        (Minute, Timedelta(hours=2, minutes=3)),
 | 
						|
        (Second, Timedelta(hours=2, seconds=3)),
 | 
						|
        (Milli, Timedelta(hours=2, milliseconds=3)),
 | 
						|
        (Micro, Timedelta(hours=2, microseconds=3)),
 | 
						|
        (Nano, Timedelta(hours=2, nanoseconds=3)),
 | 
						|
    ],
 | 
						|
)
 | 
						|
def test_tick_addition(kls, expected):
 | 
						|
    offset = kls(3)
 | 
						|
    td = Timedelta(hours=2)
 | 
						|
 | 
						|
    for other in [td, td.to_pytimedelta(), td.to_timedelta64()]:
 | 
						|
        result = offset + other
 | 
						|
        assert isinstance(result, Timedelta)
 | 
						|
        assert result == expected
 | 
						|
 | 
						|
        result = other + offset
 | 
						|
        assert isinstance(result, Timedelta)
 | 
						|
        assert result == expected
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_tick_division(cls):
 | 
						|
    off = cls(10)
 | 
						|
 | 
						|
    assert off / cls(5) == 2
 | 
						|
    assert off / 2 == cls(5)
 | 
						|
    assert off / 2.0 == cls(5)
 | 
						|
 | 
						|
    assert off / off.delta == 1
 | 
						|
    assert off / off.delta.to_timedelta64() == 1
 | 
						|
 | 
						|
    assert off / Nano(1) == off.delta / Nano(1).delta
 | 
						|
 | 
						|
    if cls is not Nano:
 | 
						|
        # A case where we end up with a smaller class
 | 
						|
        result = off / 1000
 | 
						|
        assert isinstance(result, offsets.Tick)
 | 
						|
        assert not isinstance(result, cls)
 | 
						|
        assert result.delta == off.delta / 1000
 | 
						|
 | 
						|
    if cls._nanos_inc < Timedelta(seconds=1).value:
 | 
						|
        # Case where we end up with a bigger class
 | 
						|
        result = off / 0.001
 | 
						|
        assert isinstance(result, offsets.Tick)
 | 
						|
        assert not isinstance(result, cls)
 | 
						|
        assert result.delta == off.delta / 0.001
 | 
						|
 | 
						|
 | 
						|
def test_tick_mul_float():
 | 
						|
    off = Micro(2)
 | 
						|
 | 
						|
    # Case where we retain type
 | 
						|
    result = off * 1.5
 | 
						|
    expected = Micro(3)
 | 
						|
    assert result == expected
 | 
						|
    assert isinstance(result, Micro)
 | 
						|
 | 
						|
    # Case where we bump up to the next type
 | 
						|
    result = off * 1.25
 | 
						|
    expected = Nano(2500)
 | 
						|
    assert result == expected
 | 
						|
    assert isinstance(result, Nano)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_tick_rdiv(cls):
 | 
						|
    off = cls(10)
 | 
						|
    delta = off.delta
 | 
						|
    td64 = delta.to_timedelta64()
 | 
						|
    instance__type = ".".join([cls.__module__, cls.__name__])
 | 
						|
    msg = (
 | 
						|
        "unsupported operand type\\(s\\) for \\/: 'int'|'float' and "
 | 
						|
        f"'{instance__type}'"
 | 
						|
    )
 | 
						|
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        2 / off
 | 
						|
    with pytest.raises(TypeError, match=msg):
 | 
						|
        2.0 / off
 | 
						|
 | 
						|
    assert (td64 * 2.5) / off == 2.5
 | 
						|
 | 
						|
    if cls is not Nano:
 | 
						|
        # skip pytimedelta for Nano since it gets dropped
 | 
						|
        assert (delta.to_pytimedelta() * 2) / off == 2
 | 
						|
 | 
						|
    result = np.array([2 * td64, td64]) / off
 | 
						|
    expected = np.array([2.0, 1.0])
 | 
						|
    tm.assert_numpy_array_equal(result, expected)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls1", tick_classes)
 | 
						|
@pytest.mark.parametrize("cls2", tick_classes)
 | 
						|
def test_tick_zero(cls1, cls2):
 | 
						|
    assert cls1(0) == cls2(0)
 | 
						|
    assert cls1(0) + cls2(0) == cls1(0)
 | 
						|
 | 
						|
    if cls1 is not Nano:
 | 
						|
        assert cls1(2) + cls2(0) == cls1(2)
 | 
						|
 | 
						|
    if cls1 is Nano:
 | 
						|
        assert cls1(2) + Nano(0) == cls1(2)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_tick_equalities(cls):
 | 
						|
    assert cls() == cls(1)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_tick_offset(cls):
 | 
						|
    assert not cls().is_anchored()
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_compare_ticks(cls):
 | 
						|
    three = cls(3)
 | 
						|
    four = cls(4)
 | 
						|
 | 
						|
    assert three < cls(4)
 | 
						|
    assert cls(3) < four
 | 
						|
    assert four > cls(3)
 | 
						|
    assert cls(4) > three
 | 
						|
    assert cls(3) == cls(3)
 | 
						|
    assert cls(3) != cls(4)
 | 
						|
 | 
						|
 | 
						|
@pytest.mark.parametrize("cls", tick_classes)
 | 
						|
def test_compare_ticks_to_strs(cls):
 | 
						|
    # GH#23524
 | 
						|
    off = cls(19)
 | 
						|
 | 
						|
    # These tests should work with any strings, but we particularly are
 | 
						|
    #  interested in "infer" as that comparison is convenient to make in
 | 
						|
    #  Datetime/Timedelta Array/Index constructors
 | 
						|
    assert not off == "infer"
 | 
						|
    assert not "foo" == off
 | 
						|
 | 
						|
    instance_type = ".".join([cls.__module__, cls.__name__])
 | 
						|
    msg = (
 | 
						|
        "'<'|'<='|'>'|'>=' not supported between instances of "
 | 
						|
        f"'str' and '{instance_type}'|'{instance_type}' and 'str'"
 | 
						|
    )
 | 
						|
 | 
						|
    for left, right in [("infer", off), (off, "infer")]:
 | 
						|
        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("cls", tick_classes)
 | 
						|
def test_compare_ticks_to_timedeltalike(cls):
 | 
						|
    off = cls(19)
 | 
						|
 | 
						|
    td = off.delta
 | 
						|
 | 
						|
    others = [td, td.to_timedelta64()]
 | 
						|
    if cls is not Nano:
 | 
						|
        others.append(td.to_pytimedelta())
 | 
						|
 | 
						|
    for other in others:
 | 
						|
        assert off == other
 | 
						|
        assert not off != other
 | 
						|
        assert not off < other
 | 
						|
        assert not off > other
 | 
						|
        assert off <= other
 | 
						|
        assert off >= other
 |