简体   繁体   中英

Python get days/weeks/months ahead

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:

  • I'm using ahead < 30 but what about months with 31 days? Won't this cause some kind of overhead and give errors at some point?
  • Is there a better way to get this information? Some kind of library or built-in function for datetime or dateutil which returns you this information?

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.

Edit

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 ""

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM