Reputation: 734
I've been searching through stackoverflow for the answer but I haven't been able to find what I'm looking for in Python and in a Pythonic way.
I'm trying to get the number of days, weeks or months ahead based on two dates. Here is a little script I created which does what I want to do but I'm concerned about it.
import datetime
from dateutil.relativedelta import relativedelta
now = datetime.datetime.now()
days_ahead = datetime.datetime.now() + relativedelta(days=3)
weeks_ahead = datetime.datetime.now() + relativedelta(weeks=2)
month_ahead = datetime.datetime.now() + relativedelta(months=1)
months_ahead = datetime.datetime.now() + relativedelta(months=3)
def get_relative_date(dt):
ahead = (dt - now).days
if ahead < 7:
return "Due in " + str(ahead) + " days"
elif ahead < 31:
return "Due in " + str(ahead/7) + " weeks"
else:
return "Due in " + str(ahead/30) + " months"
print get_relative_date(days_ahead)
print get_relative_date(weeks_ahead)
print get_relative_date(month_ahead)
print get_relative_date(months_ahead)
The result is the following:
Due in 3 days
Due in 2 weeks
Due in 1 months
Due in 3 months
Despite being a good answer my concerns are related to:
ahead < 30
but what about months with 31 days? Won't this cause some kind of overhead and give errors at some point?Thanks in advance. If the question was answered please link me to the post and I'll read it carefully. I'm willing to give more information if required.
I include here my complete updated code for anyone who required too this functionality in Python. It also takes care of negative day values and Today.
def relative_date(dt):
if dt is not None and len(dt) > 0:
now = datetime.now()
then = arrow.get(dt).naive
rd = relativedelta(then, now)
if rd.years or rd.months:
months = 12 * rd.years + rd.months
if months < 0:
if months == -1:
return "Due 1 month ago"
return "Due %i months ago" % -months
if months == 1:
return "Due in 1 month"
return "Due in %d months" % months
elif rd.days > 7 or rd.days < -7:
weeks = rd.days / 7
if weeks < 0:
if weeks == -1:
return "Due 1 week ago"
return "Due %i weeks ago" % -weeks
if weeks == 1:
return "Due in 1 week"
return "Due in %d weeks" % weeks
else:
if rd.days == 0:
return "Due Today"
elif rd.days < 0:
if rd.days == -1:
return "Due 1 day ago"
return "Due %i days ago" % -rd.days
elif rd.days == 1:
return "Due in 1 day"
return "Due in %d days" % rd.days
else:
return ""
Upvotes: 4
Views: 3384
Reputation: 308899
Yes, your current code an issue because not all months have 31 days. In practice, you might decide that it's not too important if it says "Due in 2 months" when it's actually due in 1 month and 28 days. After all, the rounding down means you are showing "Due in 2 months" when it's due in 2 months and 28 days.
Since you are using the dateutil
module already, note that you can use relativedelta
the other way around as well (see the examples page).
If you instantiate a relativedelta
with two date objects, it returns a relativedelta
object with year
, month
and day
attributes.
>>> relativedelta(date(2015, 7, 20), date(2014, 6, 10))
relativedelta(years=+1, months=+1, days=+10)
You could use this in your method as follows:
from dateutil.relativedelta import relativedelta
def get_relative_date(dt):
rd = relativedelta(dt, now)
if rd.years or rd.months:
months = 12 * rd.years + rd.months
return "Due in %d months" % months
elif rd.days > 7:
weeks = rd.days / 7
return "Due in %d weeks" % weeks
else:
return "Due in %d days" % rd.days
Upvotes: 6