47

For some reason, the jsonify function is converting my datetime.date to what appears to be an HTTP date. How can I keep the date in yyyy-mm-dd format when using jsonify?

test_date = datetime.date(2017, 4, 27) print(test_date) # 2017-04-27 test_date_jsonify = jsonify(test_date) print(test_date_jsonify.get_data(as_text=True)) # Thu, 27 Apr 2017 00:00:00 GMT 

As suggested in the comments, using jsonify(str(test_date)) returns the desired format. However, consider the following case:

test_dict = {"name": "name1", "date":datetime.date(2017, 4, 27)} print(test_dict) # {"name": "name1", "date":datetime.date(2017, 4, 27)} test_dict_jsonify = jsonify(test_dict) print(test_dict_jsonify.get_data(as_text=True)) # {"date": "Thu, 27 Apr 2017 00:00:00 GMT", "name": "name1"} test_dict_jsonify = jsonify(str(test_dict)) print(test_dict_jsonify.get_data(as_text=True)) # "{"date": datetime.date(2017, 4, 27), "name": "name1"}" 

In this case, the str() solution does not work.

5
  • Serialize it as a string? Commented Apr 27, 2017 at 16:45
  • I don't really understand what you mean by that. Commented Apr 27, 2017 at 16:50
  • jsonify(str(test_date)) Commented Apr 27, 2017 at 16:51
  • 1
    When you print an object, the object's __str__ method is called, so that's what you see. If that's what you want to serialize and deserialize, call str() on your object. Commented Apr 27, 2017 at 16:53
  • That worked, thank you. However, I think I made the mistake of oversimplifying the case I'm dealing with. Usually when I jsonify a datetime.date, it is part of a dict with other elements, in which case this approach does not work. I have updated my question to better show the case I'm dealing with. Commented Apr 27, 2017 at 17:05

5 Answers 5

64

edit: this answer is now too old for Flask versions 2.3+.

for those newer versions, instead customize json_provider_class; reference: https://flask.palletsprojects.com/en/2.2.x/api/?highlight=json_encoder#flask.Flask.json_provider_class


Following this snippet you can do this:

from flask.json import JSONEncoder from datetime import date class CustomJSONEncoder(JSONEncoder): def default(self, obj): try: if isinstance(obj, date): return obj.isoformat() iterable = iter(obj) except TypeError: pass else: return list(iterable) return JSONEncoder.default(self, obj) app = Flask(__name__) app.json_encoder = CustomJSONEncoder 

Route:

import datetime as dt @app.route('/', methods=['GET']) def index(): now = dt.datetime.now() return jsonify({'now': now}) 
Sign up to request clarification or add additional context in comments.

4 Comments

Could you elaborate on the necessity of the try ... except ... else block compared to the other, simpler answer?
@MaxNoe: On review of the simpler approach, I don't think the extra checking is really necessary since -- as shown there -- simply checking for an instance of date should work. Thanks for commenting.
app.json_encoder is deprecated and will be removed in Flask 2.3.
cannot import JSONEncoder, version Flask-JSON 0.4.0
26

datetime.date is not a JSON type, so it's not serializable by default. Instead, Flask adds a hook to dump the date to a string in RFC 1123 format, which is consistent with dates in other parts of HTTP requests and responses.

Use a custom JSON encoder if you want to change the format. Subclass JSONEncoder and set Flask.json_encoder to it.

from flask import Flask from flask.json import JSONEncoder class MyJSONEncoder(JSONEncoder): def default(self, o): if isinstance(o, date): return o.isoformat() return super().default(o) class MyFlask(Flask): json_encoder = MyJSONEncoder app = MyFlask(__name__) 

It is a good idea to use ISO 8601 to transmit and store the value. It can be parsed unambiguously by JavaScript Date.parse (and other parsers). Choose the output format when you output, not when you store.

A string representing an RFC 2822 or ISO 8601 date (other formats may be used, but results may be unexpected).

When you load the data, there's no way to know the value was meant to be a date instead of a string (since date is not a JSON type), so you don't get a datetime.date back, you get a string. (And if you did get a date, how would it know to return date instead of datetime?)

Comments

18

Flask 2.2 shows a deprecation warning

'JSONEncoder' is deprecated and will be removed in Flask 2.3. Use 'Flask.json' to provide an alternate JSON implementation instead.

An update is needed to remove it and/or have it work in Flask 2.3+. Another example from the Flask repository here

from datetime import datetime, date from flask import Flask from flask.json.provider import DefaultJSONProvider class UpdatedJSONProvider(DefaultJSONProvider): def default(self, o): if isinstance(o, date) or isinstance(o, datetime): return o.isoformat() return super().default(o) app = Flask(__name__) app.json = UpdatedJSONProvider(app) 

1 Comment

Thanks for this @Sobingen. Note that this code requires import datetime and then datetime.date and datetime.datetime instead of date and datetime, and also it's presumably slightly faster to use isinstance(o, (datetime.date, datetime.datetime)) in one call to isinstance.
1

You can change your app's .json_encoder attribute, implementing a variant of JSONEncoder that formats dates as you see fit.

Comments

0

So, if your problem is only with date, time and datetime, this could be solved very easily by using this (code change is minimal this way):

app.json.default = lambda obj: obj.isoformat() if isinstance(obj, (date, time, datetime)) else None 

If you need more control over serialization (e.g., handling custom objects, enums, or special types) or you want to extend Flask’s JSON behavior rather than just override app.json.default or more importantly, you prefer structuring your code for reusability in larger applications, you can use a CustomJSONProvider to override the current default provider.

from datetime import date, datetime, time from flask.json.provider import DefaultJSONProvider class CustomJSONProvider(DefaultJSONProvider): def default(self, obj): try: if isinstance(obj, (date, time, datetime)): return obj.isoformat() iterable = iter(obj) except TypeError: pass else: return list(iterable) return super().default(obj) app = Flask(__name__) app.json_provider_class = CustomJSONProvider app.json = CustomJSONProvider(app) 

Hope this is helpful! Found that the JSONEncoder is deprecated after Flask 2.3 and finally found this solution phew.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.