Python: get datetime for '3 years ago today'
Asked Answered
B

7

108

In Python, how do I get a datetime object for '3 years ago today'?

UPDATE: FWIW, I don't care hugely about accuracy... i.e. it's Feb 29th today, I don't care whether I'm given Feb 28th or March 1st in my answer. Concision is more important than configurability, in this case.

Blasphemous answered 1/3, 2011 at 17:37 Comment(6)
possible duplicate of How to create a DateTime equal to 15 minutes ago?Launder
Presumably if it's March 1 today, you want to get March 1 no matter if a leap year occurs in between or not? I think all the existing answers fail in that regard.Degrading
Almost, but not quite: the difference between 15 minutes, which is invariable, and 3 years, which is not, is significant.Chiekochien
@Jason I agree with you, although from the OP's edit he doesn't seem super concerned about that, so that being the case it would be a duplicate.Template
This site really needs a way for the community to override when people accept a clearly incorrect answer. 3*365 days is not 3 years, and there's a correct answer right there.Kapp
@Glenn Maynard - You could edit the title. Since he really didn't want 3 years ago exactly.Uncial
T
133
import datetime
datetime.datetime.now() - datetime.timedelta(days=3*365)
Telephony answered 1/3, 2011 at 17:45 Comment(6)
AP257 said: "I don't care hugely about accuracy". I interpreted as "I don't care about leap years"Ebenezer
Is it correct to give a less concise but more accurate answer when the asker explicitly told the opposite?Ebenezer
+1 For simplicity. Maybe the OP just wanted an example of how to do something like this in general.Uncial
Thanks for simplicity. I also don't care about accuracy. I was actually looking for a difference of days anyway.Floater
The example given for "I don't care hugely about accuracy" suggests that the questioner didn't actually think about things enough. The example is of not caring whether February 29 becomes February 28 or March 1, but this code can (and usually will) do stuff like turn March 5 into March 6. Someone who doesn't care about the first thing will probably still care about the second.Northcliffe
You can deal precisely with leap years by instead multiplying by 365.2422, if you care specifically about where the earth is in its orbit.Sulphathiazole
O
207

If you need to be exact use the dateutil module to calculate relative dates

from datetime import datetime
from dateutil.relativedelta import relativedelta

three_yrs_ago = datetime.now() - relativedelta(years=3)
Oribella answered 1/3, 2011 at 19:5 Comment(1)
How do I do this for just the Date?Gog
T
133
import datetime
datetime.datetime.now() - datetime.timedelta(days=3*365)
Telephony answered 1/3, 2011 at 17:45 Comment(6)
AP257 said: "I don't care hugely about accuracy". I interpreted as "I don't care about leap years"Ebenezer
Is it correct to give a less concise but more accurate answer when the asker explicitly told the opposite?Ebenezer
+1 For simplicity. Maybe the OP just wanted an example of how to do something like this in general.Uncial
Thanks for simplicity. I also don't care about accuracy. I was actually looking for a difference of days anyway.Floater
The example given for "I don't care hugely about accuracy" suggests that the questioner didn't actually think about things enough. The example is of not caring whether February 29 becomes February 28 or March 1, but this code can (and usually will) do stuff like turn March 5 into March 6. Someone who doesn't care about the first thing will probably still care about the second.Northcliffe
You can deal precisely with leap years by instead multiplying by 365.2422, if you care specifically about where the earth is in its orbit.Sulphathiazole
K
32

Subtracting 365*3 days is wrong, of course--you're crossing a leap year more than half the time.

dt = datetime.now()
dt = dt.replace(year=dt.year-3)
# datetime.datetime(2008, 3, 1, 13, 2, 36, 274276)

ED: To get the leap-year issue right,

def subtract_years(dt, years):
    try:
        dt = dt.replace(year=dt.year-years)
    except ValueError:
        dt = dt.replace(year=dt.year-years, day=dt.day-1)
    return dt
Kapp answered 1/3, 2011 at 18:0 Comment(4)
Well, now you have that other issue: datetime.datetime(2008,2,29).replace(year=2005) -> ValueError. It is still more accurate to catch that error and just subtract one extra day I guess.Ormolu
I keep forgetting about replace. It makes for a simpler solution than mine.Degrading
@Mark: I did at first, too; I initially did what you did. The site seems to have misplaced that version in the edit history, though.Kapp
What happens after 2100?Avignon
D
4
def add_years(dt, years):
    try:
        result = datetime.datetime(dt.year + years, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
    except ValueError:
        result = datetime.datetime(dt.year + years, dt.month, dt.day - 1, dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
    return result

>>> add_years(datetime.datetime.now(), -3)
datetime.datetime(2008, 3, 1, 12, 2, 35, 22000)
>>> add_years(datetime.datetime(2008, 2, 29), -3)
datetime.datetime(2005, 2, 28, 0, 0)
Degrading answered 1/3, 2011 at 18:4 Comment(0)
D
1

This works to cater for leap year corner cases and non-leap years too. Because, if day = 29 and month = 2 (Feb), a non-leap year would throw a value error because there is no 29th Feb and the last day of Feb would be 28th, thus doing a -1 on the date works in a try-except block.

from datetime import datetime

last_year = datetime.today().year - 1
month = datetime.today().month
day = datetime.today().day

try:
    # try returning same date last year
    last_year_date = datetime.strptime(f"{last_year}-{month}-{day}",'%Y-%m-%d').date()
except ValueError: 
    # incase of error due to leap year, return date - 1 in last year
    last_year_date = datetime.strptime(f"{last_year}-{month}-{day-1}",'%Y-%m-%d').date()

print(last_year_date)
Draughtboard answered 20/1, 2022 at 2:22 Comment(0)
T
-1
In [3]: import datetime as dt

In [4]: today=dt.date.today()

In [5]: three_years_ago=today-dt.timedelta(days=3*365)

In [6]: three_years_ago
Out[6]: datetime.date(2008, 3, 1)
Trilateration answered 1/3, 2011 at 17:44 Comment(1)
It does not consider leap years.Tishtisha
C
-1

I was looking for a solution using only the standard library, and this worked for me. Note that without the check, Feb. 29th will give you a ValueError for most years. On all other days, it will give you the date for "three years ago today".

today = date.today()
day = 28 if today.month == 2 and today.day == 29 else today.day
three_years_ago = date(today.year - 3, today.month, day)
Candiscandle answered 20/12, 2022 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.