string to date in python with the greatest possible year when dow is given
Question: Given:

    
    
    from datetime import datetime
    
    date = datetime.strptime('Wed 12 Nov', '%a %d %b')
    

It returns `datetime.datetime(1900, 11, 12, 0, 0)`. What might be the best way
to get the greatest past year?, in this case should 2014 instead of 1900.

Any help will be appreciate.


Answer: You can get the year from `datetime.now()` and subtract 1:

    
    
    from datetime import datetime
    
    date = datetime.strptime('Wed 12 Nov {}'.format(datetime.now().year-1), '%a %d %b %Y')
    

This will not work for feb 29.

Actually a bug in the first implementation, it was starting on a monday
because 12 November 1900 was a Monday:

    
    
    dte = 'Wed 12 Nov'
    
    start = datetime.strptime('Wed 12 Nov', "%a %d %b")
    greatest = None
    
    while start <= datetime.now():
        start += timedelta(days=1)
        if start.strftime("%a %d %b") == dte:
            greatest = start
    
    print(greatest)
    
    2014-11-12 00:00:00
    

There is also a [monthdelta](https://pythonhosted.org/MonthDelta/) package
that you can use to increment by month:

    
    
    from datetime import datetime
    from monthdelta import monthdelta
    
    
    dte = 'Wed 12 Nov'
    
    start = datetime.strptime('Wed 12 Nov', "%a %d %b")
    greatest = None
    
    while start <= datetime.now():
        start += monthdelta(1)
        if start.strftime("%a %d %b") == dte:
            greatest = start
    
    print(greatest)
    

You can see incrementing by months is much more efficient:

    
    
    In [1]: from datetime import datetime, timedelta
    
    In [2]: %%timeit
       ...: dte = 'Wed 12 Nov'
       ...: start = datetime.strptime('Wed 12 Nov', "%a %d %b")
       ...: greatest = None
       ...: while start <= datetime.now():
       ...:     start += timedelta(days=1)
       ...:     if start.strftime("%a %d %b") == dte:
       ...:         greatest = start
       ...: 
    1 loops, best of 3: 382 ms per loop
    
    In [3]: from datetime import datetime
    
    In [4]: from monthdelta import monthdelta
    
    In [5]: %%timeit
       ...: dte = 'Wed 12 Nov'
       ...: start = datetime.strptime('Wed 12 Nov', "%a %d %b")
       ...: greatest = None
       ...: while start <= datetime.now():
       ...:     start += monthdelta(1)
       ...:     if start.strftime("%a %d %b") == dte:
       ...:         greatest = start
       ...: 
    100 loops, best of 3: 18.7 ms per loop
    

Both return pretty quick but if you had many calls to the method then the
monthly increase is a better option. We could also add 30 days and then set
the day to 12, there may be bugs as I have not overly tested it:

    
    
    def match_date(abb_wk_dy, day_date, abb_mon):
        dte = "{} {} {}".format(abb_wk_dy.capitalize(), day_date, abb_mon.capitalize())
        start = datetime.strptime(dte, "%a %d %b")
        greatest = None
        while start <= datetime.now():
            start +=  timedelta(days=30)
            start = start.strptime("{} {} {}".format(start.year, start.month, day_date), "%Y %m %d")
            if start.strftime("%a %d %b") == dte:
                greatest = start
        return greatest
    

The last code runs pretty efficiently:

    
    
    In [12]: timeit match_date("wed","12","nov")
    10 loops, best of 3: 34.7 ms per loop
    

If you only want the year then return `greatest.year`.

On testing the above code fails for leap years so we need to catch that, we
can also just increase the year by 1 each time:

    
    
    def match_date(abb_wk_dy, day_date, abb_mon):
        wkd, dd, ab = abb_wk_dy.capitalize(), day_date, abb_mon.capitalize()
        match = "{} {} {}".format(wkd, dd, ab)
        try:
            dte = "{} {} {} {}".format(1900, wkd, dd, ab)
            start = datetime.strptime(dte, "%Y %a %d %b")
        except ValueError:
            # first leap year since 1900
            dte = "{} {} {} {}".format(1904, wkd, dd, ab)
            start = datetime.strptime(dte, "%Y %a %d %b")
        day, mon = start.day, start.month
        greatest = None
        while start <= datetime.now():
            try:
                start = start.strptime("{} {} {}".format(start.year + 1, mon, day), "%Y %m %d")
            except ValueError:
                start = start.strptime("{} {} {}".format(start.year + 1, 01, 01), "%Y %m %d")
                continue
            if start.strftime("%a %d %b") == match:
                greatest = start
        return greatest.year if greatest else "No match"
    

Which runs in:

    
    
    In [27]: timeit match_date("Wed","12","Nov")
    100 loops, best of 3: 2.63 ms per loop
    

You would also need to validate that no day > 31 is ever entered and other
months and days match up which could be achieved using a dict or
`calender.monthrange` or a dict mapping max day in month to month name.

