Skip to content

Commit e369e26

Browse files
committed
Adds parsing of more ISO 8601 strings.
1 parent 3fc993b commit e369e26

File tree

2 files changed

+84
-44
lines changed

2 files changed

+84
-44
lines changed

pendulum/parsing/parser.py

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,26 @@
1010

1111

1212
class Parser(object):
13-
1413
COMMON = re.compile(
1514
# Date (optional)
1615
'^'
17-
'(?P<date>'
18-
' (?P<year>\d{4})' # Year
19-
' (?P<monthday>'
20-
' (?P<monthsep>-|/)?(?P<month>\d{2})' # Month (optional)
21-
' ((?:-|/)?(?P<day>\d{1,2}))?' # Day (optional)
22-
' )?'
16+
'('
17+
' (?P<date>' # Classic date (YYYY-MM-DD) or ordinal (YYYY-DDD)
18+
' (?P<year>\d{4})' # Year
19+
' (?P<monthday>'
20+
' (?P<monthsep>-|/)?(?P<month>\d{2})' # Month (optional)
21+
' ((?:-|/)?(?P<day>\d{1,2}))?' # Day (optional)
22+
' )?'
23+
' )'
24+
' |'
25+
' (?P<isocalendar>' # Calendar date (2016-W05 or 2016-W05-5)
26+
' (?P<isoyear>\d{4})' # Year
27+
' -?' # Separator (optional)
28+
' W' # W separator
29+
' (?P<isoweek>\d{2})' # Week number
30+
' -?' # Separator (optional)
31+
' (?P<isoweekday>\d)?' # Weekday (optional)
32+
' )'
2333
')?'
2434

2535
# Time (optional)
@@ -40,17 +50,6 @@ class Parser(object):
4050
re.VERBOSE
4151
)
4252

43-
ISO8601_WEEK = re.compile(
44-
'^'
45-
'(\d{4})' # Year
46-
'-?' # Separator (optional)
47-
'W' # W separator
48-
'(\d{2})' # Week number
49-
'-?' # Separator (optional)
50-
'(\d)?' # Weekday (optional)
51-
'$'
52-
)
53-
5453
DEFAULT_OPTIONS = {
5554
'day_first': False,
5655
'strict': False,
@@ -82,7 +81,23 @@ def parse_common(self, text):
8281
has_date = False
8382

8483
if m:
85-
if m.group('date'):
84+
if m.group('isocalendar'):
85+
date = self._get_iso_8601_week(
86+
m.group('isoyear'),
87+
m.group('isoweek'),
88+
m.group('isoweekday')
89+
)
90+
91+
year = date['year']
92+
month = date['month']
93+
day = date['day']
94+
95+
parsed.update({
96+
'year': year,
97+
'month': month,
98+
'day': day,
99+
})
100+
elif m.group('date'):
86101
has_date = True
87102
# A date has been specified
88103
year = int(m.group('year'))
@@ -197,26 +212,7 @@ def parse_common(self, text):
197212

198213
return parsed
199214

200-
def parse_iso_8601_week(self, text):
201-
"""
202-
Tries to parse a ISO 8601 week string.
203-
204-
2012-W05
205-
2012-W05-5
206-
207-
:param text: The string to parse.
208-
:type text: str
209-
210-
:rtype: dict
211-
"""
212-
m = self.ISO8601_WEEK.match(text)
213-
214-
if not m:
215-
return {}
216-
217-
year = m.group(1)
218-
week = m.group(2)
219-
weekday = m.group(3)
215+
def _get_iso_8601_week(self, year, week, weekday):
220216
if not weekday:
221217
weekday = '1'
222218

@@ -271,11 +267,6 @@ def normalize(self, parsed):
271267
return default
272268

273269
def _parse(self, text):
274-
# ISO8601 week notation
275-
parsed = self.parse_iso_8601_week(text)
276-
if parsed:
277-
return parsed
278-
279270
parsed = self.parse_common(text)
280271
if parsed:
281272
return parsed

tests/parsing_test/test_parser.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,55 @@ def test_iso8601_week_number(self):
332332
self.assertEqual(0, parsed['subsecond'])
333333
self.assertEqual(None, parsed['offset'])
334334

335+
def test_iso8601_week_number_with_time(self):
336+
text = '2012-W05T09'
337+
338+
parsed = Parser().parse(text)
339+
self.assertEqual(2012, parsed['year'])
340+
self.assertEqual(1, parsed['month'])
341+
self.assertEqual(30, parsed['day'])
342+
self.assertEqual(9, parsed['hour'])
343+
self.assertEqual(0, parsed['minute'])
344+
self.assertEqual(0, parsed['second'])
345+
self.assertEqual(0, parsed['subsecond'])
346+
self.assertEqual(None, parsed['offset'])
347+
348+
text = '2012W05T09'
349+
350+
parsed = Parser().parse(text)
351+
self.assertEqual(2012, parsed['year'])
352+
self.assertEqual(1, parsed['month'])
353+
self.assertEqual(30, parsed['day'])
354+
self.assertEqual(9, parsed['hour'])
355+
self.assertEqual(0, parsed['minute'])
356+
self.assertEqual(0, parsed['second'])
357+
self.assertEqual(0, parsed['subsecond'])
358+
self.assertEqual(None, parsed['offset'])
359+
360+
text = '2012-W05-5T09'
361+
362+
parsed = Parser().parse(text)
363+
self.assertEqual(2012, parsed['year'])
364+
self.assertEqual(2, parsed['month'])
365+
self.assertEqual(3, parsed['day'])
366+
self.assertEqual(9, parsed['hour'])
367+
self.assertEqual(0, parsed['minute'])
368+
self.assertEqual(0, parsed['second'])
369+
self.assertEqual(0, parsed['subsecond'])
370+
self.assertEqual(None, parsed['offset'])
371+
372+
text = '2012W055T09'
373+
374+
parsed = Parser().parse(text)
375+
self.assertEqual(2012, parsed['year'])
376+
self.assertEqual(2, parsed['month'])
377+
self.assertEqual(3, parsed['day'])
378+
self.assertEqual(9, parsed['hour'])
379+
self.assertEqual(0, parsed['minute'])
380+
self.assertEqual(0, parsed['second'])
381+
self.assertEqual(0, parsed['subsecond'])
382+
self.assertEqual(None, parsed['offset'])
383+
335384
def test_iso8601_ordinal(self):
336385
text = '2012-007'
337386

0 commit comments

Comments
 (0)