first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
"""Tests that the tslibs API is locked down"""
from pandas._libs import tslibs
def test_namespace():
submodules = [
"base",
"ccalendar",
"conversion",
"dtypes",
"fields",
"nattype",
"np_datetime",
"offsets",
"parsing",
"period",
"strptime",
"vectorized",
"timedeltas",
"timestamps",
"timezones",
"tzconversion",
]
api = [
"BaseOffset",
"NaT",
"NaTType",
"iNaT",
"nat_strings",
"OutOfBoundsDatetime",
"OutOfBoundsTimedelta",
"Period",
"IncompatibleFrequency",
"Resolution",
"Tick",
"Timedelta",
"dt64arr_to_periodarr",
"Timestamp",
"is_date_array_normalized",
"ints_to_pydatetime",
"normalize_i8_timestamps",
"get_resolution",
"delta_to_nanoseconds",
"ints_to_pytimedelta",
"localize_pydatetime",
"tz_convert_from_utc_single",
"to_offset",
"tz_compare",
]
expected = set(submodules + api)
names = [x for x in dir(tslibs) if not x.startswith("__")]
assert set(names) == expected

View File

@@ -0,0 +1,200 @@
from datetime import (
date,
datetime,
)
from dateutil.tz.tz import tzoffset
import numpy as np
import pytest
import pytz
from pandas._libs import (
iNaT,
tslib,
)
from pandas import Timestamp
import pandas._testing as tm
@pytest.mark.parametrize(
"data,expected",
[
(
["01-01-2013", "01-02-2013"],
[
"2013-01-01T00:00:00.000000000",
"2013-01-02T00:00:00.000000000",
],
),
(
["Mon Sep 16 2013", "Tue Sep 17 2013"],
[
"2013-09-16T00:00:00.000000000",
"2013-09-17T00:00:00.000000000",
],
),
],
)
def test_parsing_valid_dates(data, expected):
arr = np.array(data, dtype=object)
result, _ = tslib.array_to_datetime(arr)
expected = np.array(expected, dtype="M8[ns]")
tm.assert_numpy_array_equal(result, expected)
@pytest.mark.parametrize(
"dt_string, expected_tz",
[
["01-01-2013 08:00:00+08:00", 480],
["2013-01-01T08:00:00.000000000+0800", 480],
["2012-12-31T16:00:00.000000000-0800", -480],
["12-31-2012 23:00:00-01:00", -60],
],
)
def test_parsing_timezone_offsets(dt_string, expected_tz):
# All of these datetime strings with offsets are equivalent
# to the same datetime after the timezone offset is added.
arr = np.array(["01-01-2013 00:00:00"], dtype=object)
expected, _ = tslib.array_to_datetime(arr)
arr = np.array([dt_string], dtype=object)
result, result_tz = tslib.array_to_datetime(arr)
tm.assert_numpy_array_equal(result, expected)
assert result_tz is pytz.FixedOffset(expected_tz)
def test_parsing_non_iso_timezone_offset():
dt_string = "01-01-2013T00:00:00.000000000+0000"
arr = np.array([dt_string], dtype=object)
result, result_tz = tslib.array_to_datetime(arr)
expected = np.array([np.datetime64("2013-01-01 00:00:00.000000000")])
tm.assert_numpy_array_equal(result, expected)
assert result_tz is pytz.FixedOffset(0)
def test_parsing_different_timezone_offsets():
# see gh-17697
data = ["2015-11-18 15:30:00+05:30", "2015-11-18 15:30:00+06:30"]
data = np.array(data, dtype=object)
result, result_tz = tslib.array_to_datetime(data)
expected = np.array(
[
datetime(2015, 11, 18, 15, 30, tzinfo=tzoffset(None, 19800)),
datetime(2015, 11, 18, 15, 30, tzinfo=tzoffset(None, 23400)),
],
dtype=object,
)
tm.assert_numpy_array_equal(result, expected)
assert result_tz is None
@pytest.mark.parametrize(
"data", [["-352.737091", "183.575577"], ["1", "2", "3", "4", "5"]]
)
def test_number_looking_strings_not_into_datetime(data):
# see gh-4601
#
# These strings don't look like datetimes, so
# they shouldn't be attempted to be converted.
arr = np.array(data, dtype=object)
result, _ = tslib.array_to_datetime(arr, errors="ignore")
tm.assert_numpy_array_equal(result, arr)
@pytest.mark.parametrize(
"invalid_date",
[
date(1000, 1, 1),
datetime(1000, 1, 1),
"1000-01-01",
"Jan 1, 1000",
np.datetime64("1000-01-01"),
],
)
@pytest.mark.parametrize("errors", ["coerce", "raise"])
def test_coerce_outside_ns_bounds(invalid_date, errors):
arr = np.array([invalid_date], dtype="object")
kwargs = {"values": arr, "errors": errors}
if errors == "raise":
msg = "Out of bounds nanosecond timestamp"
with pytest.raises(ValueError, match=msg):
tslib.array_to_datetime(**kwargs)
else: # coerce.
result, _ = tslib.array_to_datetime(**kwargs)
expected = np.array([iNaT], dtype="M8[ns]")
tm.assert_numpy_array_equal(result, expected)
def test_coerce_outside_ns_bounds_one_valid():
arr = np.array(["1/1/1000", "1/1/2000"], dtype=object)
result, _ = tslib.array_to_datetime(arr, errors="coerce")
expected = [iNaT, "2000-01-01T00:00:00.000000000"]
expected = np.array(expected, dtype="M8[ns]")
tm.assert_numpy_array_equal(result, expected)
@pytest.mark.parametrize("errors", ["ignore", "coerce"])
def test_coerce_of_invalid_datetimes(errors):
arr = np.array(["01-01-2013", "not_a_date", "1"], dtype=object)
kwargs = {"values": arr, "errors": errors}
if errors == "ignore":
# Without coercing, the presence of any invalid
# dates prevents any values from being converted.
result, _ = tslib.array_to_datetime(**kwargs)
tm.assert_numpy_array_equal(result, arr)
else: # coerce.
# With coercing, the invalid dates becomes iNaT
result, _ = tslib.array_to_datetime(arr, errors="coerce")
expected = ["2013-01-01T00:00:00.000000000", iNaT, iNaT]
tm.assert_numpy_array_equal(result, np.array(expected, dtype="M8[ns]"))
def test_to_datetime_barely_out_of_bounds():
# see gh-19382, gh-19529
#
# Close enough to bounds that dropping nanos
# would result in an in-bounds datetime.
arr = np.array(["2262-04-11 23:47:16.854775808"], dtype=object)
msg = "Out of bounds nanosecond timestamp: 2262-04-11 23:47:16"
with pytest.raises(tslib.OutOfBoundsDatetime, match=msg):
tslib.array_to_datetime(arr)
class SubDatetime(datetime):
pass
@pytest.mark.parametrize(
"data,expected",
[
([SubDatetime(2000, 1, 1)], ["2000-01-01T00:00:00.000000000"]),
([datetime(2000, 1, 1)], ["2000-01-01T00:00:00.000000000"]),
([Timestamp(2000, 1, 1)], ["2000-01-01T00:00:00.000000000"]),
],
)
def test_datetime_subclass(data, expected):
# GH 25851
# ensure that subclassed datetime works with
# array_to_datetime
arr = np.array(data, dtype=object)
result, _ = tslib.array_to_datetime(arr)
expected = np.array(expected, dtype="M8[ns]")
tm.assert_numpy_array_equal(result, expected)

View File

@@ -0,0 +1,63 @@
from datetime import (
date,
datetime,
)
from hypothesis import given
import numpy as np
import pytest
from pandas._libs.tslibs import ccalendar
from pandas._testing._hypothesis import DATETIME_IN_PD_TIMESTAMP_RANGE_NO_TZ
@pytest.mark.parametrize(
"date_tuple,expected",
[
((2001, 3, 1), 60),
((2004, 3, 1), 61),
((1907, 12, 31), 365), # End-of-year, non-leap year.
((2004, 12, 31), 366), # End-of-year, leap year.
],
)
def test_get_day_of_year_numeric(date_tuple, expected):
assert ccalendar.get_day_of_year(*date_tuple) == expected
def test_get_day_of_year_dt():
dt = datetime.fromordinal(1 + np.random.randint(365 * 4000))
result = ccalendar.get_day_of_year(dt.year, dt.month, dt.day)
expected = (dt - dt.replace(month=1, day=1)).days + 1
assert result == expected
@pytest.mark.parametrize(
"input_date_tuple, expected_iso_tuple",
[
[(2020, 1, 1), (2020, 1, 3)],
[(2019, 12, 31), (2020, 1, 2)],
[(2019, 12, 30), (2020, 1, 1)],
[(2009, 12, 31), (2009, 53, 4)],
[(2010, 1, 1), (2009, 53, 5)],
[(2010, 1, 3), (2009, 53, 7)],
[(2010, 1, 4), (2010, 1, 1)],
[(2006, 1, 1), (2005, 52, 7)],
[(2005, 12, 31), (2005, 52, 6)],
[(2008, 12, 28), (2008, 52, 7)],
[(2008, 12, 29), (2009, 1, 1)],
],
)
def test_dt_correct_iso_8601_year_week_and_day(input_date_tuple, expected_iso_tuple):
result = ccalendar.get_iso_calendar(*input_date_tuple)
expected_from_date_isocalendar = date(*input_date_tuple).isocalendar()
assert result == expected_from_date_isocalendar
assert result == expected_iso_tuple
@given(DATETIME_IN_PD_TIMESTAMP_RANGE_NO_TZ)
def test_isocalendar(dt):
expected = dt.isocalendar()
result = ccalendar.get_iso_calendar(dt.year, dt.month, dt.day)
assert result == expected

View File

@@ -0,0 +1,141 @@
from datetime import datetime
import numpy as np
import pytest
from pytz import UTC
from pandas._libs.tslibs import (
OutOfBoundsTimedelta,
conversion,
iNaT,
timezones,
tzconversion,
)
from pandas import (
Timestamp,
date_range,
)
import pandas._testing as tm
def _compare_utc_to_local(tz_didx):
def f(x):
return tzconversion.tz_convert_from_utc_single(x, tz_didx.tz)
result = tzconversion.tz_convert_from_utc(tz_didx.asi8, tz_didx.tz)
expected = np.vectorize(f)(tz_didx.asi8)
tm.assert_numpy_array_equal(result, expected)
def _compare_local_to_utc(tz_didx, naive_didx):
# Check that tz_localize behaves the same vectorized and pointwise.
err1 = err2 = None
try:
result = tzconversion.tz_localize_to_utc(naive_didx.asi8, tz_didx.tz)
err1 = None
except Exception as err:
err1 = err
try:
expected = naive_didx.map(lambda x: x.tz_localize(tz_didx.tz)).asi8
except Exception as err:
err2 = err
if err1 is not None:
assert type(err1) == type(err2)
else:
assert err2 is None
tm.assert_numpy_array_equal(result, expected)
def test_tz_convert_single_matches_tz_convert_hourly(tz_aware_fixture):
tz = tz_aware_fixture
tz_didx = date_range("2014-03-01", "2015-01-10", freq="H", tz=tz)
naive_didx = date_range("2014-03-01", "2015-01-10", freq="H")
_compare_utc_to_local(tz_didx)
_compare_local_to_utc(tz_didx, naive_didx)
@pytest.mark.parametrize("freq", ["D", "A"])
def test_tz_convert_single_matches_tz_convert(tz_aware_fixture, freq):
tz = tz_aware_fixture
tz_didx = date_range("2000-01-01", "2020-01-01", freq=freq, tz=tz)
naive_didx = date_range("2000-01-01", "2020-01-01", freq=freq)
_compare_utc_to_local(tz_didx)
_compare_local_to_utc(tz_didx, naive_didx)
@pytest.mark.parametrize(
"arr",
[
pytest.param(np.array([], dtype=np.int64), id="empty"),
pytest.param(np.array([iNaT], dtype=np.int64), id="all_nat"),
],
)
def test_tz_convert_corner(arr):
result = tzconversion.tz_convert_from_utc(arr, timezones.maybe_get_tz("Asia/Tokyo"))
tm.assert_numpy_array_equal(result, arr)
def test_tz_convert_readonly():
# GH#35530
arr = np.array([0], dtype=np.int64)
arr.setflags(write=False)
result = tzconversion.tz_convert_from_utc(arr, UTC)
tm.assert_numpy_array_equal(result, arr)
@pytest.mark.parametrize("copy", [True, False])
@pytest.mark.parametrize("dtype", ["M8[ns]", "M8[s]"])
def test_length_zero_copy(dtype, copy):
arr = np.array([], dtype=dtype)
result = conversion.ensure_datetime64ns(arr, copy=copy)
assert result.base is (None if copy else arr)
def test_ensure_datetime64ns_bigendian():
# GH#29684
arr = np.array([np.datetime64(1, "ms")], dtype=">M8[ms]")
result = conversion.ensure_datetime64ns(arr)
expected = np.array([np.datetime64(1, "ms")], dtype="M8[ns]")
tm.assert_numpy_array_equal(result, expected)
def test_ensure_timedelta64ns_overflows():
arr = np.arange(10).astype("m8[Y]") * 100
msg = r"Out of bounds for nanosecond timedelta64\[Y\] 900"
with pytest.raises(OutOfBoundsTimedelta, match=msg):
conversion.ensure_timedelta64ns(arr)
class SubDatetime(datetime):
pass
@pytest.mark.parametrize(
"dt, expected",
[
pytest.param(
Timestamp("2000-01-01"), Timestamp("2000-01-01", tz=UTC), id="timestamp"
),
pytest.param(
datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=UTC), id="datetime"
),
pytest.param(
SubDatetime(2000, 1, 1),
SubDatetime(2000, 1, 1, tzinfo=UTC),
id="subclassed_datetime",
),
],
)
def test_localize_pydatetime_dt_types(dt, expected):
# GH 25851
# ensure that subclassed datetime works with
# localize_pydatetime
result = conversion.localize_pydatetime(dt, UTC)
assert result == expected

View File

@@ -0,0 +1,40 @@
import numpy as np
import pytest
from pandas._libs.tslibs import fields
import pandas._testing as tm
@pytest.fixture
def dtindex():
dtindex = np.arange(5, dtype=np.int64) * 10**9 * 3600 * 24 * 32
dtindex.flags.writeable = False
return dtindex
def test_get_date_name_field_readonly(dtindex):
# https://github.com/vaexio/vaex/issues/357
# fields functions shouldn't raise when we pass read-only data
result = fields.get_date_name_field(dtindex, "month_name")
expected = np.array(["January", "February", "March", "April", "May"], dtype=object)
tm.assert_numpy_array_equal(result, expected)
def test_get_date_field_readonly(dtindex):
result = fields.get_date_field(dtindex, "Y")
expected = np.array([1970, 1970, 1970, 1970, 1970], dtype=np.int32)
tm.assert_numpy_array_equal(result, expected)
def test_get_start_end_field_readonly(dtindex):
result = fields.get_start_end_field(dtindex, "is_month_start", None)
expected = np.array([True, False, False, False, False], dtype=np.bool_)
tm.assert_numpy_array_equal(result, expected)
def test_get_timedelta_field_readonly(dtindex):
# treat dtindex as timedeltas for this next one
result = fields.get_timedelta_field(dtindex, "days")
expected = np.arange(5, dtype=np.int32) * 32
tm.assert_numpy_array_equal(result, expected)

View File

@@ -0,0 +1,29 @@
import pytest
from pandas._libs.tslibs.parsing import get_rule_month
from pandas.tseries import offsets
@pytest.mark.parametrize(
"obj,expected",
[
("W", "DEC"),
(offsets.Week().freqstr, "DEC"),
("D", "DEC"),
(offsets.Day().freqstr, "DEC"),
("Q", "DEC"),
(offsets.QuarterEnd(startingMonth=12).freqstr, "DEC"),
("Q-JAN", "JAN"),
(offsets.QuarterEnd(startingMonth=1).freqstr, "JAN"),
("A-DEC", "DEC"),
("Y-DEC", "DEC"),
(offsets.YearEnd().freqstr, "DEC"),
("A-MAY", "MAY"),
("Y-MAY", "MAY"),
(offsets.YearEnd(month=5).freqstr, "MAY"),
],
)
def test_get_rule_month(obj, expected):
result = get_rule_month(obj)
assert result == expected

View File

@@ -0,0 +1,173 @@
"""
Tests for helper functions in the cython tslibs.offsets
"""
from datetime import datetime
import pytest
from pandas._libs.tslibs.ccalendar import (
get_firstbday,
get_lastbday,
)
import pandas._libs.tslibs.offsets as liboffsets
from pandas._libs.tslibs.offsets import roll_qtrday
from pandas import Timestamp
@pytest.fixture(params=["start", "end", "business_start", "business_end"])
def day_opt(request):
return request.param
@pytest.mark.parametrize(
"dt,exp_week_day,exp_last_day",
[
(datetime(2017, 11, 30), 3, 30), # Business day.
(datetime(1993, 10, 31), 6, 29), # Non-business day.
],
)
def test_get_last_bday(dt, exp_week_day, exp_last_day):
assert dt.weekday() == exp_week_day
assert get_lastbday(dt.year, dt.month) == exp_last_day
@pytest.mark.parametrize(
"dt,exp_week_day,exp_first_day",
[
(datetime(2017, 4, 1), 5, 3), # Non-weekday.
(datetime(1993, 10, 1), 4, 1), # Business day.
],
)
def test_get_first_bday(dt, exp_week_day, exp_first_day):
assert dt.weekday() == exp_week_day
assert get_firstbday(dt.year, dt.month) == exp_first_day
@pytest.mark.parametrize(
"months,day_opt,expected",
[
(0, 15, datetime(2017, 11, 15)),
(0, None, datetime(2017, 11, 30)),
(1, "start", datetime(2017, 12, 1)),
(-145, "end", datetime(2005, 10, 31)),
(0, "business_end", datetime(2017, 11, 30)),
(0, "business_start", datetime(2017, 11, 1)),
],
)
def test_shift_month_dt(months, day_opt, expected):
dt = datetime(2017, 11, 30)
assert liboffsets.shift_month(dt, months, day_opt=day_opt) == expected
@pytest.mark.parametrize(
"months,day_opt,expected",
[
(1, "start", Timestamp("1929-06-01")),
(-3, "end", Timestamp("1929-02-28")),
(25, None, Timestamp("1931-06-5")),
(-1, 31, Timestamp("1929-04-30")),
],
)
def test_shift_month_ts(months, day_opt, expected):
ts = Timestamp("1929-05-05")
assert liboffsets.shift_month(ts, months, day_opt=day_opt) == expected
def test_shift_month_error():
dt = datetime(2017, 11, 15)
day_opt = "this should raise"
with pytest.raises(ValueError, match=day_opt):
liboffsets.shift_month(dt, 3, day_opt=day_opt)
@pytest.mark.parametrize(
"other,expected",
[
# Before March 1.
(datetime(2017, 2, 10), {2: 1, -7: -7, 0: 0}),
# After March 1.
(Timestamp("2014-03-15", tz="US/Eastern"), {2: 2, -7: -6, 0: 1}),
],
)
@pytest.mark.parametrize("n", [2, -7, 0])
def test_roll_qtrday_year(other, expected, n):
month = 3
day_opt = "start" # `other` will be compared to March 1.
assert roll_qtrday(other, n, month, day_opt, modby=12) == expected[n]
@pytest.mark.parametrize(
"other,expected",
[
# Before June 30.
(datetime(1999, 6, 29), {5: 4, -7: -7, 0: 0}),
# After June 30.
(Timestamp(2072, 8, 24, 6, 17, 18), {5: 5, -7: -6, 0: 1}),
],
)
@pytest.mark.parametrize("n", [5, -7, 0])
def test_roll_qtrday_year2(other, expected, n):
month = 6
day_opt = "end" # `other` will be compared to June 30.
assert roll_qtrday(other, n, month, day_opt, modby=12) == expected[n]
def test_get_day_of_month_error():
# get_day_of_month is not directly exposed.
# We test it via roll_qtrday.
dt = datetime(2017, 11, 15)
day_opt = "foo"
with pytest.raises(ValueError, match=day_opt):
# To hit the raising case we need month == dt.month and n > 0.
roll_qtrday(dt, n=3, month=11, day_opt=day_opt, modby=12)
@pytest.mark.parametrize(
"month",
[3, 5], # (other.month % 3) < (month % 3) # (other.month % 3) > (month % 3)
)
@pytest.mark.parametrize("n", [4, -3])
def test_roll_qtr_day_not_mod_unequal(day_opt, month, n):
expected = {3: {-3: -2, 4: 4}, 5: {-3: -3, 4: 3}}
other = Timestamp(2072, 10, 1, 6, 17, 18) # Saturday.
assert roll_qtrday(other, n, month, day_opt, modby=3) == expected[month][n]
@pytest.mark.parametrize(
"other,month,exp_dict",
[
# Monday.
(datetime(1999, 5, 31), 2, {-1: {"start": 0, "business_start": 0}}),
# Saturday.
(
Timestamp(2072, 10, 1, 6, 17, 18),
4,
{2: {"end": 1, "business_end": 1, "business_start": 1}},
),
# First business day.
(
Timestamp(2072, 10, 3, 6, 17, 18),
4,
{2: {"end": 1, "business_end": 1}, -1: {"start": 0}},
),
],
)
@pytest.mark.parametrize("n", [2, -1])
def test_roll_qtr_day_mod_equal(other, month, exp_dict, n, day_opt):
# All cases have (other.month % 3) == (month % 3).
expected = exp_dict.get(n, {}).get(day_opt, n)
assert roll_qtrday(other, n, month, day_opt, modby=3) == expected
@pytest.mark.parametrize(
"n,expected", [(42, {29: 42, 1: 42, 31: 41}), (-4, {29: -4, 1: -3, 31: -4})]
)
@pytest.mark.parametrize("compare", [29, 1, 31])
def test_roll_convention(n, expected, compare):
assert liboffsets.roll_convention(29, n, compare) == expected[compare]

View File

@@ -0,0 +1,72 @@
from datetime import datetime
import pytest
from pandas._libs import tslib
@pytest.mark.parametrize(
"date_str, exp",
[
("2011-01-02", datetime(2011, 1, 2)),
("2011-1-2", datetime(2011, 1, 2)),
("2011-01", datetime(2011, 1, 1)),
("2011-1", datetime(2011, 1, 1)),
("2011 01 02", datetime(2011, 1, 2)),
("2011.01.02", datetime(2011, 1, 2)),
("2011/01/02", datetime(2011, 1, 2)),
("2011\\01\\02", datetime(2011, 1, 2)),
("2013-01-01 05:30:00", datetime(2013, 1, 1, 5, 30)),
("2013-1-1 5:30:00", datetime(2013, 1, 1, 5, 30)),
],
)
def test_parsers_iso8601(date_str, exp):
# see gh-12060
#
# Test only the ISO parser - flexibility to
# different separators and leading zero's.
actual = tslib._test_parse_iso8601(date_str)
assert actual == exp
@pytest.mark.parametrize(
"date_str",
[
"2011-01/02",
"2011=11=11",
"201401",
"201111",
"200101",
# Mixed separated and unseparated.
"2005-0101",
"200501-01",
"20010101 12:3456",
"20010101 1234:56",
# HHMMSS must have two digits in
# each component if unseparated.
"20010101 1",
"20010101 123",
"20010101 12345",
"20010101 12345Z",
],
)
def test_parsers_iso8601_invalid(date_str):
msg = f'Error parsing datetime string "{date_str}"'
with pytest.raises(ValueError, match=msg):
tslib._test_parse_iso8601(date_str)
def test_parsers_iso8601_invalid_offset_invalid():
date_str = "2001-01-01 12-34-56"
msg = f'Timezone hours offset out of range in datetime string "{date_str}"'
with pytest.raises(ValueError, match=msg):
tslib._test_parse_iso8601(date_str)
def test_parsers_iso8601_leading_space():
# GH#25895 make sure isoparser doesn't overflow with long input
date_str, expected = ("2013-1-1 5:30:00", datetime(2013, 1, 1, 5, 30))
actual = tslib._test_parse_iso8601(" " * 200 + date_str)
assert actual == expected

View File

@@ -0,0 +1,280 @@
"""
Tests for Timestamp parsing, aimed at pandas/_libs/tslibs/parsing.pyx
"""
from datetime import datetime
import re
from dateutil.parser import parse
import numpy as np
import pytest
from pandas._libs.tslibs import parsing
from pandas._libs.tslibs.parsing import parse_time_string
import pandas.util._test_decorators as td
import pandas._testing as tm
def test_parse_time_string():
(parsed, reso) = parse_time_string("4Q1984")
(parsed_lower, reso_lower) = parse_time_string("4q1984")
assert reso == reso_lower
assert parsed == parsed_lower
def test_parse_time_string_invalid_type():
# Raise on invalid input, don't just return it
msg = "Argument 'arg' has incorrect type (expected str, got tuple)"
with pytest.raises(TypeError, match=re.escape(msg)):
parse_time_string((4, 5))
@pytest.mark.parametrize(
"dashed,normal", [("1988-Q2", "1988Q2"), ("2Q-1988", "2Q1988")]
)
def test_parse_time_quarter_with_dash(dashed, normal):
# see gh-9688
(parsed_dash, reso_dash) = parse_time_string(dashed)
(parsed, reso) = parse_time_string(normal)
assert parsed_dash == parsed
assert reso_dash == reso
@pytest.mark.parametrize("dashed", ["-2Q1992", "2-Q1992", "4-4Q1992"])
def test_parse_time_quarter_with_dash_error(dashed):
msg = f"Unknown datetime string format, unable to parse: {dashed}"
with pytest.raises(parsing.DateParseError, match=msg):
parse_time_string(dashed)
@pytest.mark.parametrize(
"date_string,expected",
[
("123.1234", False),
("-50000", False),
("999", False),
("m", False),
("T", False),
("Mon Sep 16, 2013", True),
("2012-01-01", True),
("01/01/2012", True),
("01012012", True),
("0101", True),
("1-1", True),
],
)
def test_does_not_convert_mixed_integer(date_string, expected):
assert parsing._does_string_look_like_datetime(date_string) is expected
@pytest.mark.parametrize(
"date_str,kwargs,msg",
[
(
"2013Q5",
{},
(
"Incorrect quarterly string is given, "
"quarter must be between 1 and 4: 2013Q5"
),
),
# see gh-5418
(
"2013Q1",
{"freq": "INVLD-L-DEC-SAT"},
(
"Unable to retrieve month information "
"from given freq: INVLD-L-DEC-SAT"
),
),
],
)
def test_parsers_quarterly_with_freq_error(date_str, kwargs, msg):
with pytest.raises(parsing.DateParseError, match=msg):
parsing.parse_time_string(date_str, **kwargs)
@pytest.mark.parametrize(
"date_str,freq,expected",
[
("2013Q2", None, datetime(2013, 4, 1)),
("2013Q2", "A-APR", datetime(2012, 8, 1)),
("2013-Q2", "A-DEC", datetime(2013, 4, 1)),
],
)
def test_parsers_quarterly_with_freq(date_str, freq, expected):
result, _ = parsing.parse_time_string(date_str, freq=freq)
assert result == expected
@pytest.mark.parametrize(
"date_str", ["2Q 2005", "2Q-200A", "2Q-200", "22Q2005", "2Q200.", "6Q-20"]
)
def test_parsers_quarter_invalid(date_str):
if date_str == "6Q-20":
msg = (
"Incorrect quarterly string is given, quarter "
f"must be between 1 and 4: {date_str}"
)
else:
msg = f"Unknown datetime string format, unable to parse: {date_str}"
with pytest.raises(ValueError, match=msg):
parsing.parse_time_string(date_str)
@pytest.mark.parametrize(
"date_str,expected",
[("201101", datetime(2011, 1, 1, 0, 0)), ("200005", datetime(2000, 5, 1, 0, 0))],
)
def test_parsers_month_freq(date_str, expected):
result, _ = parsing.parse_time_string(date_str, freq="M")
assert result == expected
@td.skip_if_not_us_locale
@pytest.mark.parametrize(
"string,fmt",
[
("20111230", "%Y%m%d"),
("2011-12-30", "%Y-%m-%d"),
("30-12-2011", "%d-%m-%Y"),
("2011-12-30 00:00:00", "%Y-%m-%d %H:%M:%S"),
("2011-12-30T00:00:00", "%Y-%m-%dT%H:%M:%S"),
("2011-12-30T00:00:00UTC", "%Y-%m-%dT%H:%M:%S%Z"),
("2011-12-30T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+9", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+09", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+090", None),
("2011-12-30T00:00:00+0900", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00-0900", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+09:00", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+09:000", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+9:0", "%Y-%m-%dT%H:%M:%S%z"),
("2011-12-30T00:00:00+09:", None),
("2011-12-30T00:00:00.000000UTC", "%Y-%m-%dT%H:%M:%S.%f%Z"),
("2011-12-30T00:00:00.000000Z", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+9", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+09", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+090", None),
("2011-12-30T00:00:00.000000+0900", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000-0900", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+09:00", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+09:000", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+9:0", "%Y-%m-%dT%H:%M:%S.%f%z"),
("2011-12-30T00:00:00.000000+09:", None),
("2011-12-30 00:00:00.000000", "%Y-%m-%d %H:%M:%S.%f"),
("Tue 24 Aug 2021 01:30:48 AM", "%a %d %b %Y %H:%M:%S %p"),
("Tuesday 24 Aug 2021 01:30:48 AM", "%A %d %b %Y %H:%M:%S %p"),
],
)
def test_guess_datetime_format_with_parseable_formats(string, fmt):
result = parsing.guess_datetime_format(string)
assert result == fmt
@pytest.mark.parametrize("dayfirst,expected", [(True, "%d/%m/%Y"), (False, "%m/%d/%Y")])
def test_guess_datetime_format_with_dayfirst(dayfirst, expected):
ambiguous_string = "01/01/2011"
result = parsing.guess_datetime_format(ambiguous_string, dayfirst=dayfirst)
assert result == expected
@td.skip_if_has_locale
@pytest.mark.parametrize(
"string,fmt",
[
("30/Dec/2011", "%d/%b/%Y"),
("30/December/2011", "%d/%B/%Y"),
("30/Dec/2011 00:00:00", "%d/%b/%Y %H:%M:%S"),
],
)
def test_guess_datetime_format_with_locale_specific_formats(string, fmt):
result = parsing.guess_datetime_format(string)
assert result == fmt
@pytest.mark.parametrize(
"invalid_dt",
[
"2013",
"01/2013",
"12:00:00",
"1/1/1/1",
"this_is_not_a_datetime",
"51a",
9,
datetime(2011, 1, 1),
],
)
def test_guess_datetime_format_invalid_inputs(invalid_dt):
# A datetime string must include a year, month and a day for it to be
# guessable, in addition to being a string that looks like a datetime.
assert parsing.guess_datetime_format(invalid_dt) is None
@pytest.mark.parametrize(
"string,fmt",
[
("2011-1-1", "%Y-%m-%d"),
("1/1/2011", "%m/%d/%Y"),
("30-1-2011", "%d-%m-%Y"),
("2011-1-1 0:0:0", "%Y-%m-%d %H:%M:%S"),
("2011-1-3T00:00:0", "%Y-%m-%dT%H:%M:%S"),
("2011-1-1 00:00:00", "%Y-%m-%d %H:%M:%S"),
],
)
def test_guess_datetime_format_no_padding(string, fmt):
# see gh-11142
result = parsing.guess_datetime_format(string)
assert result == fmt
def test_try_parse_dates():
arr = np.array(["5/1/2000", "6/1/2000", "7/1/2000"], dtype=object)
result = parsing.try_parse_dates(arr, dayfirst=True)
expected = np.array([parse(d, dayfirst=True) for d in arr])
tm.assert_numpy_array_equal(result, expected)
def test_parse_time_string_check_instance_type_raise_exception():
# issue 20684
msg = "Argument 'arg' has incorrect type (expected str, got tuple)"
with pytest.raises(TypeError, match=re.escape(msg)):
parse_time_string((1, 2, 3))
result = parse_time_string("2019")
expected = (datetime(2019, 1, 1), "year")
assert result == expected
@pytest.mark.parametrize(
"fmt,expected",
[
("%Y %m %d %H:%M:%S", True),
("%Y/%m/%d %H:%M:%S", True),
(r"%Y\%m\%d %H:%M:%S", True),
("%Y-%m-%d %H:%M:%S", True),
("%Y.%m.%d %H:%M:%S", True),
("%Y%m%d %H:%M:%S", True),
("%Y-%m-%dT%H:%M:%S", True),
("%Y-%m-%dT%H:%M:%S%z", True),
("%Y-%m-%dT%H:%M:%S%Z", True),
("%Y-%m-%dT%H:%M:%S.%f", True),
("%Y-%m-%dT%H:%M:%S.%f%z", True),
("%Y-%m-%dT%H:%M:%S.%f%Z", True),
("%Y%m%d", False),
("%Y%m", False),
("%Y", False),
("%Y-%m-%d", True),
("%Y-%m", True),
],
)
def test_is_iso_format(fmt, expected):
# see gh-41047
result = parsing.format_is_iso(fmt)
assert result == expected

View File

@@ -0,0 +1,90 @@
import pytest
from pandas._libs.tslibs import to_offset
from pandas._libs.tslibs.period import (
period_asfreq,
period_ordinal,
)
def get_freq_code(freqstr: str) -> int:
off = to_offset(freqstr)
code = off._period_dtype_code
return code
@pytest.mark.parametrize(
"freq1,freq2,expected",
[
("D", "H", 24),
("D", "T", 1440),
("D", "S", 86400),
("D", "L", 86400000),
("D", "U", 86400000000),
("D", "N", 86400000000000),
("H", "T", 60),
("H", "S", 3600),
("H", "L", 3600000),
("H", "U", 3600000000),
("H", "N", 3600000000000),
("T", "S", 60),
("T", "L", 60000),
("T", "U", 60000000),
("T", "N", 60000000000),
("S", "L", 1000),
("S", "U", 1000000),
("S", "N", 1000000000),
("L", "U", 1000),
("L", "N", 1000000),
("U", "N", 1000),
],
)
def test_intra_day_conversion_factors(freq1, freq2, expected):
assert (
period_asfreq(1, get_freq_code(freq1), get_freq_code(freq2), False) == expected
)
@pytest.mark.parametrize(
"freq,expected", [("A", 0), ("M", 0), ("W", 1), ("D", 0), ("B", 0)]
)
def test_period_ordinal_start_values(freq, expected):
# information for Jan. 1, 1970.
assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq_code(freq)) == expected
@pytest.mark.parametrize(
"dt,expected",
[
((1970, 1, 4, 0, 0, 0, 0, 0), 1),
((1970, 1, 5, 0, 0, 0, 0, 0), 2),
((2013, 10, 6, 0, 0, 0, 0, 0), 2284),
((2013, 10, 7, 0, 0, 0, 0, 0), 2285),
],
)
def test_period_ordinal_week(dt, expected):
args = dt + (get_freq_code("W"),)
assert period_ordinal(*args) == expected
@pytest.mark.parametrize(
"day,expected",
[
# Thursday (Oct. 3, 2013).
(3, 11415),
# Friday (Oct. 4, 2013).
(4, 11416),
# Saturday (Oct. 5, 2013).
(5, 11417),
# Sunday (Oct. 6, 2013).
(6, 11417),
# Monday (Oct. 7, 2013).
(7, 11417),
# Tuesday (Oct. 8, 2013).
(8, 11418),
],
)
def test_period_ordinal_business_day(day, expected):
# 5000 is PeriodDtypeCode for BusinessDay
args = (2013, 10, day, 0, 0, 0, 0, 0, 5000)
assert period_ordinal(*args) == expected

View File

@@ -0,0 +1,65 @@
import re
import numpy as np
import pytest
from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds
from pandas import (
Timedelta,
offsets,
)
@pytest.mark.parametrize(
"obj,expected",
[
(np.timedelta64(14, "D"), 14 * 24 * 3600 * 1e9),
(Timedelta(minutes=-7), -7 * 60 * 1e9),
(Timedelta(minutes=-7).to_pytimedelta(), -7 * 60 * 1e9),
(Timedelta(seconds=1234e-9), 1234), # GH43764, GH40946
(
Timedelta(seconds=1e-9, milliseconds=1e-5, microseconds=1e-1),
111,
), # GH43764
(
Timedelta(days=1, seconds=1e-9, milliseconds=1e-5, microseconds=1e-1),
24 * 3600e9 + 111,
), # GH43764
(offsets.Nano(125), 125),
(1, 1),
(np.int64(2), 2),
(np.int32(3), 3),
],
)
def test_delta_to_nanoseconds(obj, expected):
result = delta_to_nanoseconds(obj)
assert result == expected
def test_delta_to_nanoseconds_error():
obj = np.array([123456789], dtype="m8[ns]")
with pytest.raises(TypeError, match="<class 'numpy.ndarray'>"):
delta_to_nanoseconds(obj)
def test_huge_nanoseconds_overflow():
# GH 32402
assert delta_to_nanoseconds(Timedelta(1e10)) == 1e10
assert delta_to_nanoseconds(Timedelta(nanoseconds=1e10)) == 1e10
@pytest.mark.parametrize(
"kwargs", [{"Seconds": 1}, {"seconds": 1, "Nanoseconds": 1}, {"Foo": 2}]
)
def test_kwarg_assertion(kwargs):
err_message = (
"cannot construct a Timedelta from the passed arguments, "
"allowed keywords are "
"[weeks, days, hours, minutes, seconds, "
"milliseconds, microseconds, nanoseconds]"
)
with pytest.raises(ValueError, match=re.escape(err_message)):
Timedelta(**kwargs)

View File

@@ -0,0 +1,168 @@
from datetime import (
datetime,
timedelta,
timezone,
)
import dateutil.tz
import pytest
import pytz
from pandas._libs.tslibs import (
conversion,
timezones,
)
from pandas import Timestamp
def test_is_utc(utc_fixture):
tz = timezones.maybe_get_tz(utc_fixture)
assert timezones.is_utc(tz)
@pytest.mark.parametrize("tz_name", list(pytz.common_timezones))
def test_cache_keys_are_distinct_for_pytz_vs_dateutil(tz_name):
if tz_name == "UTC":
pytest.skip("UTC: special case in dateutil")
tz_p = timezones.maybe_get_tz(tz_name)
tz_d = timezones.maybe_get_tz("dateutil/" + tz_name)
if tz_d is None:
pytest.skip(tz_name + ": dateutil does not know about this one")
assert timezones._p_tz_cache_key(tz_p) != timezones._p_tz_cache_key(tz_d)
def test_tzlocal_repr():
# see gh-13583
ts = Timestamp("2011-01-01", tz=dateutil.tz.tzlocal())
assert ts.tz == dateutil.tz.tzlocal()
assert "tz='tzlocal()')" in repr(ts)
def test_tzlocal_maybe_get_tz():
# see gh-13583
tz = timezones.maybe_get_tz("tzlocal()")
assert tz == dateutil.tz.tzlocal()
def test_tzlocal_offset():
# see gh-13583
#
# Get offset using normal datetime for test.
ts = Timestamp("2011-01-01", tz=dateutil.tz.tzlocal())
offset = dateutil.tz.tzlocal().utcoffset(datetime(2011, 1, 1))
offset = offset.total_seconds() * 1000000000
assert ts.value + offset == Timestamp("2011-01-01").value
def test_tzlocal_is_not_utc():
# even if the machine running the test is localized to UTC
tz = dateutil.tz.tzlocal()
assert not timezones.is_utc(tz)
assert not timezones.tz_compare(tz, dateutil.tz.tzutc())
def test_tz_compare_utc(utc_fixture, utc_fixture2):
tz = timezones.maybe_get_tz(utc_fixture)
tz2 = timezones.maybe_get_tz(utc_fixture2)
assert timezones.tz_compare(tz, tz2)
@pytest.fixture(
params=[
(pytz.timezone("US/Eastern"), lambda tz, x: tz.localize(x)),
(dateutil.tz.gettz("US/Eastern"), lambda tz, x: x.replace(tzinfo=tz)),
]
)
def infer_setup(request):
eastern, localize = request.param
start_naive = datetime(2001, 1, 1)
end_naive = datetime(2009, 1, 1)
start = localize(eastern, start_naive)
end = localize(eastern, end_naive)
return eastern, localize, start, end, start_naive, end_naive
def test_infer_tz_compat(infer_setup):
eastern, _, start, end, start_naive, end_naive = infer_setup
assert (
timezones.infer_tzinfo(start, end)
is conversion.localize_pydatetime(start_naive, eastern).tzinfo
)
assert (
timezones.infer_tzinfo(start, None)
is conversion.localize_pydatetime(start_naive, eastern).tzinfo
)
assert (
timezones.infer_tzinfo(None, end)
is conversion.localize_pydatetime(end_naive, eastern).tzinfo
)
def test_infer_tz_utc_localize(infer_setup):
_, _, start, end, start_naive, end_naive = infer_setup
utc = pytz.utc
start = utc.localize(start_naive)
end = utc.localize(end_naive)
assert timezones.infer_tzinfo(start, end) is utc
@pytest.mark.parametrize("ordered", [True, False])
def test_infer_tz_mismatch(infer_setup, ordered):
eastern, _, _, _, start_naive, end_naive = infer_setup
msg = "Inputs must both have the same timezone"
utc = pytz.utc
start = utc.localize(start_naive)
end = conversion.localize_pydatetime(end_naive, eastern)
args = (start, end) if ordered else (end, start)
with pytest.raises(AssertionError, match=msg):
timezones.infer_tzinfo(*args)
def test_maybe_get_tz_invalid_types():
with pytest.raises(TypeError, match="<class 'float'>"):
timezones.maybe_get_tz(44.0)
with pytest.raises(TypeError, match="<class 'module'>"):
timezones.maybe_get_tz(pytz)
msg = "<class 'pandas._libs.tslibs.timestamps.Timestamp'>"
with pytest.raises(TypeError, match=msg):
timezones.maybe_get_tz(Timestamp("2021-01-01", tz="UTC"))
def test_maybe_get_tz_offset_only():
# see gh-36004
# timezone.utc
tz = timezones.maybe_get_tz(timezone.utc)
assert tz == timezone(timedelta(hours=0, minutes=0))
# without UTC+- prefix
tz = timezones.maybe_get_tz("+01:15")
assert tz == timezone(timedelta(hours=1, minutes=15))
tz = timezones.maybe_get_tz("-01:15")
assert tz == timezone(-timedelta(hours=1, minutes=15))
# with UTC+- prefix
tz = timezones.maybe_get_tz("UTC+02:45")
assert tz == timezone(timedelta(hours=2, minutes=45))
tz = timezones.maybe_get_tz("UTC-02:45")
assert tz == timezone(-timedelta(hours=2, minutes=45))

View File

@@ -0,0 +1,174 @@
import re
import pytest
from pandas._libs.tslibs import (
Timedelta,
offsets,
to_offset,
)
@pytest.mark.parametrize(
"freq_input,expected",
[
(to_offset("10us"), offsets.Micro(10)),
(offsets.Hour(), offsets.Hour()),
("2h30min", offsets.Minute(150)),
("2h 30min", offsets.Minute(150)),
("2h30min15s", offsets.Second(150 * 60 + 15)),
("2h 60min", offsets.Hour(3)),
("2h 20.5min", offsets.Second(8430)),
("1.5min", offsets.Second(90)),
("0.5S", offsets.Milli(500)),
("15l500u", offsets.Micro(15500)),
("10s75L", offsets.Milli(10075)),
("1s0.25ms", offsets.Micro(1000250)),
("1s0.25L", offsets.Micro(1000250)),
("2800N", offsets.Nano(2800)),
("2SM", offsets.SemiMonthEnd(2)),
("2SM-16", offsets.SemiMonthEnd(2, day_of_month=16)),
("2SMS-14", offsets.SemiMonthBegin(2, day_of_month=14)),
("2SMS-15", offsets.SemiMonthBegin(2)),
],
)
def test_to_offset(freq_input, expected):
result = to_offset(freq_input)
assert result == expected
@pytest.mark.parametrize(
"freqstr,expected", [("-1S", -1), ("-2SM", -2), ("-1SMS", -1), ("-5min10s", -310)]
)
def test_to_offset_negative(freqstr, expected):
result = to_offset(freqstr)
assert result.n == expected
@pytest.mark.parametrize(
"freqstr",
[
"2h20m",
"U1",
"-U",
"3U1",
"-2-3U",
"-2D:3H",
"1.5.0S",
"2SMS-15-15",
"2SMS-15D",
"100foo",
# Invalid leading +/- signs.
"+-1d",
"-+1h",
"+1",
"-7",
"+d",
"-m",
# Invalid shortcut anchors.
"SM-0",
"SM-28",
"SM-29",
"SM-FOO",
"BSM",
"SM--1",
"SMS-1",
"SMS-28",
"SMS-30",
"SMS-BAR",
"SMS-BYR",
"BSMS",
"SMS--2",
],
)
def test_to_offset_invalid(freqstr):
# see gh-13930
# We escape string because some of our
# inputs contain regex special characters.
msg = re.escape(f"Invalid frequency: {freqstr}")
with pytest.raises(ValueError, match=msg):
to_offset(freqstr)
def test_to_offset_no_evaluate():
msg = str(("", ""))
with pytest.raises(TypeError, match=msg):
to_offset(("", ""))
def test_to_offset_tuple_unsupported():
with pytest.raises(TypeError, match="pass as a string instead"):
to_offset((5, "T"))
@pytest.mark.parametrize(
"freqstr,expected",
[
("2D 3H", offsets.Hour(51)),
("2 D3 H", offsets.Hour(51)),
("2 D 3 H", offsets.Hour(51)),
(" 2 D 3 H ", offsets.Hour(51)),
(" H ", offsets.Hour()),
(" 3 H ", offsets.Hour(3)),
],
)
def test_to_offset_whitespace(freqstr, expected):
result = to_offset(freqstr)
assert result == expected
@pytest.mark.parametrize(
"freqstr,expected", [("00H 00T 01S", 1), ("-00H 03T 14S", -194)]
)
def test_to_offset_leading_zero(freqstr, expected):
result = to_offset(freqstr)
assert result.n == expected
@pytest.mark.parametrize("freqstr,expected", [("+1d", 1), ("+2h30min", 150)])
def test_to_offset_leading_plus(freqstr, expected):
result = to_offset(freqstr)
assert result.n == expected
@pytest.mark.parametrize(
"kwargs,expected",
[
({"days": 1, "seconds": 1}, offsets.Second(86401)),
({"days": -1, "seconds": 1}, offsets.Second(-86399)),
({"hours": 1, "minutes": 10}, offsets.Minute(70)),
({"hours": 1, "minutes": -10}, offsets.Minute(50)),
({"weeks": 1}, offsets.Day(7)),
({"hours": 1}, offsets.Hour(1)),
({"hours": 1}, to_offset("60min")),
({"microseconds": 1}, offsets.Micro(1)),
({"microseconds": 0}, offsets.Nano(0)),
],
)
def test_to_offset_pd_timedelta(kwargs, expected):
# see gh-9064
td = Timedelta(**kwargs)
result = to_offset(td)
assert result == expected
@pytest.mark.parametrize(
"shortcut,expected",
[
("W", offsets.Week(weekday=6)),
("W-SUN", offsets.Week(weekday=6)),
("Q", offsets.QuarterEnd(startingMonth=12)),
("Q-DEC", offsets.QuarterEnd(startingMonth=12)),
("Q-MAY", offsets.QuarterEnd(startingMonth=5)),
("SM", offsets.SemiMonthEnd(day_of_month=15)),
("SM-15", offsets.SemiMonthEnd(day_of_month=15)),
("SM-1", offsets.SemiMonthEnd(day_of_month=1)),
("SM-27", offsets.SemiMonthEnd(day_of_month=27)),
("SMS-2", offsets.SemiMonthBegin(day_of_month=2)),
("SMS-27", offsets.SemiMonthBegin(day_of_month=27)),
],
)
def test_anchored_shortcuts(shortcut, expected):
result = to_offset(shortcut)
assert result == expected