1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
import numbers
import time
from iso8601 import iso8601
import six
from oslo_utils import timeutils
class Formatter(object):
@classmethod
def serialize(cls, value):
"""Return a string representing the formatted value"""
raise NotImplementedError
@classmethod
def deserialize(cls, value):
"""Return a formatted object representing the value"""
raise NotImplementedError
class ISO8601(Formatter):
@classmethod
def serialize(cls, value):
"""Convert a datetime to an ISO8601 string"""
if isinstance(value, datetime):
return value.isoformat()
elif isinstance(value, six.string_types):
# If we're already given a string, keep it as-is.
# This happens when a string comes back in a response body,
# as opposed to the datetime case above which happens when
# a user is setting a datetime for a request.
return value
else:
raise ValueError("Unable to serialize ISO8601: %s" % value)
@classmethod
def deserialize(cls, value):
"""Convert an ISO8601 string to a datetime object"""
if isinstance(value, six.string_types):
return timeutils.parse_isotime(value)
else:
raise ValueError("Unable to deserialize ISO8601: %s" % value)
class UNIXEpoch(Formatter):
EPOCH = datetime(1970, 1, 1, tzinfo=iso8601.UTC)
@classmethod
def serialize(cls, value):
"""Convert a datetime to a UNIX epoch"""
if isinstance(value, datetime):
# Do not try to format using %s as it's platform dependent.
return (value - cls.EPOCH).total_seconds()
elif isinstance(value, numbers.Number):
return value
else:
raise ValueError("Unable to serialize UNIX epoch: %s" % value)
@classmethod
def deserialize(cls, value):
"""Convert a UNIX epoch into a datetime object"""
try:
value = float(value)
except ValueError:
raise ValueError("Unable to deserialize UNIX epoch: %s" % value)
# gmtime doesn't respect microseconds so we need to parse them out
# if they're there and build the datetime appropriately with the
# proper precision.
# NOTES:
# 1. datetime.fromtimestamp sort of solves this but using localtime
# instead of UTC, which we need.
# 2. On Python 2 we can't just str(value) as it truncates digits
# that are significant to us.
parsed_value = "%000000f" % value
decimal = parsed_value.find(".")
if decimal == -1:
microsecond = 0
else:
# Some examples of these timestamps include less precision
# than the allowable 6 digits that can represent microseconds,
# so since we have a string we need to construct a real
# count of microseconds instead of just converting the
# stringified amount to an int.
fractional_second = float(parsed_value[decimal:]) * 1e6
microsecond = int(fractional_second)
gmt = time.gmtime(value)
return datetime(*gmt[:6], microsecond=microsecond,
tzinfo=iso8601.UTC)
class BoolStr(Formatter):
# The behavior here primarily exists for the deserialize method
# to be producing Python booleans.
@classmethod
def deserialize(cls, value):
return cls.convert(value)
@classmethod
def serialize(cls, value):
return cls.convert(value)
@classmethod
def convert(cls, value):
expr = str(value).lower()
if "true" == expr:
return True
elif "false" == expr:
return False
else:
raise ValueError("Unable to convert as boolean: %s" % value)
|