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?

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
Dieter
  • 441
  • 1
  • 5
  • 15
  • 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. – Pedro Romano Nov 17 '12 at 10:12

2 Answers2

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

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Shouldn't the year be added to the date only for passed _months_, according to the OP? – David Pärsson Nov 17 '12 at 11:27
  • @DavidPärsson: just pass the last day of the previous month as default e.g., `default=datetime.today().replace(day=1) - timedelta(1)` – jfs Nov 17 '12 at 11:46
  • 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... – Dieter Nov 17 '12 at 14:38
  • 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. – Dieter Nov 17 '12 at 14:40
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)
David Pärsson
  • 6,038
  • 2
  • 37
  • 52