ActiveState Code

Recipe 425607: Findng the x'th day in a month


What is the 2nd last Friday in May 2005 ? What is the first Monday in August 2006 ?

cutoff_date = dow_date_finder(SECONDLAST,FRI,OCT,2005)

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import datetime   # a thing of beauty and a joy forever

FIRST = 0
SECOND = 1
THIRD = 2
FOURTH = FORTH = 3  # for people who have finger trouble
FIFTH = 4
LAST = -1
SECONDLAST = -2
THIRDLAST = -3

MONDAY = MON = 0
TUESDAY = TUE = TUES = 1
WEDNESDAY = WED = 2
THURSDAY = THU = THUR = 3
FRIDAY = FRI = 4
SATURDAY = SAT = 5
SUNDAY = SUN = 6

JANUARY = JAN = 1
FEBRUARY = FEB = 2
MARCH = MAR = 3
APRIL = APR = 4
MAY = 5
JUNE = JUN = 6
JULY = JUL = 7
AUGUST = AUG = 8
SEPTEMBER = SEP = 9
OCTOBER = OCT = 10
NOVEMBER = NOV = 11
DECEMBER = DEC = 12

def dow_date_finder(which_weekday_in_month=FIRST,day=MONDAY,month=JANUARY,year=2000):
    dt = datetime.date(year,month,1)
    dow_lst = []
    while dt.weekday() != day:
        dt = dt + datetime.timedelta(days=1)
    while dt.month == month:
        dow_lst.append(dt)
        dt = dt + datetime.timedelta(days=7)
    return dow_lst[which_weekday_in_month]  # may raise an exception if slicing is wrong


if __name__ == "__main__":
    print "2nd tuesday of may 2005"
    print dow_date_finder(SECOND,TUESDAY,MAY,2005)
    print "last wednesday of april 2005"
    print dow_date_finder(LAST,WEDNESDAY,APRIL,2005)
    print "secondlast friday of october 2005 - short form"
    print dow_date_finder(SECONDLAST,FRI,OCT,2005)

Discussion

Commercial programs often need to know these days : usually they are special because they're holidays or financial periods or whatever.

I personally don't like the Monday=0 that we find in Python - it goes against "cron" standard, so I always battle a but with it. So "hash-defining" it as above makes it slightly more palatable.

The function should really be hardened a lot more : checking for invalid days of week, days of month and the "which value". But it runs in a consistent environment so I haven't felt the need for that.

Comments

  1. 1. At 4:12 a.m. on 30 jun 2005, Raymond Hettinger said:

    Alternate version using the calendar module.

    from calendar import monthrange
    
    def dow_date_finder(which_weekday_in_month=FIRST,day=MONDAY,month=JANUARY,year=2000):
        bom, days = monthrange(year, month)
        firstmatch = (day - bom) % 7 + 1
        return xrange(firstmatch, days+1, 7)[which_weekday_in_month]
    
  2. 2. At 9:16 p.m. on 28 mar 2006, Chris Smith said:

    You might want to embed that last line in a try/except to catch the case when the requested day does not exist, e.g. there's not a 5th Thursday of Nov 1977 but there is in 1978:

    try:
        return xrange(firstmatch, days+1, 7)[which_weekday_in_month]
    except:
        return None
    
  3. 3. At 12:01 a.m. on 5 aug 2007, greg p said:

    I put this into an online utility here:

    http://www.utilitymill.com/utility/Nth_Weekday_in_Month

    Hopefully it will be useful to people. Feel free to improve it, some of the parameters still aren't working.

Sign in to comment