Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 777
Christian Gutierrez Sierra Avatar asked Jan 02 '26 04:01

Christian Gutierrez Sierra


1 Answers

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

like image 105
Padraic Cunningham Avatar answered Jan 03 '26 17:01

Padraic Cunningham



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!