Skip to content

Instantly share code, notes, and snippets.

@zed
Last active February 7, 2018 01:15
Show Gist options
  • Save zed/11441b4776f7bbecc830 to your computer and use it in GitHub Desktop.
Save zed/11441b4776f7bbecc830 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
"""Test functions from
http://stackoverflow.com/questions/28680896/how-can-i-get-the-3rd-friday-of-a-month-in-python
"""
import calendar
from datetime import date, datetime, time, timedelta
from itertools import islice
DAY = timedelta(1)
WEEK = 7*DAY
try:
from dateutil.relativedelta import relativedelta, FR # $ pip install python-dateutil
def third_friday_dateutil(now, delta=relativedelta(weeks=2, weekday=FR)):
"""the 3rd Friday of the month, not the 3rd Friday after today."""
now = now.replace(day=1) # 1st day of the month
return now + delta
from dateutil.rrule import rrule, MONTHLY, FR
def third_friday_rrule(now):
r = rrule(MONTHLY, count=1, byweekday=FR, bysetpos=3, dtstart=now.replace(day=1))
return r[0].date()
def get_third_fris_rrule(how_many, now=date.today()):
r = rrule(MONTHLY, count=how_many, byweekday=FR, bysetpos=3, dtstart=now+DAY)
return [dt.date() for dt in r]
except ImportError:
pass
def fridays(now):
"""Generate Fridays starting (including) from now."""
while True:
if now.weekday() == calendar.FRIDAY:
while True:
yield now
now += WEEK
now += DAY
def next_month(now):
"""Return the first date that is in the next month."""
return (now.replace(day=15) + 20*DAY).replace(day=1)
def third_friday_brute_force(now):
"""the 3rd Friday of the month, not the 3rd Friday after today."""
return next(islice(fridays(now.replace(day=1)), 2, 3))
def get_third_fris(how_many, now=date.today()):
result = []
while len(result) < how_many:
fr = third_friday_brute_force(now)
if fr > now: # use only the 3rd Friday after today
result.append(fr)
now = next_month(now)
return result
def third_friday_adam_smith(now, c=calendar.Calendar(firstweekday=calendar.SUNDAY)):
year, month = now.year, now.month
monthcal = c.monthdatescalendar(year, month)
third_friday = [day for week in monthcal for day in week if
day.weekday() == calendar.FRIDAY and day.month == month][2]
return third_friday
def third_friday_juniorcompressor(d):
s = date(d.year, d.month, 15)
return s + timedelta(days=(calendar.FRIDAY - s.weekday()) % 7)
def juniorcompressor_next_third_friday(d):
""" Given a third friday find next third friday"""
d += timedelta(weeks=4)
return d if d.day >= 15 else d + timedelta(weeks=1)
def get_third_fris_juniorcompressor(n, d=date.today()):
"""Given a date, calculates n next third fridays"""
# Find closest friday to 15th of month
s = date(d.year, d.month, 15)
result = [s + timedelta(days=(calendar.FRIDAY - s.weekday()) % 7)]
# This month's third friday passed. Find next.
if result[0] <= d:
result[0] = juniorcompressor_next_third_friday(result[0])
for i in range(n - 1):
result.append(juniorcompressor_next_third_friday(result[-1]))
return result
def test(functions, start=date.today(), end=None):
if end is None:
end = start.replace(year=start.year + 400) + DAY
d = start
while d < end:
expected = functions[0](d)
assert all(expected == f(d) for f in functions), (d, [(getattr(f, '__name__', f), f(d)) for f in functions])
d += DAY
if __name__=="__main__":
from pprint import pprint
n = 6
pprint(get_third_fris_rrule(n))
from reporttime import get_functions_with_prefix, measure # https://gist.github.com/zed/5650097
functions = get_functions_with_prefix('third_friday_')
test(functions)
measure(functions, [date.today()])
functions = get_functions_with_prefix('get_third_fris')
assert get_third_fris_juniorcompressor(n) == get_third_fris(n)
measure(functions, [n])
from functools import partial
test([partial(f, 1) for f in functions])
n = 12*401
assert get_third_fris_juniorcompressor(n) == get_third_fris(n)
"""### Output
[datetime.date(2015, 3, 20),
datetime.date(2015, 4, 17),
datetime.date(2015, 5, 15),
datetime.date(2015, 6, 19),
datetime.date(2015, 7, 17),
datetime.date(2015, 8, 21)]
name time ratio comment
third_friday_juniorcompressor 1.69 usec 1.00 [datetime.date(2015, 2, 27)]
third_friday_brute_force 3.38 usec 2.00 [datetime.date(2015, 2, 27)]
third_friday_dateutil 11.2 usec 6.61 [datetime.date(2015, 2, 27)]
third_friday_adam_smith 18.8 usec 11.12 [datetime.date(2015, 2, 27)]
third_friday_rrule 52.7 usec 31.12 [datetime.date(2015, 2, 27)]
name time ratio comment
get_third_fris_juniorcompressor 15.6 usec 1.00 [6]
get_third_fris 38.1 usec 2.44 [6]
get_third_fris_rrule 153 usec 9.77 [6]
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment