diff options
author | chemelnucfin <alan.c.wu@gmail.com> | 2018-03-09 15:58:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-09 15:58:44 -0800 |
commit | 49ddc9d4f5ad995f05ac2dd64d7f570abc924da2 (patch) | |
tree | d1f27f7864d40845ff9525a7a876dec0ca48068e /google | |
parent | e1976858f124e16bf452013f6308bf8a1103f797 (diff) | |
download | python-api-core-49ddc9d4f5ad995f05ac2dd64d7f570abc924da2.tar.gz |
Move DatetimeWithNanoSeconds to api_core (#4979)
* Move DatetimewithNanoSeconds to api_core
Diffstat (limited to 'google')
-rw-r--r-- | google/api_core/datetime_helpers.py | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/google/api_core/datetime_helpers.py b/google/api_core/datetime_helpers.py index e3f720a..393d2d6 100644 --- a/google/api_core/datetime_helpers.py +++ b/google/api_core/datetime_helpers.py @@ -179,3 +179,71 @@ def to_rfc3339(value, ignore_zone=True): value = value.replace(tzinfo=None) - value.utcoffset() return value.strftime(_RFC3339_MICROS) + + +class DatetimeWithNanoseconds(datetime.datetime): + """Track nanosecond in addition to normal datetime attrs. + + Nanosecond can be passed only as a keyword argument. + """ + __slots__ = ('_nanosecond',) + + # pylint: disable=arguments-differ + def __new__(cls, *args, **kw): + nanos = kw.pop('nanosecond', 0) + if nanos > 0: + if 'microsecond' in kw: + raise TypeError( + "Specify only one of 'microsecond' or 'nanosecond'") + kw['microsecond'] = nanos // 1000 + inst = datetime.datetime.__new__(cls, *args, **kw) + inst._nanosecond = nanos or 0 + return inst + # pylint: disable=arguments-differ + + @property + def nanosecond(self): + """Read-only: nanosecond precision.""" + return self._nanosecond + + def rfc3339(self): + """Return an RFC 3339-compliant timestamp. + + Returns: + (str): Timestamp string according to RFC 3339 spec. + """ + if self._nanosecond == 0: + return to_rfc3339(self) + nanos = str(self._nanosecond).rstrip('0') + return '{}.{}Z'.format(self.strftime(_RFC3339_NO_FRACTION), nanos) + + @classmethod + def from_rfc3339(cls, stamp): + """Parse RFC 3339-compliant timestamp, preserving nanoseconds. + + Args: + stamp (str): RFC 3339 stamp, with up to nanosecond precision + + Returns: + :class:`DatetimeWithNanoseconds`: + an instance matching the timestamp string + + Raises: + ValueError: if `stamp` does not match the expected format + """ + with_nanos = _RFC3339_NANOS.match(stamp) + if with_nanos is None: + raise ValueError( + 'Timestamp: {}, does not match pattern: {}'.format( + stamp, _RFC3339_NANOS.pattern)) + bare = datetime.datetime.strptime( + with_nanos.group('no_fraction'), _RFC3339_NO_FRACTION) + fraction = with_nanos.group('nanos') + if fraction is None: + nanos = 0 + else: + scale = 9 - len(fraction) + nanos = int(fraction) * (10 ** scale) + return cls(bare.year, bare.month, bare.day, + bare.hour, bare.minute, bare.second, + nanosecond=nanos, tzinfo=pytz.UTC) |