Update: Thanks to @maiksprenger for pointing out an alternative to convert to DateTime.

I'm currently trying to deserialize JSON data to a Django model. This is quite straight forward for most numeric fields, but I have a DateTime field which is stored as a timestamp in the original JSON.

Using the DateTimeField

At first, I thought I should read the timestamp as an integer and convert it through post-processing. Then, after reading more of the DRF docs, it became evident I needed a custom field. The code is below:

from rest_framework import serializers
from datetime import datetime


class TimestampField(serializers.DateTimeField):
    """
    Convert a django datetime to/from timestamp.
    """
    def to_representation(self, value):
        """
        Convert the field to its internal representation (aka timestamp)
        :param value: the DateTime value
        :return: a UTC timestamp integer
        """
        result = super(TimestampField, self).to_representation(value)
        return result.timestamp()

    def to_internal_value(self, value):
        """
        deserialize a timestamp to a DateTime value
        :param value: the timestamp value
        :return: a django DateTime value
        """
        converted = datetime.fromtimestamp(float('%s' % value))
        return super(TimestampField, self).to_representation(converted)

The idea is that to_representation converts a DateTime field into a timestamp value for serialisation and to_internal_value does exactly the opposite:

  • to_representation: just gets the timestamp() value out of the DateTime field

  • to_internal_value: converts the timestamp value to a DateTime object via:

    datetime.fromtimestamp(float('%s' % value))
    

Using FloatField serializer

Maik Sprenger pointed out a faster way, by serialising to/from a FloatField:

from rest_framework import serializers
from datetime import datetime


class UnixTimestampField(serializers.FloatField):
    """
    Stores Django datetimes as a Unix timestamp.
    
    Code by Maik Sprenger
    https://gist.github.com/maiksprenger/1b67662c489d7b710242190de9a992ff
    """

    def to_internal_value(self, value):
        """
        Convert a float of a Unix timestamp to an aware datetime
        """
        # Convert given value to float, or fail
        timestamp = super().to_internal_value(value)

        # Convert to aware datetime

        # If a timezone is given, fromtimestamp will always create a datetime for the
        # same point in time. The only difference is the associated timezone.
        # But that is slightly surprising, so to make it least surprising, we're
        # returning an aware datetime based on UTC.
        return datetime.fromtimestamp(timestamp, tz=timezone.utc)

    def to_representation(self, value):
        """
        Convert a datetime to a float of an Unix timestamp
        Any timezone information is handled correctly by timestamp()
        https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
        """
        return value.timestamp()

This is all!

HTH,