52

With inspectdb I was able to get a "interval" field from postgres into django. In Django, it was a TextField. The object that I retrieved was indeed a timedelta object!

Now I want to put this timedelta object in a new model. What's the best way to do this? Because putting a timedelta in a TextField results in the str version of the object...

Braiam
  • 1
  • 11
  • 47
  • 78
Jack Ha
  • 19,661
  • 11
  • 37
  • 41

8 Answers8

110

Since Django 1.8 you can use DurationField.

SukiCZ
  • 300
  • 5
  • 10
Marc Tudurí
  • 1,869
  • 3
  • 18
  • 22
  • 4
    SO on how to use: https://stackoverflow.com/questions/30222660/how-should-i-use-durationfield-in-my-model – Csaba Toth Nov 18 '19 at 21:37
32

You can trivially normalize a timedelta to a single floating-point number in days or seconds.

Here's the "Normalize to Days" version.

float(timedelta.days) + float(timedelta.seconds) / float(86400)

You can trivially turn a floating-point number into a timedelta.

>>> datetime.timedelta(2.5)
datetime.timedelta(2, 43200)

So, store your timedelta as a float.

Here's the "Normalize to Seconds" version.

timedelta.days*86400+timedelta.seconds

Here's the reverse (using seconds)

datetime.timedelta( someSeconds/86400 )
Johndt
  • 4,187
  • 1
  • 23
  • 29
S.Lott
  • 384,516
  • 81
  • 508
  • 779
8

First, define your model:

class TimeModel(models.Model):
    time = models.FloatField()

To store a timedelta object:

# td is a timedelta object
TimeModel.objects.create(time=td.total_seconds())

To get the timedelta object out of the database:

# Assume the previously created TimeModel object has an id of 1
td = timedelta(seconds=TimeModel.objects.get(id=1).time)

Note: I'm using Python 2.7 for this example.

Garrett Hyde
  • 5,409
  • 8
  • 49
  • 55
  • 1
    on python with version lower than 2.7 can be used formula: `total_seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6` – Pol Jun 21 '12 at 14:40
  • This is great. Thank you for being both concise and thorough – R Claven Mar 27 '15 at 17:02
7

https://bitbucket.org/schinckel/django-timedelta-field/src

Python
  • 79
  • 1
  • 1
4

There is a ticket which dates back to July 2006 relating to this: https://code.djangoproject.com/ticket/2443

Several patches were written but the one that was turned in to a project: https://github.com/johnpaulett/django-durationfield

Compared to all the other answers here this project is mature and would have been merged to core except that its inclusion is currently considered to be "bloaty".

Personally, I've just tried a bunch of solutions and this is the one that works beautifully.

from django.db import models
from durationfield.db.models.fields.duration import DurationField

class Event(models.Model):
    start = models.DateTimeField()
    duration = DurationField()

    @property
    def finish(self):
        return self.start + self.duration

Result:

$ evt = Event.objects.create(start=datetime.datetime.now(), duration='1 week')
$ evt.finish
Out[]: datetime.datetime(2013, 6, 13, 5, 29, 29, 404753)

And in admin:

Change event

Duration: 7 days, 0:00:00

Williams
  • 4,044
  • 1
  • 37
  • 53
  • 1
    It looks like in Django 1.8, that ticket will finally be closed and Django will have a `DurationField`: https://docs.djangoproject.com/en/dev/releases/1.8/#new-data-types – nnyby Dec 31 '14 at 18:46
3

For PostgreSQL, use django-pgsql-interval-field here: http://code.google.com/p/django-pgsql-interval-field/

dotz
  • 884
  • 1
  • 8
  • 17
2

Putting this out there cause it might be another way to solve this problem. first install this library: https://pypi.python.org/pypi/django-timedeltafield

Then:

import timedelta

class ModelWithTimeDelta(models.Model):
    timedeltafield = timedelta.fields.TimedeltaField()

within the admin you will be asked to enter data into the field with the following format: 3 days, 4 hours, 2 minutes

colins44
  • 544
  • 6
  • 9
0

There is a workaround explained here. If you're using Postgresql, then multiplying the result of F expression with timedelta solves the problem. For example if you have a start_time and a duration in minutes, you can calculate the end_time like this:

YourModel.objects.annotate(
    end_time=ExpressionWrapper(F('start_time') + timedelta(minutes=1) * F('duration'), output_field=DateTimeField())
)
therealak12
  • 1,178
  • 2
  • 12
  • 26