diff --git a/doc/source/whatsnew/v0.16.1.txt b/doc/source/whatsnew/v0.16.1.txt index bf01d3b21f3fa..9c88815887674 100644 --- a/doc/source/whatsnew/v0.16.1.txt +++ b/doc/source/whatsnew/v0.16.1.txt @@ -22,6 +22,9 @@ API changes Enhancements ~~~~~~~~~~~~ +- Add support for separating years and quarters using dashes, for + example 2014-Q1. (:issue:`9688`) + .. _whatsnew_0161.performance: Performance Improvements diff --git a/pandas/tseries/tests/test_offsets.py b/pandas/tseries/tests/test_offsets.py index 609ffc4c93c10..0793508b4912c 100644 --- a/pandas/tseries/tests/test_offsets.py +++ b/pandas/tseries/tests/test_offsets.py @@ -21,7 +21,7 @@ from pandas import Series from pandas.tseries.frequencies import _offset_map from pandas.tseries.index import _to_m8, DatetimeIndex, _daterange_cache, date_range -from pandas.tseries.tools import parse_time_string +from pandas.tseries.tools import parse_time_string, DateParseError import pandas.tseries.offsets as offsets from pandas.io.pickle import read_pickle @@ -3010,12 +3010,33 @@ def test_get_offset(): (name, expected, offset)) -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 parsed == parsed_lower - assert reso == reso_lower +class TestParseTimeString(tm.TestCase): + + def test_parse_time_string(self): + (date, parsed, reso) = parse_time_string('4Q1984') + (date_lower, parsed_lower, reso_lower) = parse_time_string('4q1984') + self.assertEqual(date, date_lower) + self.assertEqual(parsed, parsed_lower) + self.assertEqual(reso, reso_lower) + + def test_parse_time_quarter_w_dash(self): + # https://github.com/pydata/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) + + self.assertEqual(date_dash, date) + self.assertEqual(parsed_dash, parsed) + self.assertEqual(reso_dash, reso) + + self.assertRaises(DateParseError, parse_time_string, "-2Q1992") + self.assertRaises(DateParseError, parse_time_string, "2-Q1992") + self.assertRaises(DateParseError, parse_time_string, "4-4Q1992") def test_get_standard_freq(): diff --git a/pandas/tseries/tools.py b/pandas/tseries/tools.py index db62fceaceef8..8430e0209fd78 100644 --- a/pandas/tseries/tools.py +++ b/pandas/tseries/tools.py @@ -383,10 +383,10 @@ def calc_with_mask(carg,mask): return None # patterns for quarters like '4Q2005', '05Q1' -qpat1full = re.compile(r'(\d)Q(\d\d\d\d)') -qpat2full = re.compile(r'(\d\d\d\d)Q(\d)') -qpat1 = re.compile(r'(\d)Q(\d\d)') -qpat2 = re.compile(r'(\d\d)Q(\d)') +qpat1full = re.compile(r'(\d)Q-?(\d\d\d\d)') +qpat2full = re.compile(r'(\d\d\d\d)-?Q(\d)') +qpat1 = re.compile(r'(\d)Q-?(\d\d)') +qpat2 = re.compile(r'(\d\d)-?Q(\d)') ypat = re.compile(r'(\d\d\d\d)$') has_time = re.compile('(.+)([\s]|T)+(.+)') @@ -424,18 +424,18 @@ def parse_time_string(arg, freq=None, dayfirst=None, yearfirst=None): second=0, microsecond=0) # special handling for possibilities eg, 2Q2005, 2Q05, 2005Q1, 05Q1 - if len(arg) in [4, 6]: + if len(arg) in [4, 5, 6, 7]: m = ypat.match(arg) if m: ret = default.replace(year=int(m.group(1))) return ret, ret, 'year' add_century = False - if len(arg) == 4: + if len(arg) > 5: + qpats = [(qpat1full, 1), (qpat2full, 0)] + else: add_century = True qpats = [(qpat1, 1), (qpat2, 0)] - else: - qpats = [(qpat1full, 1), (qpat2full, 0)] for pat, yfirst in qpats: qparse = pat.match(arg)