Python Smart date parsing without a year specified
Asked Answered
T

2

6

How can I parse dates (with dateutil) without a year so that when the current date is 17/11/2012 these dates will be parsed like this:

print parser.parse("23 nov", dayfirst=True, yearfirst=False, fuzzy=True)
# 23/11/2012
print parser.parse("28 dec", dayfirst=True, yearfirst=False, fuzzy=True)
# 28/12/2012
print parser.parse("3 jan", dayfirst=True, yearfirst=False, fuzzy=True)
# 3/01/2013

What I want is that already passed months will be in the year that follows the current year. Is there any easy solution for this?

Theocritus answered 17/11, 2012 at 10:4 Comment(1)
I think the simplest solution is, after the date has been parsed, to check if it is in the past and in that case add 1 year.Despumate
T
4

To find future dates automatically:

from dateutil import parser
from dateutil.relativedelta import relativedelta

def parse_future(timestr, default, **parse_kwargs):
    """Same as dateutil.parser.parse() but only returns future dates."""
    now = default
    for _ in range(401):  # assume gregorian calendar repeats every 400 year
        try:
            dt = parser.parse(timestr, default=default, **parse_kwargs)
        except ValueError:
            pass
        else:
            if dt > now: # found future date
                break
        default += relativedelta(years=+1)
    else: # future date not found
        raise ValueError('failed to find future date for %r' % (timestr,))
    return dt

Example

from datetime import datetime

for timestr in ["23 nov", "28 dec", "3 jan", "29 feb"]:
    print parse_future(timestr, default=datetime(2012, 11, 17)).date()

Output

2012-11-23
2012-12-28
2013-01-03
2016-02-29

Note: "29 feb" is translated to "2016-02-29".

Tham answered 17/11, 2012 at 11:19 Comment(4)
Shouldn't the year be added to the date only for passed months, according to the OP?Higbee
@DavidPärsson: just pass the last day of the previous month as default e.g., default=datetime.today().replace(day=1) - timedelta(1)Tham
actually I was looking more for a function that could decide wether the date is passed or it's about next year, I'm thinking it would be better to get dates which are 3 months passed to be the current year and the 9 other months to be future years.No matter what it will always be current or next year dates...Theocritus
but hey, I like both your approaches! But in my program a date that doesn't exist in one year, should be trying the other (current/next) year and otherwise return false.Theocritus
C
0

This function adds one year to the supplied date for passed months:

from datetime import date
from dateutil.relativedelta import relativedelta

def add_one_year_to_passed_months(date):
    today = date.today()
    if date.month < today.month:
        return date + relativedelta(years=1)
    return date

Leap years like 2012 are taken care of by relativedelta in this way:

>>> add_one_year_to_passed_months(date(2012, 2, 28))
datetime.date(2013, 2, 28)

>>> add_one_year_to_passed_months(date(2012, 2, 29))
datetime.date(2013, 2, 28)

>>> add_one_year_to_passed_months(date(2012, 3, 1))
datetime.date(2013, 3, 1)
Conformation answered 17/11, 2012 at 10:27 Comment(4)
shouldn't the first "29 feb" in the future be "2016-02-29"?Tham
2016 is not "the year that follows the current year", so I don't think so.Higbee
@J.F.Sebastian's point was that the 2013-02-29 does not exist as 2013 is not a leap year.Peccadillo
@Ben: I understand that, but since the OP doesn't specify what's expected in this case, we can only make assumptions and my answer shows what I assume that the OP wants.Higbee

© 2022 - 2024 — McMap. All rights reserved.