Skip to content

Commit

Permalink
Create timezone-aware datetimes when parsed as such (#163)
Browse files Browse the repository at this point in the history
* On load, now use aware datetimes if possible

On loading data, if timestamps have an ISO "+HH:MM" UTC offset then the resultant datetime is converted to UTC.  This change adds that timezone information to the datetime objects.

Importantly, this addresses a Django warning (and potential error) that appears when using both YAML fixtures in a timezone-aware project.  It was raised as a Django issue (https://code.djangoproject.com/ticket/18867), but subsequently closed because the Django devs felt that this is a PyYAML problem.

* Create timezone-aware datetime in timezone from data

* Create timezone-aware datetime in timezone from data for python2

* Define better timezone implementation for python2

* Handle timezone "Z" for python 3

* Handle timezone "Z" for python 2

* Fix code structure for Python 3

Call datetime.datetime constructor once at return.

* Fix code structure for Python 2

Call datetime.datetime constructor once at return.
  • Loading branch information
akaIDIOT authored and perlpunk committed Dec 15, 2019
1 parent 7f977b5 commit 0cdef1d
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 11 deletions.
36 changes: 30 additions & 6 deletions lib/yaml/constructor.py
Expand Up @@ -18,6 +18,29 @@
class ConstructorError(MarkedYAMLError):
pass


class timezone(datetime.tzinfo):
def __init__(self, offset):
self._offset = offset
seconds = abs(offset).total_seconds()
self._name = '%s%02d:%02d' % (
'-' if offset.days < 0 else '+',
seconds // 3600,
seconds % 3600 // 60
)

def tzname(self, dt=None):
return self._name

def utcoffset(self, dt=None):
return self._offset

def dst(self, dt=None):
return datetime.timedelta(0)

__repr__ = __str__ = tzname


class BaseConstructor(object):

yaml_constructors = {}
Expand Down Expand Up @@ -293,7 +316,7 @@ def construct_yaml_binary(self, node):
return str(value).decode('base64')
except (binascii.Error, UnicodeEncodeError), exc:
raise ConstructorError(None, None,
"failed to decode base64 data: %s" % exc, node.start_mark)
"failed to decode base64 data: %s" % exc, node.start_mark)

timestamp_regexp = re.compile(
ur'''^(?P<year>[0-9][0-9][0-9][0-9])
Expand All @@ -320,22 +343,23 @@ def construct_yaml_timestamp(self, node):
minute = int(values['minute'])
second = int(values['second'])
fraction = 0
tzinfo = None
if values['fraction']:
fraction = values['fraction'][:6]
while len(fraction) < 6:
fraction += '0'
fraction = int(fraction)
delta = None
if values['tz_sign']:
tz_hour = int(values['tz_hour'])
tz_minute = int(values['tz_minute'] or 0)
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
if delta:
data -= delta
return data
tzinfo = timezone(delta)
elif values['tz']:
tzinfo = timezone(datetime.timedelta(0))
return datetime.datetime(year, month, day, hour, minute, second, fraction,
tzinfo=tzinfo)

def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
Expand Down
11 changes: 6 additions & 5 deletions lib3/yaml/constructor.py
Expand Up @@ -324,22 +324,23 @@ def construct_yaml_timestamp(self, node):
minute = int(values['minute'])
second = int(values['second'])
fraction = 0
tzinfo = None
if values['fraction']:
fraction = values['fraction'][:6]
while len(fraction) < 6:
fraction += '0'
fraction = int(fraction)
delta = None
if values['tz_sign']:
tz_hour = int(values['tz_hour'])
tz_minute = int(values['tz_minute'] or 0)
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
if values['tz_sign'] == '-':
delta = -delta
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
if delta:
data -= delta
return data
tzinfo = datetime.timezone(delta)
elif values['tz']:
tzinfo = datetime.timezone.utc
return datetime.datetime(year, month, day, hour, minute, second, fraction,
tzinfo=tzinfo)

def construct_yaml_omap(self, node):
# Note: we do not check for duplicate keys, because it's too
Expand Down

0 comments on commit 0cdef1d

Please sign in to comment.