Skip to content

Commit 0f1b1ab

Browse files
committed
move parsing to a sub function, add additional test
1 parent 4e75ae3 commit 0f1b1ab

File tree

2 files changed

+56
-24
lines changed

2 files changed

+56
-24
lines changed

pandas/_libs/tslibs/strptime.pyx

+46-23
Original file line numberDiff line numberDiff line change
@@ -292,29 +292,7 @@ def array_strptime(ndarray[object] values, object fmt,
292292
break
293293
elif parse_code == 19:
294294
z = found_dict['z']
295-
if z == 'Z':
296-
gmtoff = 0
297-
else:
298-
if z[3] == ':':
299-
z = z[:3] + z[4:]
300-
if len(z) > 5:
301-
if z[5] != ':':
302-
msg = "Inconsistent use of : in {0}"
303-
raise ValueError(msg.format(found_dict['z']))
304-
z = z[:5] + z[6:]
305-
hours = int(z[1:3])
306-
minutes = int(z[3:5])
307-
seconds = int(z[5:7] or 0)
308-
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
309-
gmtoff_remainder = z[8:]
310-
# Pad to always return microseconds.
311-
pad_number = 6 - len(gmtoff_remainder)
312-
gmtoff_remainder_padding = "0" * pad_number
313-
gmtoff_fraction = int(gmtoff_remainder +
314-
gmtoff_remainder_padding)
315-
if z.startswith("-"):
316-
gmtoff = -gmtoff
317-
gmtoff_fraction = -gmtoff_fraction
295+
gmtoff, gmtoff_fraction = _parse_timezone_directive(z)
318296

319297
# If we know the wk of the year and what day of that wk, we can figure
320298
# out the Julian day of the year.
@@ -677,3 +655,48 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year,
677655
else:
678656
days_to_week = week_0_length + (7 * (week_of_year - 1))
679657
return 1 + days_to_week + day_of_week
658+
659+
cdef _parse_timezone_directive(object z):
660+
"""
661+
Parse the '%z' directive and return an offset from UTC
662+
663+
Parameters
664+
----------
665+
z : string-like of the UTC offset
666+
667+
Returns
668+
-------
669+
tuple of (offset, offset_fraction)
670+
"""
671+
672+
cdef:
673+
int gmtoff, gmtoff_fraction, hours, minutes, seconds, pad_number
674+
object gmtoff_remainder, gmtoff_remainder_padding
675+
676+
if z == 'Z':
677+
gmtoff = 0
678+
gmtoff_fraction = 0
679+
else:
680+
if z[3] == ':':
681+
z = z[:3] + z[4:]
682+
if len(z) > 5:
683+
if z[5] != ':':
684+
msg = "Inconsistent use of : in {0}"
685+
raise ValueError(msg.format(found_dict['z']))
686+
z = z[:5] + z[6:]
687+
hours = int(z[1:3])
688+
minutes = int(z[3:5])
689+
seconds = int(z[5:7] or 0)
690+
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
691+
gmtoff_remainder = z[8:]
692+
693+
# Pad to always return microseconds.
694+
pad_number = 6 - len(gmtoff_remainder)
695+
gmtoff_remainder_padding = "0" * pad_number
696+
gmtoff_fraction = int(gmtoff_remainder +
697+
gmtoff_remainder_padding)
698+
if z.startswith("-"):
699+
gmtoff = -gmtoff
700+
gmtoff_fraction = -gmtoff_fraction
701+
702+
return (gmtoff, gmtoff_fraction)

pandas/tests/indexes/datetimes/test_tools.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
from pandas import (isna, to_datetime, Timestamp, Series, DataFrame,
2626
Index, DatetimeIndex, NaT, date_range, compat)
2727

28+
if PY3:
29+
from datetime import timezone
30+
2831

2932
class TestTimeConversionFormats(object):
3033

@@ -186,7 +189,6 @@ def test_to_datetime_format_weeks(self, cache):
186189
@pytest.mark.skipif(not PY3,
187190
reason="datetime.timezone not supported in PY2")
188191
def test_to_datetime_parse_timezone(self):
189-
from datetime import timezone
190192
# %Z parsing only
191193
fmt = '%Y-%m-%d %H:%M:%S %Z'
192194
dates = ['2010-01-01 12:00:00 UTC'] * 2
@@ -240,6 +242,13 @@ def test_to_datetime_parse_timezone(self):
240242
with pytest.raises(ValueError):
241243
pd.to_datetime(dates, format=fmt, utc=True)
242244

245+
@pytest.mark.parametrize('cache', ['+0', '-1foo', 'UTCbar', ':10'])
246+
def test_to_datetime_parse_timezone_malformed(self, offset):
247+
fmt = '%Y-%m-%d %H:%M:%S %z'
248+
date = '2010-01-01 12:00:00 ' + offset
249+
with pytest.raises(ValueError):
250+
pd.to_datetime([date], format=fmt)
251+
243252

244253
class TestToDatetime(object):
245254
def test_to_datetime_pydatetime(self):

0 commit comments

Comments
 (0)