Skip to content

Commit 0cdef1d

Browse files
akaIDIOTperlpunk
authored andcommitted
Create timezone-aware datetimes when parsed as such (#163)
* 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.
1 parent 7f977b5 commit 0cdef1d

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

lib/yaml/constructor.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,29 @@
1818
class ConstructorError(MarkedYAMLError):
1919
pass
2020

21+
22+
class timezone(datetime.tzinfo):
23+
def __init__(self, offset):
24+
self._offset = offset
25+
seconds = abs(offset).total_seconds()
26+
self._name = '%s%02d:%02d' % (
27+
'-' if offset.days < 0 else '+',
28+
seconds // 3600,
29+
seconds % 3600 // 60
30+
)
31+
32+
def tzname(self, dt=None):
33+
return self._name
34+
35+
def utcoffset(self, dt=None):
36+
return self._offset
37+
38+
def dst(self, dt=None):
39+
return datetime.timedelta(0)
40+
41+
__repr__ = __str__ = tzname
42+
43+
2144
class BaseConstructor(object):
2245

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

298321
timestamp_regexp = re.compile(
299322
ur'''^(?P<year>[0-9][0-9][0-9][0-9])
@@ -320,22 +343,23 @@ def construct_yaml_timestamp(self, node):
320343
minute = int(values['minute'])
321344
second = int(values['second'])
322345
fraction = 0
346+
tzinfo = None
323347
if values['fraction']:
324348
fraction = values['fraction'][:6]
325349
while len(fraction) < 6:
326350
fraction += '0'
327351
fraction = int(fraction)
328-
delta = None
329352
if values['tz_sign']:
330353
tz_hour = int(values['tz_hour'])
331354
tz_minute = int(values['tz_minute'] or 0)
332355
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
333356
if values['tz_sign'] == '-':
334357
delta = -delta
335-
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
336-
if delta:
337-
data -= delta
338-
return data
358+
tzinfo = timezone(delta)
359+
elif values['tz']:
360+
tzinfo = timezone(datetime.timedelta(0))
361+
return datetime.datetime(year, month, day, hour, minute, second, fraction,
362+
tzinfo=tzinfo)
339363

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

lib3/yaml/constructor.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,22 +324,23 @@ def construct_yaml_timestamp(self, node):
324324
minute = int(values['minute'])
325325
second = int(values['second'])
326326
fraction = 0
327+
tzinfo = None
327328
if values['fraction']:
328329
fraction = values['fraction'][:6]
329330
while len(fraction) < 6:
330331
fraction += '0'
331332
fraction = int(fraction)
332-
delta = None
333333
if values['tz_sign']:
334334
tz_hour = int(values['tz_hour'])
335335
tz_minute = int(values['tz_minute'] or 0)
336336
delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute)
337337
if values['tz_sign'] == '-':
338338
delta = -delta
339-
data = datetime.datetime(year, month, day, hour, minute, second, fraction)
340-
if delta:
341-
data -= delta
342-
return data
339+
tzinfo = datetime.timezone(delta)
340+
elif values['tz']:
341+
tzinfo = datetime.timezone.utc
342+
return datetime.datetime(year, month, day, hour, minute, second, fraction,
343+
tzinfo=tzinfo)
343344

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

0 commit comments

Comments
 (0)