Skip to content

Commit 4f38261

Browse files
committed
Use regex to crack RFC 3339 w/ nanosecond precision.
Avoids manual parsing, preseres initial strptime-generated error for malformed timestamp strings. Addresses: #1569 (comment)
1 parent 7e769c0 commit 4f38261

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

gcloud/_helpers.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@
3838

3939
_NOW = datetime.datetime.utcnow # To be replaced by tests.
4040
_RFC3339_MICROS = '%Y-%m-%dT%H:%M:%S.%fZ'
41-
_RFC3339_NO_FRACTION_NO_ZULU = '%Y-%m-%dT%H:%M:%S'
41+
_RFC3339_NO_FRACTION = '%Y-%m-%dT%H:%M:%S'
42+
# datetime.strptime cannot handle nanosecond precision: parse w/ regex
43+
_RFC3339_NANOS = re.compile(r"""
44+
(?P<no_fraction>
45+
\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2} # YYYY-MM-DDTHH:MM:SS
46+
)
47+
\. # decimal point
48+
(?P<nanos>\d{9}) # nanoseconds
49+
Z # Zulu
50+
""", re.VERBOSE)
4251

4352

4453
class _LocalStack(Local):
@@ -319,15 +328,14 @@ def _rfc3339_to_datetime(dt_str):
319328
return datetime.datetime.strptime(
320329
dt_str, _RFC3339_MICROS).replace(tzinfo=UTC)
321330
except ValueError: # maybe nanosecond resoultion
322-
prefix, suffix = dt_str.split('.')
323-
fraction, zulu = suffix[:-1], suffix[-1]
324-
if zulu == 'Z' and len(fraction) == 9: # nanoseconds
325-
nanos = int(fraction)
326-
micros = nanos // 1000
327-
bare_seconds = datetime.datetime.strptime(
328-
prefix, _RFC3339_NO_FRACTION_NO_ZULU)
329-
return bare_seconds.replace(microsecond=micros, tzinfo=UTC)
330-
raise
331+
with_nanos = _RFC3339_NANOS.match(dt_str)
332+
if with_nanos is None:
333+
raise
334+
bare_seconds = datetime.datetime.strptime(
335+
with_nanos.group('no_fraction'), _RFC3339_NO_FRACTION)
336+
nanos = int(with_nanos.group('nanos'))
337+
micros = nanos // 1000
338+
return bare_seconds.replace(microsecond=micros, tzinfo=UTC)
331339

332340

333341
def _datetime_to_rfc3339(value):

0 commit comments

Comments
 (0)