Skip to content

Commit

Permalink
Merge pull request #2374 from astralblue/json_encode_non_utc_datetimes
Browse files Browse the repository at this point in the history
Correctly JSON-encode datetimes aware of non-UTC timezones
  • Loading branch information
davidism committed Jun 15, 2017
2 parents 1e112cc + 63ccdad commit d625d41
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ Major release, unreleased
- Removed error handler caching because it caused unexpected results for some
exception inheritance hierarchies. Register handlers explicitly for each
exception if you don't want to traverse the MRO. (`#2362`_)
- Fix incorrect JSON encoding of aware, non-UTC datetimes. (`#2374`_)
- Template auto reloading will honor the ``run`` command's ``debug`` flag even
if ``app.jinja_env`` was already accessed. (`#2373`_)

Expand All @@ -104,6 +105,7 @@ Major release, unreleased
.. _#2354: https://github.com/pallets/flask/pull/2354
.. _#2358: https://github.com/pallets/flask/pull/2358
.. _#2362: https://github.com/pallets/flask/pull/2362
.. _#2374: https://github.com/pallets/flask/pull/2374
.. _#2373: https://github.com/pallets/flask/pull/2373

Version 0.12.2
Expand Down
4 changes: 3 additions & 1 deletion flask/json/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import io
import uuid
from datetime import date
from datetime import date, datetime
from flask.globals import current_app, request
from flask._compat import text_type, PY2

Expand Down Expand Up @@ -62,6 +62,8 @@ def default(self, o):
return list(iterable)
return JSONEncoder.default(self, o)
"""
if isinstance(o, datetime):
return http_date(o.utctimetuple())
if isinstance(o, date):
return http_date(o.timetuple())
if isinstance(o, uuid.UUID):
Expand Down
30 changes: 30 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@ def has_encoding(name):
return False


class FixedOffset(datetime.tzinfo):
"""Fixed offset in hours east from UTC.
This is a slight adaptation of the ``FixedOffset`` example found in
https://docs.python.org/2.7/library/datetime.html.
"""

def __init__(self, hours, name):
self.__offset = datetime.timedelta(hours=hours)
self.__name = name

def utcoffset(self, dt):
return self.__offset

def tzname(self, dt):
return self.__name

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


class TestJSON(object):
def test_ignore_cached_json(self, app):
with app.test_request_context('/', method='POST', data='malformed',
Expand Down Expand Up @@ -177,6 +198,15 @@ def test_jsonify_date_types(self, app, client):
assert rv.mimetype == 'application/json'
assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple())

@pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9)))
def test_jsonify_aware_datetimes(self, tz):
"""Test if aware datetime.datetime objects are converted into GMT."""
tzinfo = FixedOffset(hours=tz[1], name=tz[0])
dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo)
gmt = FixedOffset(hours=0, name='GMT')
expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"')
assert flask.json.JSONEncoder().encode(dt) == expected

def test_jsonify_uuid_types(self, app, client):
"""Test jsonify with uuid.UUID types"""

Expand Down

0 comments on commit d625d41

Please sign in to comment.