69

How can I subtract or add 100 years to a datetime field in the database in Django?

The date is in database, I just want to directly update the field without retrieving it out to calculate and then insert.

kelvinfix
  • 2,915
  • 8
  • 37
  • 49
  • 1
    What do you mean? Do you want to modify all dates in the database by 100 years? Do you want to write a query that compares the date + 100 years? Do you just want to output dates with 100 years added? What? – Daniel Roseman May 03 '11 at 14:39
  • 1
    Write something like queryset.update(end_time=end_time + 1) – kelvinfix May 03 '11 at 14:50

6 Answers6

116

I would use the relativedelta function of the dateutil.relativedelta package, which will give you are more accurate 'n-years ago' calculation:

from dateutil.relativedelta import relativedelta
import datetime

years_ago = datetime.datetime.now() - relativedelta(years=5)

Then simply update the date field as others have shown here.

Daniel Holmes
  • 1,952
  • 2
  • 17
  • 28
Max
  • 6,901
  • 7
  • 46
  • 61
  • 1
    I've never even heard of this package (yet it's installed on my system). I don't recall installing it, so any idea if it's somehow included with another package? – Gerrat Sep 06 '11 at 14:28
  • It's part of the python-dateutil – Max Sep 06 '11 at 14:42
  • 1
    That's the package I don't recall installing. Found my answer though - it's installed alongside Matplotlib if not already installed. – Gerrat Sep 06 '11 at 14:51
  • 3
    I've done significant testing on this today. timedelta(days=365*100) is flat wrong. timedelta(days=365.25*100) has a few outliers that don't work. timedelta(days=365.24*100) is closer but I found a couple of exceptions. relativedelta(years=100) works every single time. Now the stupid '%m%d%y' date format storage I have to deal with will come out correct every time (well...at least if they're talking about dates greater than 1917. Hard to tell 2016 from 1916 with no other context. But 2018 hasn't happened yet) So yay! – Alan Leuthard Aug 04 '17 at 16:15
  • 1
    See https://stackoverflow.com/questions/12433233/what-is-the-difference-between-datetime-timedelta-and-dateutil-relativedelta for more details on relativedelta – mic Aug 26 '19 at 03:25
20

Use timedelta. Something like this should do the trick:

import datetime
years = 100
days_per_year = 365.24
hundred_years_later = my_object.date + datetime.timedelta(days=(years*days_per_year))
Vladimir Prudnikov
  • 6,974
  • 4
  • 48
  • 57
6

The .update() method on a Django query set allows you update all values without retrieving the object from the database. You can refer to the existing value using an F() object.

Unfortunately Python's timedelta doesn't work with years, so you'll have to work out 100 years expressed in days (it's 36524.25):

MyModel.objects.update(timestamp=F('timestamp')+timedelta(days=36524.25))
Will Hardy
  • 14,588
  • 5
  • 44
  • 43
  • Just for reference using F() objects with timedelta's like this is only available in Django 1.3 or later – John Montgomery May 03 '11 at 15:25
  • This ignores leap days like 2012-02-29, better use `relativedate()` instead of `timedelta()` if you need that accuracy – vdboor Feb 29 '12 at 09:47
  • I'd be very surprised if that worked with `F()` objects. The .2425 in the number 365.2425 is the bit that tries to take leap years into account, as much as you can with `F()` objects. – Will Hardy Mar 01 '12 at 13:39
2

Though setting the number of days in a year as 365.25 (from (365+365+365+366)/4) perfectly offsets the difference-in-days error, it would sometimes lead to unwanted results as you might cause undesirable changes in attributes other than year, especially when you are adding/subtracting 1 or a few years.

If you want to just change the year while preventing changes in other datetime's attributes, just do the algebra on the year attribute like the following:

from datetime import datetime 

d = my_obj.my_datetime_field

""" subtract 100 years. """
my_obj.my_datetime_field = datetime(d.year-100, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, d.tzinfo)

my_obj.save()

Hope it helps!

xemexpress
  • 177
  • 1
  • 13
  • 3
    While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Brian61354270 Apr 19 '20 at 17:41
2

Subtract year from today and use this format. x = datetime.datetime(2020 - 100, 5, 17)

import datetime
datetime.date(datetime.date.today().year - 100, datetime.date.today().month, datetime.date.today().day)
Pravin L
  • 31
  • 1
  • 5
1

I Know it's an old question, but I had the problem to find out a good one to solve my problem, I have created this: Use plus(+) or minus(-) to handle with:

import datetime # Don't forget to import it

def subadd_date(date,years):
    ''' Subtract or add Years to a specific date by pre add  + or - '''
    if isinstance(date,datetime.datetime) and isinstance(years,int):
        day,month,year = date.day , date.month , date.year
        #If you want to have HOUR, MINUTE, SECOND 
        #With TIME: 
        # day,month,year,hour,minute,second = date.day, date.month,date.year,date.hour,date.minute,date.second  

        py = year + years # The Past / Futur Year
        new_date_str = "%s-%s-%s" % (day,month,py) # New Complete Date
        # With TIME : new_date_str = "%s-%s-%s %s:%s:%s" % (month,day,py,hour,minute,second)
        try:
            new_date = datetime.datetime.strptime(new_date_str,"%d-%m-%Y")
        except ValueError: # day is out of range for month (February 29th)
            new_date_str = "%s-%s-%s" % (1,month+1,py) # New Complete Date : March 1st
            new_date = datetime.datetime.strptime(new_date_str,"%d-%m-%Y")

        return new_date
        # With TIME : return datetime.datetime.strptime(new_date_str,"%d-%m-%Y %H:%M:%Y")
    return None
Lemayzeur
  • 8,297
  • 3
  • 23
  • 50