diff --git a/pandas/tests/tslibs/test_array_to_datetime.py b/pandas/tests/tslibs/test_array_to_datetime.py index ff8880257b225..f5b036dde2094 100644 --- a/pandas/tests/tslibs/test_array_to_datetime.py +++ b/pandas/tests/tslibs/test_array_to_datetime.py @@ -12,159 +12,145 @@ import pandas.util.testing as tm -class TestParseISO8601(object): - @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(self, date_str, exp): - # GH#12060 - # test only the iso parser - flexibility to different - # separators and leadings 0s - # Timestamp construction falls back to dateutil - 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', - # wrong separator for HHMMSS - '2001-01-01 12-34-56']) - def test_parsers_iso8601_invalid(self, date_str): - # separators must all match - YYYYMM not valid - with pytest.raises(ValueError): - tslib._test_parse_iso8601(date_str) - - -class TestArrayToDatetime(object): - def test_parsing_valid_dates(self): - arr = np.array(['01-01-2013', '01-02-2013'], dtype=object) - result, _ = tslib.array_to_datetime(arr) - expected = ['2013-01-01T00:00:00.000000000-0000', - '2013-01-02T00:00:00.000000000-0000'] - tm.assert_numpy_array_equal( - result, - np_array_datetime64_compat(expected, dtype='M8[ns]')) +@pytest.mark.parametrize("data,expected", [ + (["01-01-2013", "01-02-2013"], + ["2013-01-01T00:00:00.000000000-0000", + "2013-01-02T00:00:00.000000000-0000"]), + (["Mon Sep 16 2013", "Tue Sep 17 2013"], + ["2013-09-16T00:00:00.000000000-0000", + "2013-09-17T00:00:00.000000000-0000"]) +]) +def test_parsing_valid_dates(data, expected): + arr = np.array(data, dtype=object) + result, _ = tslib.array_to_datetime(arr) + + expected = np_array_datetime64_compat(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 = dict(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]") - arr = np.array(['Mon Sep 16 2013', 'Tue Sep 17 2013'], dtype=object) - result, _ = tslib.array_to_datetime(arr) - expected = ['2013-09-16T00:00:00.000000000-0000', - '2013-09-17T00:00:00.000000000-0000'] - tm.assert_numpy_array_equal( - result, - np_array_datetime64_compat(expected, dtype='M8[ns]')) - - @pytest.mark.parametrize('dt_string, expected_tz', [ - ['01-01-2013 08:00:00+08:00', pytz.FixedOffset(480)], - ['2013-01-01T08:00:00.000000000+0800', pytz.FixedOffset(480)], - ['2012-12-31T16:00:00.000000000-0800', pytz.FixedOffset(-480)], - ['12-31-2012 23:00:00-01:00', pytz.FixedOffset(-60)]]) - def test_parsing_timezone_offsets(self, 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 expected_tz - def test_parsing_non_iso_timezone_offset(self): - 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(self): - # 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 - - def test_number_looking_strings_not_into_datetime(self): - # GH#4601 - # These strings don't look like datetimes so they shouldn't be - # attempted to be converted - arr = np.array(['-352.737091', '183.575577'], dtype=object) - result, _ = tslib.array_to_datetime(arr, errors='ignore') - tm.assert_numpy_array_equal(result, arr) - arr = np.array(['1', '2', '3', '4', '5'], dtype=object) - result, _ = tslib.array_to_datetime(arr, errors='ignore') - tm.assert_numpy_array_equal(result, arr) +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") - @pytest.mark.parametrize('invalid_date', [ - date(1000, 1, 1), - datetime(1000, 1, 1), - '1000-01-01', - 'Jan 1, 1000', - np.datetime64('1000-01-01')]) - def test_coerce_outside_ns_bounds(self, invalid_date): - arr = np.array([invalid_date], dtype='object') - with pytest.raises(ValueError): - tslib.array_to_datetime(arr, errors='raise') - - result, _ = tslib.array_to_datetime(arr, errors='coerce') - expected = np.array([iNaT], dtype='M8[ns]') - tm.assert_numpy_array_equal(result, expected) + expected = [iNaT, "2000-01-01T00:00:00.000000000-0000"] + expected = np_array_datetime64_compat(expected, dtype="M8[ns]") - def test_coerce_outside_ns_bounds_one_valid(self): - 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-0000'] - tm.assert_numpy_array_equal( - result, - np_array_datetime64_compat(expected, dtype='M8[ns]')) + tm.assert_numpy_array_equal(result, expected) - def test_coerce_of_invalid_datetimes(self): - arr = np.array(['01-01-2013', 'not_a_date', '1'], dtype=object) - # Without coercing, the presence of any invalid dates prevents - # any values from being converted - result, _ = tslib.array_to_datetime(arr, errors='ignore') - tm.assert_numpy_array_equal(result, arr) +@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 = dict(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-0000', + result, _ = tslib.array_to_datetime(arr, errors="coerce") + expected = ["2013-01-01T00:00:00.000000000-0000", iNaT, iNaT] tm.assert_numpy_array_equal( result, - np_array_datetime64_compat(expected, dtype='M8[ns]')) - - def test_to_datetime_barely_out_of_bounds(self): - # GH#19529 - # GH#19382 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) - with pytest.raises(tslib.OutOfBoundsDatetime): - tslib.array_to_datetime(arr) + np_array_datetime64_compat(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) diff --git a/pandas/tests/tslibs/test_ccalendar.py b/pandas/tests/tslibs/test_ccalendar.py index b5d562a7b5a9c..255558a80018b 100644 --- a/pandas/tests/tslibs/test_ccalendar.py +++ b/pandas/tests/tslibs/test_ccalendar.py @@ -2,17 +2,24 @@ from datetime import datetime import numpy as np +import pytest from pandas._libs.tslibs import ccalendar -def test_get_day_of_year(): - assert ccalendar.get_day_of_year(2001, 3, 1) == 60 - assert ccalendar.get_day_of_year(2004, 3, 1) == 61 - assert ccalendar.get_day_of_year(1907, 12, 31) == 365 - assert ccalendar.get_day_of_year(2004, 12, 31) == 366 +@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 diff --git a/pandas/tests/tslibs/test_conversion.py b/pandas/tests/tslibs/test_conversion.py index 6bfc686ba830e..13398a69b4982 100644 --- a/pandas/tests/tslibs/test_conversion.py +++ b/pandas/tests/tslibs/test_conversion.py @@ -11,61 +11,58 @@ import pandas.util.testing as tm -def compare_utc_to_local(tz_didx, utc_didx): - f = lambda x: conversion.tz_convert_single(x, UTC, tz_didx.tz) +def _compare_utc_to_local(tz_didx): + def f(x): + return conversion.tz_convert_single(x, UTC, tz_didx.tz) + result = conversion.tz_convert(tz_didx.asi8, UTC, tz_didx.tz) - result_single = np.vectorize(f)(tz_didx.asi8) - tm.assert_numpy_array_equal(result, result_single) + expected = np.vectorize(f)(tz_didx.asi8) + + tm.assert_numpy_array_equal(result, expected) -def compare_local_to_utc(tz_didx, utc_didx): - f = lambda x: conversion.tz_convert_single(x, tz_didx.tz, UTC) +def _compare_local_to_utc(tz_didx, utc_didx): + def f(x): + return conversion.tz_convert_single(x, tz_didx.tz, UTC) + result = conversion.tz_convert(utc_didx.asi8, tz_didx.tz, UTC) - result_single = np.vectorize(f)(utc_didx.asi8) - tm.assert_numpy_array_equal(result, result_single) - - -class TestTZConvert(object): - - @pytest.mark.parametrize('tz', ['UTC', 'Asia/Tokyo', - 'US/Eastern', 'Europe/Moscow']) - def test_tz_convert_single_matches_tz_convert_hourly(self, tz): - # US: 2014-03-09 - 2014-11-11 - # MOSCOW: 2014-10-26 / 2014-12-31 - tz_didx = date_range('2014-03-01', '2015-01-10', freq='H', tz=tz) - utc_didx = date_range('2014-03-01', '2015-01-10', freq='H') - compare_utc_to_local(tz_didx, utc_didx) - - # local tz to UTC can be differ in hourly (or higher) freqs because - # of DST - compare_local_to_utc(tz_didx, utc_didx) - - @pytest.mark.parametrize('tz', ['UTC', 'Asia/Tokyo', - 'US/Eastern', 'Europe/Moscow']) - @pytest.mark.parametrize('freq', ['D', 'A']) - def test_tz_convert_single_matches_tz_convert(self, tz, freq): - tz_didx = date_range('2000-01-01', '2020-01-01', freq=freq, tz=tz) - utc_didx = date_range('2000-01-01', '2020-01-01', freq=freq) - compare_utc_to_local(tz_didx, utc_didx) - compare_local_to_utc(tz_didx, utc_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(self, arr): - result = conversion.tz_convert(arr, - timezones.maybe_get_tz('US/Eastern'), - timezones.maybe_get_tz('Asia/Tokyo')) - tm.assert_numpy_array_equal(result, arr) - - -class TestEnsureDatetime64NS(object): - @pytest.mark.parametrize('copy', [True, False]) - @pytest.mark.parametrize('dtype', ['M8[ns]', 'M8[s]']) - def test_length_zero_copy(self, dtype, copy): - arr = np.array([], dtype=dtype) - result = conversion.ensure_datetime64ns(arr, copy=copy) - if copy: - assert result.base is None - else: - assert result.base is arr + expected = np.vectorize(f)(utc_didx.asi8) + + 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) + utc_didx = date_range("2014-03-01", "2015-01-10", freq="H") + + _compare_utc_to_local(tz_didx) + _compare_local_to_utc(tz_didx, utc_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) + utc_didx = date_range("2000-01-01", "2020-01-01", freq=freq) + + _compare_utc_to_local(tz_didx) + _compare_local_to_utc(tz_didx, utc_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 = conversion.tz_convert(arr, + timezones.maybe_get_tz("US/Eastern"), + timezones.maybe_get_tz("Asia/Tokyo")) + 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) diff --git a/pandas/tests/tslibs/test_libfrequencies.py b/pandas/tests/tslibs/test_libfrequencies.py index 1bf6d0596e2fe..b9b1c72dbf2e1 100644 --- a/pandas/tests/tslibs/test_libfrequencies.py +++ b/pandas/tests/tslibs/test_libfrequencies.py @@ -9,108 +9,92 @@ from pandas.tseries import offsets -def assert_aliases_deprecated(freq, expected, aliases): +@pytest.mark.parametrize("obj,expected", [ + ("W", "DEC"), + (offsets.Week(), "DEC"), + + ("D", "DEC"), + (offsets.Day(), "DEC"), + + ("Q", "DEC"), + (offsets.QuarterEnd(startingMonth=12), "DEC"), + + ("Q-JAN", "JAN"), + (offsets.QuarterEnd(startingMonth=1), "JAN"), + + ("A-DEC", "DEC"), + ("Y-DEC", "DEC"), + (offsets.YearEnd(), "DEC"), + + ("A-MAY", "MAY"), + ("Y-MAY", "MAY"), + (offsets.YearEnd(month=5), "MAY") +]) +def test_get_rule_month(obj, expected): + result = get_rule_month(obj) + assert result == expected + + +@pytest.mark.parametrize("obj,expected", [ + ("A", 1000), + ("A-DEC", 1000), + ("A-JAN", 1001), + + ("Y", 1000), + ("Y-DEC", 1000), + ("Y-JAN", 1001), + + ("Q", 2000), + ("Q-DEC", 2000), + ("Q-FEB", 2002), + + ("W", 4000), + ("W-SUN", 4000), + ("W-FRI", 4005), + + ("Min", 8000), + ("ms", 10000), + ("US", 11000), + ("NS", 12000) +]) +def test_period_str_to_code(obj, expected): + assert _period_str_to_code(obj) == expected + + +@pytest.mark.parametrize("p1,p2,expected", [ + # Input validation. + (offsets.MonthEnd(), None, False), + (offsets.YearEnd(), None, False), + (None, offsets.YearEnd(), False), + (None, offsets.MonthEnd(), False), + (None, None, False), + + (offsets.YearEnd(), offsets.MonthEnd(), True), + (offsets.Hour(), offsets.Minute(), True), + (offsets.Second(), offsets.Milli(), True), + (offsets.Milli(), offsets.Micro(), True), + (offsets.Micro(), offsets.Nano(), True) +]) +def test_super_sub_symmetry(p1, p2, expected): + assert is_superperiod(p1, p2) is expected + assert is_subperiod(p2, p1) is expected + + +@pytest.mark.parametrize("freq,expected,aliases", [ + ("D", 6000, ["DAY", "DLY", "DAILY"]), + ("M", 3000, ["MTH", "MONTH", "MONTHLY"]), + ("N", 12000, ["NANOSECOND", "NANOSECONDLY"]), + ("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]), + ("T", 8000, ["minute", "MINUTE", "MINUTELY"]), + ("L", 10000, ["MILLISECOND", "MILLISECONDLY"]), + ("U", 11000, ["MICROSECOND", "MICROSECONDLY"]), + ("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]), + ("B", 5000, ["BUS", "BUSINESS", "BUSINESSLY", "WEEKDAY"]), +]) +def test_assert_aliases_deprecated(freq, expected, aliases): assert isinstance(aliases, list) - assert (_period_str_to_code(freq) == expected) + assert _period_str_to_code(freq) == expected for alias in aliases: with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG): _period_str_to_code(alias) - - -def test_get_rule_month(): - result = get_rule_month('W') - assert (result == 'DEC') - result = get_rule_month(offsets.Week()) - assert (result == 'DEC') - - result = get_rule_month('D') - assert (result == 'DEC') - result = get_rule_month(offsets.Day()) - assert (result == 'DEC') - - result = get_rule_month('Q') - assert (result == 'DEC') - result = get_rule_month(offsets.QuarterEnd(startingMonth=12)) - - result = get_rule_month('Q-JAN') - assert (result == 'JAN') - result = get_rule_month(offsets.QuarterEnd(startingMonth=1)) - assert (result == 'JAN') - - result = get_rule_month('A-DEC') - assert (result == 'DEC') - result = get_rule_month('Y-DEC') - assert (result == 'DEC') - result = get_rule_month(offsets.YearEnd()) - assert (result == 'DEC') - - result = get_rule_month('A-MAY') - assert (result == 'MAY') - result = get_rule_month('Y-MAY') - assert (result == 'MAY') - result = get_rule_month(offsets.YearEnd(month=5)) - assert (result == 'MAY') - - -def test_period_str_to_code(): - assert (_period_str_to_code('A') == 1000) - assert (_period_str_to_code('A-DEC') == 1000) - assert (_period_str_to_code('A-JAN') == 1001) - assert (_period_str_to_code('Y') == 1000) - assert (_period_str_to_code('Y-DEC') == 1000) - assert (_period_str_to_code('Y-JAN') == 1001) - - assert (_period_str_to_code('Q') == 2000) - assert (_period_str_to_code('Q-DEC') == 2000) - assert (_period_str_to_code('Q-FEB') == 2002) - - assert_aliases_deprecated("M", 3000, ["MTH", "MONTH", "MONTHLY"]) - - assert (_period_str_to_code('W') == 4000) - assert (_period_str_to_code('W-SUN') == 4000) - assert (_period_str_to_code('W-FRI') == 4005) - - assert_aliases_deprecated("B", 5000, ["BUS", "BUSINESS", - "BUSINESSLY", "WEEKDAY"]) - assert_aliases_deprecated("D", 6000, ["DAY", "DLY", "DAILY"]) - assert_aliases_deprecated("H", 7000, ["HR", "HOUR", "HRLY", "HOURLY"]) - - assert_aliases_deprecated("T", 8000, ["minute", "MINUTE", "MINUTELY"]) - assert (_period_str_to_code('Min') == 8000) - - assert_aliases_deprecated("S", 9000, ["sec", "SEC", "SECOND", "SECONDLY"]) - assert_aliases_deprecated("L", 10000, ["MILLISECOND", "MILLISECONDLY"]) - assert (_period_str_to_code('ms') == 10000) - - assert_aliases_deprecated("U", 11000, ["MICROSECOND", "MICROSECONDLY"]) - assert (_period_str_to_code('US') == 11000) - - assert_aliases_deprecated("N", 12000, ["NANOSECOND", "NANOSECONDLY"]) - assert (_period_str_to_code('NS') == 12000) - - -def test_is_superperiod_subperiod(): - - # input validation - assert not (is_superperiod(offsets.YearEnd(), None)) - assert not (is_subperiod(offsets.MonthEnd(), None)) - assert not (is_superperiod(None, offsets.YearEnd())) - assert not (is_subperiod(None, offsets.MonthEnd())) - assert not (is_superperiod(None, None)) - assert not (is_subperiod(None, None)) - - assert (is_superperiod(offsets.YearEnd(), offsets.MonthEnd())) - assert (is_subperiod(offsets.MonthEnd(), offsets.YearEnd())) - - assert (is_superperiod(offsets.Hour(), offsets.Minute())) - assert (is_subperiod(offsets.Minute(), offsets.Hour())) - - assert (is_superperiod(offsets.Second(), offsets.Milli())) - assert (is_subperiod(offsets.Milli(), offsets.Second())) - - assert (is_superperiod(offsets.Milli(), offsets.Micro())) - assert (is_subperiod(offsets.Micro(), offsets.Milli())) - - assert (is_superperiod(offsets.Micro(), offsets.Nano())) - assert (is_subperiod(offsets.Nano(), offsets.Micro())) diff --git a/pandas/tests/tslibs/test_liboffsets.py b/pandas/tests/tslibs/test_liboffsets.py index 388df6453634e..cb699278595e7 100644 --- a/pandas/tests/tslibs/test_liboffsets.py +++ b/pandas/tests/tslibs/test_liboffsets.py @@ -12,161 +12,163 @@ from pandas import Timestamp -def test_get_lastbday(): +@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 liboffsets.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 liboffsets.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 dt.weekday() == 3 # i.e. this is a business day - assert liboffsets.get_lastbday(dt.year, dt.month) == 30 + assert liboffsets.shift_month(dt, months, day_opt=day_opt) == expected - dt = datetime(1993, 10, 31) - assert dt.weekday() == 6 # i.e. this is not a business day - assert liboffsets.get_lastbday(dt.year, dt.month) == 29 +@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_get_firstbday(): - dt = datetime(2017, 4, 1) - assert dt.weekday() == 5 # i.e. not a weekday - assert liboffsets.get_firstbday(dt.year, dt.month) == 3 - - dt = datetime(1993, 10, 1) - assert dt.weekday() == 4 # i.e. a business day - assert liboffsets.get_firstbday(dt.year, dt.month) == 1 - - -def test_shift_month(): - dt = datetime(2017, 11, 30) - assert liboffsets.shift_month(dt, 0, 'business_end') == dt - assert liboffsets.shift_month(dt, 0, - 'business_start') == datetime(2017, 11, 1) - - ts = Timestamp('1929-05-05') - assert liboffsets.shift_month(ts, 1, 'start') == Timestamp('1929-06-01') - assert liboffsets.shift_month(ts, -3, 'end') == Timestamp('1929-02-28') - - assert liboffsets.shift_month(ts, 25, None) == Timestamp('1931-06-5') - - # Try to shift to April 31, then shift back to Apr 30 to get a real date - assert liboffsets.shift_month(ts, -1, 31) == Timestamp('1929-04-30') +def test_shift_month_error(): dt = datetime(2017, 11, 15) + day_opt = "this should raise" - assert liboffsets.shift_month(dt, 0, day_opt=None) == dt - assert liboffsets.shift_month(dt, 0, day_opt=15) == dt + with pytest.raises(ValueError, match=day_opt): + liboffsets.shift_month(dt, 3, day_opt=day_opt) - assert liboffsets.shift_month(dt, 1, - day_opt='start') == datetime(2017, 12, 1) - assert liboffsets.shift_month(dt, -145, - day_opt='end') == datetime(2005, 10, 31) +@pytest.mark.parametrize("other,expected", [ + # Before March 1. + (datetime(2017, 2, 10), {2: 1, -7: -7, 0: 0}), - with pytest.raises(ValueError): - liboffsets.shift_month(dt, 3, day_opt='this should raise') + # 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_yearday(other, expected, n): + month = 3 + day_opt = "start" # `other` will be compared to March 1. + assert liboffsets.roll_yearday(other, n, month, day_opt) == expected[n] -def test_get_day_of_month(): - # get_day_of_month is not directly exposed; we test it via roll_yearday - dt = datetime(2017, 11, 15) - with pytest.raises(ValueError): - # To hit the raising case we need month == dt.month and n > 0 - liboffsets.roll_yearday(dt, n=3, month=11, day_opt='foo') +@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_yearday2(other, expected, n): + month = 6 + day_opt = "end" # `other` will be compared to June 30. -def test_roll_yearday(): - # Copied from doctest examples - month = 3 - day_opt = 'start' # `other` will be compared to March 1 - other = datetime(2017, 2, 10) # before March 1 - assert liboffsets.roll_yearday(other, 2, month, day_opt) == 1 - assert liboffsets.roll_yearday(other, -7, month, day_opt) == -7 - assert liboffsets.roll_yearday(other, 0, month, day_opt) == 0 + assert liboffsets.roll_yearday(other, n, month, day_opt) == expected[n] - other = Timestamp('2014-03-15', tz='US/Eastern') # after March 1 - assert liboffsets.roll_yearday(other, 2, month, day_opt) == 2 - assert liboffsets.roll_yearday(other, -7, month, day_opt) == -6 - assert liboffsets.roll_yearday(other, 0, month, day_opt) == 1 - month = 6 - day_opt = 'end' # `other` will be compared to June 30 - other = datetime(1999, 6, 29) # before June 30 - assert liboffsets.roll_yearday(other, 5, month, day_opt) == 4 - assert liboffsets.roll_yearday(other, -7, month, day_opt) == -7 - assert liboffsets.roll_yearday(other, 0, month, day_opt) == 0 - - other = Timestamp(2072, 8, 24, 6, 17, 18) # after June 30 - assert liboffsets.roll_yearday(other, 5, month, day_opt) == 5 - assert liboffsets.roll_yearday(other, -7, month, day_opt) == -6 - assert liboffsets.roll_yearday(other, 0, month, day_opt) == 1 - - -def test_roll_qtrday(): - other = Timestamp(2072, 10, 1, 6, 17, 18) # Saturday - for day_opt in ['start', 'end', 'business_start', 'business_end']: - # as long as (other.month % 3) != (month % 3), day_opt is irrelevant - # the `day_opt` doesn't matter. - month = 5 # (other.month % 3) < (month % 3) - assert roll_qtrday(other, 4, month, day_opt, modby=3) == 3 - assert roll_qtrday(other, -3, month, day_opt, modby=3) == -3 - - month = 3 # (other.month % 3) > (month % 3) - assert roll_qtrday(other, 4, month, day_opt, modby=3) == 4 - assert roll_qtrday(other, -3, month, day_opt, modby=3) == -2 - - month = 2 - other = datetime(1999, 5, 31) # Monday - # has (other.month % 3) == (month % 3) - - n = 2 - assert roll_qtrday(other, n, month, 'start', modby=3) == n - assert roll_qtrday(other, n, month, 'end', modby=3) == n - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - - n = -1 - assert roll_qtrday(other, n, month, 'start', modby=3) == n + 1 - assert roll_qtrday(other, n, month, 'end', modby=3) == n - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n + 1 - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - - other = Timestamp(2072, 10, 1, 6, 17, 18) # Saturday - month = 4 # (other.month % 3) == (month % 3) - n = 2 - assert roll_qtrday(other, n, month, 'start', modby=3) == n - assert roll_qtrday(other, n, month, 'end', modby=3) == n - 1 - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n - 1 - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - 1 - - n = -1 - assert roll_qtrday(other, n, month, 'start', modby=3) == n - assert roll_qtrday(other, n, month, 'end', modby=3) == n - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - - other = Timestamp(2072, 10, 3, 6, 17, 18) # First businessday - month = 4 # (other.month % 3) == (month % 3) - n = 2 - assert roll_qtrday(other, n, month, 'start', modby=3) == n - assert roll_qtrday(other, n, month, 'end', modby=3) == n - 1 - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - 1 - - n = -1 - assert roll_qtrday(other, n, month, 'start', modby=3) == n + 1 - assert roll_qtrday(other, n, month, 'end', modby=3) == n - assert roll_qtrday(other, n, month, 'business_start', modby=3) == n - assert roll_qtrday(other, n, month, 'business_end', modby=3) == n - - -def test_roll_convention(): - other = 29 - before = 1 - after = 31 - - n = 42 - assert liboffsets.roll_convention(other, n, other) == n - assert liboffsets.roll_convention(other, n, before) == n - assert liboffsets.roll_convention(other, n, after) == n - 1 - - n = -4 - assert liboffsets.roll_convention(other, n, other) == n - assert liboffsets.roll_convention(other, n, before) == n + 1 - assert liboffsets.roll_convention(other, n, after) == n +def test_get_day_of_month_error(): + # get_day_of_month is not directly exposed. + # We test it via roll_yearday. + 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. + liboffsets.roll_yearday(dt, n=3, month=11, day_opt=day_opt) + + +@pytest.mark.parametrize("month", [ + 3, # (other.month % 3) < (month % 3) + 5 # (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] diff --git a/pandas/tests/tslibs/test_normalize_date.py b/pandas/tests/tslibs/test_normalize_date.py new file mode 100644 index 0000000000000..6124121b97186 --- /dev/null +++ b/pandas/tests/tslibs/test_normalize_date.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +"""Tests for functions from pandas._libs.tslibs""" + +from datetime import date, datetime + +import pytest + +from pandas._libs import tslibs + + +@pytest.mark.parametrize("value,expected", [ + (date(2012, 9, 7), datetime(2012, 9, 7)), + (datetime(2012, 9, 7, 12), datetime(2012, 9, 7)), + (datetime(2007, 10, 1, 1, 12, 5, 10), datetime(2007, 10, 1)) +]) +def test_normalize_date(value, expected): + result = tslibs.normalize_date(value) + assert result == expected diff --git a/pandas/tests/tslibs/test_parse_iso8601.py b/pandas/tests/tslibs/test_parse_iso8601.py new file mode 100644 index 0000000000000..d1b3dee948afe --- /dev/null +++ b/pandas/tests/tslibs/test_parse_iso8601.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +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 = "Error parsing datetime string \"{s}\"".format(s=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 = ("Timezone hours offset out of range " + "in datetime string \"{s}\"".format(s=date_str)) + + with pytest.raises(ValueError, match=msg): + tslib._test_parse_iso8601(date_str) diff --git a/pandas/tests/tslibs/test_parsing.py b/pandas/tests/tslibs/test_parsing.py index 45a841cd1136d..597ec6df7389f 100644 --- a/pandas/tests/tslibs/test_parsing.py +++ b/pandas/tests/tslibs/test_parsing.py @@ -10,168 +10,177 @@ from pandas._libs.tslibs import parsing from pandas._libs.tslibs.parsing import parse_time_string -import pandas.compat as compat import pandas.util._test_decorators as td from pandas.util import testing as tm -class TestParseQuarters(object): - - def test_parse_time_string(self): - (date, parsed, reso) = parse_time_string('4Q1984') - (date_lower, parsed_lower, reso_lower) = parse_time_string('4q1984') - assert date == date_lower - assert parsed == parsed_lower - assert reso == reso_lower - - def test_parse_time_quarter_w_dash(self): - # https://github.com/pandas-dev/pandas/issue/9688 - pairs = [('1988-Q2', '1988Q2'), ('2Q-1988', '2Q1988')] - - for dashed, normal in pairs: - (date_dash, parsed_dash, reso_dash) = parse_time_string(dashed) - (date, parsed, reso) = parse_time_string(normal) - - assert date_dash == date - assert parsed_dash == parsed - assert reso_dash == reso - - pytest.raises(parsing.DateParseError, parse_time_string, "-2Q1992") - pytest.raises(parsing.DateParseError, parse_time_string, "2-Q1992") - pytest.raises(parsing.DateParseError, parse_time_string, "4-4Q1992") - - -class TestDatetimeParsingWrappers(object): - def test_does_not_convert_mixed_integer(self): - bad_date_strings = ('-50000', '999', '123.1234', 'm', 'T') - - for bad_date_string in bad_date_strings: - assert not parsing._does_string_look_like_datetime(bad_date_string) - - good_date_strings = ('2012-01-01', - '01/01/2012', - 'Mon Sep 16, 2013', - '01012012', - '0101', - '1-1') - - for good_date_string in good_date_strings: - assert parsing._does_string_look_like_datetime(good_date_string) - - def test_parsers_quarterly_with_freq(self): - msg = ('Incorrect quarterly string is given, quarter ' - 'must be between 1 and 4: 2013Q5') - with pytest.raises(parsing.DateParseError, match=msg): - parsing.parse_time_string('2013Q5') - - # GH 5418 - msg = ('Unable to retrieve month information from given freq: ' - 'INVLD-L-DEC-SAT') - with pytest.raises(parsing.DateParseError, match=msg): - parsing.parse_time_string('2013Q1', freq='INVLD-L-DEC-SAT') - - cases = {('2013Q2', None): datetime(2013, 4, 1), - ('2013Q2', 'A-APR'): datetime(2012, 8, 1), - ('2013-Q2', 'A-DEC'): datetime(2013, 4, 1)} - - for (date_str, freq), exp in compat.iteritems(cases): - result, _, _ = parsing.parse_time_string(date_str, freq=freq) - assert result == exp - - def test_parsers_quarter_invalid(self): - - cases = ['2Q 2005', '2Q-200A', '2Q-200', '22Q2005', '6Q-20', '2Q200.'] - for case in cases: - pytest.raises(ValueError, parsing.parse_time_string, case) - - def test_parsers_monthfreq(self): - cases = {'201101': datetime(2011, 1, 1, 0, 0), - '200005': datetime(2000, 5, 1, 0, 0)} - - for date_str, expected in compat.iteritems(cases): - result1, _, _ = parsing.parse_time_string(date_str, freq='M') - assert result1 == expected - - -class TestGuessDatetimeFormat(object): - - @td.skip_if_not_us_locale - @pytest.mark.parametrize( - "string, format", - [ - ('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-30 00:00:00.000000', - '%Y-%m-%d %H:%M:%S.%f')]) - def test_guess_datetime_format_with_parseable_formats( - self, string, format): - result = parsing._guess_datetime_format(string) - assert result == format - - @pytest.mark.parametrize( - "dayfirst, expected", - [ - (True, "%d/%m/%Y"), - (False, "%m/%d/%Y")]) - def test_guess_datetime_format_with_dayfirst(self, 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, format", - [ - ('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( - self, string, format): - result = parsing._guess_datetime_format(string) - assert result == format - - def test_guess_datetime_format_invalid_inputs(self): - # 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 - invalid_dts = [ - '2013', - '01/2013', - '12:00:00', - '1/1/1/1', - 'this_is_not_a_datetime', - '51a', - 9, - datetime(2011, 1, 1), - ] - - for invalid_dt in invalid_dts: - assert parsing._guess_datetime_format(invalid_dt) is None - - @pytest.mark.parametrize( - "string, format", - [ - ('2011-1-1', '%Y-%m-%d'), - ('30-1-2011', '%d-%m-%Y'), - ('1/1/2011', '%m/%d/%Y'), - ('2011-1-1 00:00:00', '%Y-%m-%d %H:%M:%S'), - ('2011-1-1 0:0:0', '%Y-%m-%d %H:%M:%S'), - ('2011-1-3T00:00:0', '%Y-%m-%dT%H:%M:%S')]) - def test_guess_datetime_format_nopadding(self, string, format): - # GH 11142 - result = parsing._guess_datetime_format(string) - assert result == format - - -class TestArrayToDatetime(object): - def test_try_parse_dates(self): - 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(): + (date, parsed, reso) = parse_time_string("4Q1984") + (date_lower, parsed_lower, reso_lower) = parse_time_string("4q1984") + + assert date == date_lower + assert reso == reso_lower + assert parsed == parsed_lower + + +@pytest.mark.parametrize("dashed,normal", [ + ("1988-Q2", "1988Q2"), + ("2Q-1988", "2Q1988") +]) +def test_parse_time_quarter_with_dash(dashed, normal): + # see gh-9688 + (date_dash, parsed_dash, reso_dash) = parse_time_string(dashed) + (date, parsed, reso) = parse_time_string(normal) + + assert date_dash == date + 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 = ("Unknown datetime string format, " + "unable to parse: {dashed}".format(dashed=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", dict(), + ("Incorrect quarterly string is given, " + "quarter must be between 1 and 4: 2013Q5")), + + # see gh-5418 + ("2013Q1", dict(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 " + "must be between 1 and 4: {date_str}".format(date_str=date_str)) + else: + msg = ("Unknown datetime string format, unable " + "to parse: {date_str}".format(date_str=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-30 00:00:00.000000", "%Y-%m-%d %H:%M:%S.%f") +]) +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) diff --git a/pandas/tests/tslibs/test_period_asfreq.py b/pandas/tests/tslibs/test_period_asfreq.py index e5978a59bc2a1..6a9522e705318 100644 --- a/pandas/tests/tslibs/test_period_asfreq.py +++ b/pandas/tests/tslibs/test_period_asfreq.py @@ -1,82 +1,87 @@ # -*- coding: utf-8 -*- +import pytest + from pandas._libs.tslibs.frequencies import get_freq from pandas._libs.tslibs.period import period_asfreq, period_ordinal -class TestPeriodFreqConversion(object): - - def test_intraday_conversion_factors(self): - assert period_asfreq(1, get_freq('D'), get_freq('H'), False) == 24 - assert period_asfreq(1, get_freq('D'), get_freq('T'), False) == 1440 - assert period_asfreq(1, get_freq('D'), get_freq('S'), False) == 86400 - assert period_asfreq(1, get_freq('D'), - get_freq('L'), False) == 86400000 - assert period_asfreq(1, get_freq('D'), - get_freq('U'), False) == 86400000000 - assert period_asfreq(1, get_freq('D'), - get_freq('N'), False) == 86400000000000 - - assert period_asfreq(1, get_freq('H'), get_freq('T'), False) == 60 - assert period_asfreq(1, get_freq('H'), get_freq('S'), False) == 3600 - assert period_asfreq(1, get_freq('H'), - get_freq('L'), False) == 3600000 - assert period_asfreq(1, get_freq('H'), - get_freq('U'), False) == 3600000000 - assert period_asfreq(1, get_freq('H'), - get_freq('N'), False) == 3600000000000 - - assert period_asfreq(1, get_freq('T'), get_freq('S'), False) == 60 - assert period_asfreq(1, get_freq('T'), get_freq('L'), False) == 60000 - assert period_asfreq(1, get_freq('T'), - get_freq('U'), False) == 60000000 - assert period_asfreq(1, get_freq('T'), - get_freq('N'), False) == 60000000000 - - assert period_asfreq(1, get_freq('S'), get_freq('L'), False) == 1000 - assert period_asfreq(1, get_freq('S'), - get_freq('U'), False) == 1000000 - assert period_asfreq(1, get_freq('S'), - get_freq('N'), False) == 1000000000 - - assert period_asfreq(1, get_freq('L'), get_freq('U'), False) == 1000 - assert period_asfreq(1, get_freq('L'), - get_freq('N'), False) == 1000000 - - assert period_asfreq(1, get_freq('U'), get_freq('N'), False) == 1000 - - def test_period_ordinal_start_values(self): - # information for 1.1.1970 - assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('A')) == 0 - assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('M')) == 0 - assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('W')) == 1 - assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('D')) == 0 - assert period_ordinal(1970, 1, 1, 0, 0, 0, 0, 0, get_freq('B')) == 0 - - def test_period_ordinal_week(self): - assert period_ordinal(1970, 1, 4, 0, 0, 0, 0, 0, get_freq('W')) == 1 - assert period_ordinal(1970, 1, 5, 0, 0, 0, 0, 0, get_freq('W')) == 2 - assert period_ordinal(2013, 10, 6, 0, - 0, 0, 0, 0, get_freq('W')) == 2284 - assert period_ordinal(2013, 10, 7, 0, - 0, 0, 0, 0, get_freq('W')) == 2285 - - def test_period_ordinal_business_day(self): - # Thursday - assert period_ordinal(2013, 10, 3, 0, - 0, 0, 0, 0, get_freq('B')) == 11415 - # Friday - assert period_ordinal(2013, 10, 4, 0, - 0, 0, 0, 0, get_freq('B')) == 11416 - # Saturday - assert period_ordinal(2013, 10, 5, 0, - 0, 0, 0, 0, get_freq('B')) == 11417 - # Sunday - assert period_ordinal(2013, 10, 6, 0, - 0, 0, 0, 0, get_freq('B')) == 11417 - # Monday - assert period_ordinal(2013, 10, 7, 0, - 0, 0, 0, 0, get_freq('B')) == 11417 - # Tuesday - assert period_ordinal(2013, 10, 8, 0, - 0, 0, 0, 0, get_freq('B')) == 11418 +@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(freq1), + get_freq(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(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("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): + args = (2013, 10, day, 0, 0, 0, 0, 0, get_freq("B")) + assert period_ordinal(*args) == expected diff --git a/pandas/tests/tslibs/test_timedeltas.py b/pandas/tests/tslibs/test_timedeltas.py index 50e64bb7c2082..fdc8eff80acad 100644 --- a/pandas/tests/tslibs/test_timedeltas.py +++ b/pandas/tests/tslibs/test_timedeltas.py @@ -5,37 +5,25 @@ from pandas._libs.tslibs.timedeltas import delta_to_nanoseconds import pandas as pd - - -def test_delta_to_nanoseconds(): - obj = np.timedelta64(14, 'D') - result = delta_to_nanoseconds(obj) - assert result == 14 * 24 * 3600 * 1e9 - - obj = pd.Timedelta(minutes=-7) - result = delta_to_nanoseconds(obj) - assert result == -7 * 60 * 1e9 - - obj = pd.Timedelta(minutes=-7).to_pytimedelta() +from pandas import Timedelta + + +@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), + (pd.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 == -7 * 60 * 1e9 + assert result == expected - obj = pd.offsets.Nano(125) - result = delta_to_nanoseconds(obj) - assert result == 125 - - obj = 1 - result = delta_to_nanoseconds(obj) - assert obj == 1 - obj = np.int64(2) - result = delta_to_nanoseconds(obj) - assert obj == 2 - - obj = np.int32(3) - result = delta_to_nanoseconds(obj) - assert result == 3 +def test_delta_to_nanoseconds_error(): + obj = np.array([123456789], dtype="m8[ns]") - obj = np.array([123456789], dtype='m8[ns]') - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="<(class|type) 'numpy.ndarray'>"): delta_to_nanoseconds(obj) diff --git a/pandas/tests/tslibs/test_timezones.py b/pandas/tests/tslibs/test_timezones.py index 68a6c1b09b992..0255865dbdf71 100644 --- a/pandas/tests/tslibs/test_timezones.py +++ b/pandas/tests/tslibs/test_timezones.py @@ -10,39 +10,51 @@ from pandas import Timestamp -@pytest.mark.parametrize('tz_name', list(pytz.common_timezones)) +@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': - # skip utc as it's a special case in dateutil - return + 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) + tz_d = timezones.maybe_get_tz("dateutil/" + tz_name) + if tz_d is None: - # skip timezones that dateutil doesn't know about. - return + 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(): - # GH#13583 - ts = Timestamp('2011-01-01', tz=dateutil.tz.tzlocal()) +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() - # get offset using normal datetime for test + +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 + assert ts.value + offset == Timestamp("2011-01-01").value -@pytest.mark.parametrize('eastern, localize', [ - (pytz.timezone('US/Eastern'), lambda tz, x: tz.localize(x)), - (dateutil.tz.gettz('US/Eastern'), lambda tz, x: x.replace(tzinfo=tz))]) -def test_infer_tz(eastern, localize): - utc = pytz.utc + +@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) @@ -50,6 +62,12 @@ def test_infer_tz(eastern, localize): 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 @@ -57,12 +75,27 @@ def test_infer_tz(eastern, localize): 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) - with pytest.raises(Exception): - timezones.infer_tzinfo(start, end) - with pytest.raises(Exception): - timezones.infer_tzinfo(end, start) + + args = (start, end) if ordered else (end, start) + + with pytest.raises(AssertionError, match=msg): + timezones.infer_tzinfo(*args) diff --git a/pandas/tests/tslibs/test_tslib.py b/pandas/tests/tslibs/test_tslib.py deleted file mode 100644 index 17bd46cd235da..0000000000000 --- a/pandas/tests/tslibs/test_tslib.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -"""Tests for functions from pandas._libs.tslibs""" - -from datetime import date, datetime - -from pandas._libs import tslibs - - -def test_normalize_date(): - value = date(2012, 9, 7) - - result = tslibs.normalize_date(value) - assert (result == datetime(2012, 9, 7)) - - value = datetime(2012, 9, 7, 12) - - result = tslibs.normalize_date(value) - assert (result == datetime(2012, 9, 7)) - - value = datetime(2007, 10, 1, 1, 12, 5, 10) - - actual = tslibs.normalize_date(value) - assert actual == datetime(2007, 10, 1)