Reputation: 6421
I have a Build
model with a start_time
field, which is of type models.DateTimeField
. On the other hand I have a BuildSerializer
class that includes this start_time
field. Now when I print the timestamp in my templates, I get a result like this:
{{ build.start_time|format:'DATETIME_FORMAT' }}
in my template will become:
April 24, 2015, 8:03 a.m.
However, the serializer outputs a different value:
2015-04-24T08:03:39.336922Z
This is fine as it is a standard JSON date representation. However, I’d like to display it in regard to the user’s locale, like Django does when I use the above template snippet does.
I tried to use
timestamp = serializers.DateTimeField(format=settings.DATETIME_FORMAT)
but DRF uses a different way to format date outputs, so this will just output:
"N j, Y, P"
for the default en-us
locale.
Is there a way to format a date exactly like Django does? I’m interested in both a python or a JavaScript solution, as I process the JSON in a JS function (AJAX callback) anyway.
Upvotes: 2
Views: 4421
Reputation: 1
Here is my solution, based on Kevin Brown's answer:
from django.utils import formats
from rest_framework import serializers
from rest_framework.settings import api_settings
class DateTimeField(serializers.DateTimeField):
"""DateTime field whichs supports Django's format definitions."""
def to_representation(self, obj):
if api_settings.DATETIME_FORMAT in formats.FORMAT_SETTINGS:
return formats.date_format(obj, api_settings.DATETIME_FORMAT)
return super().to_representation(obj)
class DateField(serializers.DateField):
"""Date field whichs supports Django's format definitions."""
def to_representation(self, obj):
if api_settings.DATE_FORMAT in formats.FORMAT_SETTINGS:
return formats.date_format(obj, api_settings.DATE_FORMAT)
return super().to_representation(obj)
class TimeField(serializers.TimeField):
"""Time field whichs supports Django's format definitions."""
def to_representation(self, obj):
if api_settings.TIME_FORMAT in formats.FORMAT_SETTINGS:
return formats.date_format(obj, api_settings.TIME_FORMAT)
return super().to_representation(obj)
Upvotes: 0
Reputation: 41691
I would highly recommend keeping your dates formatted as ISO 8601 (that's what the default is) as it is a consistent and standardized date format, so you don't need to worry too much about parsing dates manually anywhere. Most languages can parse ISO 8601 dates, and those that can't by default generally have libraries which can do it for you.
Is there a way to format a date exactly like Django does?
Django REST framework by default only supports formatting a date-related field using a single formatter, which is ISO 8601 by default. This is for general consistency, so clients can send an OPTIONS
request and parse the date format directly from there and rely on the date format never changing.
You can override this behaviour by either having a property on your model or a SerializerMethodField
that overrides the return value such that it calls Django's date formatting utilities if you want to localize the value on the backend. Alternatively, you can override the default DateTimeField
's to_native
method and do the formatting there, that way you can have a field that can be used across serializers so the localization is consistent.
I’m interested in both a python or a JavaScript solution, as I process the JSON in a JS function (AJAX callback) anyway.
The JavaScript solution that we use for parsing ISO 8601 times is Moment.js. While some browsers can parse ISO 8601 times, not all can and it is easier to pass them off to Moment so they can be converted in JavaScript Date objects.
// parse the incoming date/time string
var start = moment(response.start_time, moment.ISO_8601);
The first argument to moment
is the string to be parsed, and the second argument is the format to parse from. moment.ISO_8601
is a special hook that can automatically parse it as a valid ISO 8601 time, which is why we use it.
From there you can use the browser's Date localization to present the user a localized version of the time. Date objects provide toLocaleString()
, toLocaleDateString()
and toLocaleTimeString()
methods that can be used to format the different parts of the date (or the entire thing) how the user is expecting it.
Testing locally on Chrome (on Ubuntu, en-US) I get the following outputs for the methods
> (new Date()).toLocaleString()
< "6/2/2015, 7:23:03 PM"
> (new Date()).toLocaleDateString()
< "6/2/2015"
> (new Date()).toLocaleTimeString()
< "7:23:12 PM"
Which is how I've configured my date settings for this browser. This has the additional bonus of always using the current time zone for the system, so you don't need to worry about that part of localization - it's handled automatically.
Upvotes: 2
Reputation: 6421
I have came to a solution pretty fast, although I consider it a bit ugly:
from django.utils import formats
class BuildSerializer(serializers.ModelSerializer):
timestamp_django = serializers.SerializerMethodField()
def get_timestamp_django(self, obj):
return formats.date_format(obj.timestamp, 'DATETIME_FORMAT')
class Meta:
model = Build
fields = ('timestamp', 'timestamp_django', …)
This way I get the best of both worlds, as I can still use the JSON date for other purposes (like filtering or sorting).
Upvotes: 0