OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 Google Inc. All Rights Reserved. |
| 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at |
| 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. |
| 14 |
| 15 """timestamp provides functions that support working with timestamps. |
| 16 |
| 17 :func:`to_rfc3339` and :func:`from_rfc3339` convert between standard python |
| 18 datetime types and the rfc3339 representation used in json messsages. |
| 19 |
| 20 :func:`compare` allows comparison of any timestamp representation, either the |
| 21 standard python datetime types, or an rfc3339 string representation |
| 22 |
| 23 """ |
| 24 |
| 25 from __future__ import absolute_import |
| 26 |
| 27 import datetime |
| 28 import logging |
| 29 |
| 30 import strict_rfc3339 |
| 31 |
| 32 logger = logging.getLogger(__name__) |
| 33 |
| 34 |
| 35 _EPOCH_START = datetime.datetime(1970, 1, 1) |
| 36 |
| 37 |
| 38 def compare(a, b): |
| 39 """Compares two timestamps. |
| 40 |
| 41 ``a`` and ``b`` must be the same type, in addition to normal |
| 42 representations of timestamps that order naturally, they can be rfc3339 |
| 43 formatted strings. |
| 44 |
| 45 Args: |
| 46 a (string|object): a timestamp |
| 47 b (string|object): another timestamp |
| 48 |
| 49 Returns: |
| 50 int: -1 if a < b, 0 if a == b or 1 if a > b |
| 51 |
| 52 Raises: |
| 53 ValueError: if a or b are not the same type |
| 54 ValueError: if a or b strings but not in valid rfc3339 format |
| 55 |
| 56 """ |
| 57 a_is_text = isinstance(a, basestring) |
| 58 b_is_text = isinstance(b, basestring) |
| 59 if type(a) != type(b) and not (a_is_text and b_is_text): |
| 60 logger.error('Cannot compare %s to %s, types differ %s!=%s', |
| 61 a, b, type(a), type(b)) |
| 62 raise ValueError('cannot compare inputs of differing types') |
| 63 |
| 64 if a_is_text: |
| 65 a = from_rfc3339(a, with_nanos=True) |
| 66 b = from_rfc3339(b, with_nanos=True) |
| 67 |
| 68 if a < b: |
| 69 return -1 |
| 70 elif a > b: |
| 71 return 1 |
| 72 else: |
| 73 return 0 |
| 74 |
| 75 |
| 76 def to_rfc3339(timestamp): |
| 77 """Converts ``timestamp`` to an RFC 3339 date string format. |
| 78 |
| 79 ``timestamp`` can be either a ``datetime.datetime`` or a |
| 80 ``datetime.timedelta``. Instances of the later are assumed to be a delta |
| 81 with the beginining of the unix epoch, 1st of January, 1970 |
| 82 |
| 83 The returned string is always Z-normalized. Examples of the return format: |
| 84 '1972-01-01T10:00:20.021Z' |
| 85 |
| 86 Args: |
| 87 timestamp (datetime|timedelta): represents the timestamp to convert |
| 88 |
| 89 Returns: |
| 90 string: timestamp converted to a rfc3339 compliant string as above |
| 91 |
| 92 Raises: |
| 93 ValueError: if timestamp is not a datetime.datetime or datetime.timedelta |
| 94 |
| 95 """ |
| 96 if isinstance(timestamp, datetime.datetime): |
| 97 timestamp = timestamp - _EPOCH_START |
| 98 if not isinstance(timestamp, datetime.timedelta): |
| 99 logger.error('Could not convert %s to a rfc3339 time,', timestamp) |
| 100 raise ValueError('Invalid timestamp type') |
| 101 return strict_rfc3339.timestamp_to_rfc3339_utcoffset( |
| 102 timestamp.total_seconds()) |
| 103 |
| 104 |
| 105 def from_rfc3339(rfc3339_text, with_nanos=False): |
| 106 """Parse a RFC 3339 date string format to datetime.date. |
| 107 |
| 108 Example of accepted format: '1972-01-01T10:00:20.021-05:00' |
| 109 |
| 110 - By default, the result is a datetime.datetime |
| 111 - If with_nanos is true, the result is a 2-tuple, (datetime.datetime, |
| 112 nanos), where the second field represents the possible nanosecond |
| 113 resolution component of the second field. |
| 114 |
| 115 Args: |
| 116 rfc3339_text (string): An rfc3339 formatted date string |
| 117 with_nanos (bool): Determines if nanoseconds should be parsed from the |
| 118 string |
| 119 |
| 120 Raises: |
| 121 ValueError: if ``rfc3339_text`` is invalid |
| 122 |
| 123 Returns: |
| 124 :class:`datetime.datetime`: when with_nanos is False |
| 125 tuple(:class:`datetime.datetime`, int): when with_nanos is True |
| 126 |
| 127 """ |
| 128 timestamp = strict_rfc3339.rfc3339_to_timestamp(rfc3339_text) |
| 129 result = datetime.datetime.utcfromtimestamp(timestamp) |
| 130 if with_nanos: |
| 131 return (result, int((timestamp - int(timestamp)) * 1e9)) |
| 132 else: |
| 133 return result |
OLD | NEW |