Skip to content

Commit 8d2818e

Browse files
committed
Merge pull request #9701 from williamhogman/feature-dashed-quarters
ENH Make year and quarter dash-separatable
2 parents a477202 + 91c63a6 commit 8d2818e

File tree

3 files changed

+39
-15
lines changed

3 files changed

+39
-15
lines changed

doc/source/whatsnew/v0.16.1.txt

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ API changes
3232

3333

3434

35+
- Add support for separating years and quarters using dashes, for
36+
example 2014-Q1. (:issue:`9688`)
37+
3538
.. _whatsnew_0161.performance:
3639

3740
Performance Improvements

pandas/tseries/tests/test_offsets.py

+28-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pandas import Series
2222
from pandas.tseries.frequencies import _offset_map
2323
from pandas.tseries.index import _to_m8, DatetimeIndex, _daterange_cache, date_range
24-
from pandas.tseries.tools import parse_time_string
24+
from pandas.tseries.tools import parse_time_string, DateParseError
2525
import pandas.tseries.offsets as offsets
2626

2727
from pandas.io.pickle import read_pickle
@@ -3010,12 +3010,33 @@ def test_get_offset():
30103010
(name, expected, offset))
30113011

30123012

3013-
def test_parse_time_string():
3014-
(date, parsed, reso) = parse_time_string('4Q1984')
3015-
(date_lower, parsed_lower, reso_lower) = parse_time_string('4q1984')
3016-
assert date == date_lower
3017-
assert parsed == parsed_lower
3018-
assert reso == reso_lower
3013+
class TestParseTimeString(tm.TestCase):
3014+
3015+
def test_parse_time_string(self):
3016+
(date, parsed, reso) = parse_time_string('4Q1984')
3017+
(date_lower, parsed_lower, reso_lower) = parse_time_string('4q1984')
3018+
self.assertEqual(date, date_lower)
3019+
self.assertEqual(parsed, parsed_lower)
3020+
self.assertEqual(reso, reso_lower)
3021+
3022+
def test_parse_time_quarter_w_dash(self):
3023+
# https://github.com/pydata/pandas/issue/9688
3024+
pairs = [
3025+
('1988-Q2', '1988Q2'),
3026+
('2Q-1988', '2Q1988'),
3027+
]
3028+
3029+
for dashed, normal in pairs:
3030+
(date_dash, parsed_dash, reso_dash) = parse_time_string(dashed)
3031+
(date, parsed, reso) = parse_time_string(normal)
3032+
3033+
self.assertEqual(date_dash, date)
3034+
self.assertEqual(parsed_dash, parsed)
3035+
self.assertEqual(reso_dash, reso)
3036+
3037+
self.assertRaises(DateParseError, parse_time_string, "-2Q1992")
3038+
self.assertRaises(DateParseError, parse_time_string, "2-Q1992")
3039+
self.assertRaises(DateParseError, parse_time_string, "4-4Q1992")
30193040

30203041

30213042
def test_get_standard_freq():

pandas/tseries/tools.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,10 @@ def calc_with_mask(carg,mask):
383383
return None
384384

385385
# patterns for quarters like '4Q2005', '05Q1'
386-
qpat1full = re.compile(r'(\d)Q(\d\d\d\d)')
387-
qpat2full = re.compile(r'(\d\d\d\d)Q(\d)')
388-
qpat1 = re.compile(r'(\d)Q(\d\d)')
389-
qpat2 = re.compile(r'(\d\d)Q(\d)')
386+
qpat1full = re.compile(r'(\d)Q-?(\d\d\d\d)')
387+
qpat2full = re.compile(r'(\d\d\d\d)-?Q(\d)')
388+
qpat1 = re.compile(r'(\d)Q-?(\d\d)')
389+
qpat2 = re.compile(r'(\d\d)-?Q(\d)')
390390
ypat = re.compile(r'(\d\d\d\d)$')
391391
has_time = re.compile('(.+)([\s]|T)+(.+)')
392392

@@ -424,18 +424,18 @@ def parse_time_string(arg, freq=None, dayfirst=None, yearfirst=None):
424424
second=0, microsecond=0)
425425

426426
# special handling for possibilities eg, 2Q2005, 2Q05, 2005Q1, 05Q1
427-
if len(arg) in [4, 6]:
427+
if len(arg) in [4, 5, 6, 7]:
428428
m = ypat.match(arg)
429429
if m:
430430
ret = default.replace(year=int(m.group(1)))
431431
return ret, ret, 'year'
432432

433433
add_century = False
434-
if len(arg) == 4:
434+
if len(arg) > 5:
435+
qpats = [(qpat1full, 1), (qpat2full, 0)]
436+
else:
435437
add_century = True
436438
qpats = [(qpat1, 1), (qpat2, 0)]
437-
else:
438-
qpats = [(qpat1full, 1), (qpat2full, 0)]
439439

440440
for pat, yfirst in qpats:
441441
qparse = pat.match(arg)

0 commit comments

Comments
 (0)