|
38 | 38 |
|
39 | 39 | _NOW = datetime.datetime.utcnow # To be replaced by tests. |
40 | 40 | _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) |
42 | 51 |
|
43 | 52 |
|
44 | 53 | class _LocalStack(Local): |
@@ -319,15 +328,14 @@ def _rfc3339_to_datetime(dt_str): |
319 | 328 | return datetime.datetime.strptime( |
320 | 329 | dt_str, _RFC3339_MICROS).replace(tzinfo=UTC) |
321 | 330 | 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) |
331 | 339 |
|
332 | 340 |
|
333 | 341 | def _datetime_to_rfc3339(value): |
|
0 commit comments