# All tuesdays and wednesdays in a date range: is there a more pythonic way? - Python

TAGS :
Viewed: 2 - Published at: a few seconds ago

#### [ All tuesdays and wednesdays in a date range: is there a more pythonic way? ]

I'd like to find all the tuesdays and wednesdays (as `datetime` object) between 2015-11-02 and 2015-12-14. This works:

``````from datetime import datetime, timedelta

l = []
for i in range(100):
d = datetime(2015,11,2) + timedelta(days=i)
if d &gt; datetime(2015,12,14):
break
if d.weekday() == 1 or d.weekday() == 2:   # tuesday or wednesday
l.append(d)
print l
``````

[datetime.datetime(2015, 11, 3, 0, 0), datetime.datetime(2015, 11, 4, 0, 0), datetime.datetime(2015, 11, 10, 0, 0), datetime.datetime(2015, 11, 11, 0, 0), datetime.datetime(2015, 11, 17, 0, 0), datetime.datetime(2015, 11, 18, 0, 0), datetime.datetime(2015, 11, 24, 0, 0), datetime.datetime(2015, 11, 25, 0, 0), datetime.datetime(2015, 12, 1, 0, 0), datetime.datetime(2015, 12, 2, 0, 0), datetime.datetime(2015, 12, 8, 0, 0), datetime.datetime(2015, 12, 9, 0, 0)]

Is there a more pythonic way to do it?

``````from datetime import datetime, timedelta

start, end = datetime(2015, 11, 2), datetime(2015, 12, 14)
days = (start + timedelta(days=i) for i in range((end - start).days + 1))
l = [d for d in days if d.weekday() in [1,2] ]
``````

This will be a lot faster if you are going over a long span:

``````def helper(d, i, inc):
while d.weekday() != i:
d += timedelta(days=inc)
return d.replace(hour=0, minute=0, second=0, microsecond=0)

start, end = datetime(2015, 11, 02), datetime(2015,12, 14)

def find_days(st, end, d1, d2):
if st >= end:
raise ValueError("Start must be before end")
else:
_st, _end = helper(st, d1, inc=-1), helper(end, d2, 1)
secs = (_end - _st).total_seconds() // 86400
if st.weekday() == d2:
yield st
for i in range(int(secs / 7) + 1):
if st <= _st <= end:
yield _st
nxt = _st + timedelta(days=1)
if nxt <= end:
yield nxt
_st += timedelta(days=7)
if _st <= end:
yield _st
from pprint import pprint as pp

from pprint import pprint as pp

pp(list(find_days(start, end, 1, 2)))
``````

Output:

``````[datetime.datetime(2015, 11, 3, 0, 0),
datetime.datetime(2015, 11, 4, 0, 0),
datetime.datetime(2015, 11, 10, 0, 0),
datetime.datetime(2015, 11, 11, 0, 0),
datetime.datetime(2015, 11, 17, 0, 0),
datetime.datetime(2015, 11, 18, 0, 0),
datetime.datetime(2015, 11, 24, 0, 0),
datetime.datetime(2015, 11, 25, 0, 0),
datetime.datetime(2015, 12, 1, 0, 0),
datetime.datetime(2015, 12, 2, 0, 0),
datetime.datetime(2015, 12, 8, 0, 0),
datetime.datetime(2015, 12, 9, 0, 0)]
``````

This does what dateutil does and does it faster:

``````In : def dte():
....:         results = rrule(DAILY,
....:                 dtstart = dt.datetime(2015,11, 2),
....:                 until = end,
....:                 byweekday=(TU, WE),
....:         )
....:         return list(results)
....:

In : start, end = datetime(2015, 11, 2), datetime(2100, 11, 14)

In : for i in range(600):
end += timedelta(days=1)
assert dte() == list(find_days(start, end,1,2 ))
....:

In : start, end = datetime(2015, 11, 2), datetime(2017, 11, 14)

In : timeit  [d for d in date_range(start, end) if d.weekday() in (1, 2)]
10 loops, best of 3: 62.1 ms per loop

In : timeit list(find_days(start, end, 1, 2))
100 loops, best of 3: 8.11 ms per loop

In : timeit dte()
10 loops, best of 3: 131 ms per loop
``````

If you have a simple data range (similar to Python's `range` function):

``````import datetime as dt

def date_range(d1, d2):
d=d1
while d<=d2:
yield d
d+=dt.timedelta(days=1)
``````

Then you can use a simple list comprehension:

``````>>> '\n'.join([d.isoformat() for d in date_range(dt.date(2015,11,2),dt.date(2015,12,14)) if d.weekday() in (1,2)])
2015-11-03
2015-11-04
2015-11-10
2015-11-11
2015-11-17
2015-11-18
2015-11-24
2015-11-25
2015-12-01
2015-12-02
2015-12-08
2015-12-09
``````

If you are concerned that it is wasteful iterating day by day, time it:

``````\$ python -m timeit '
> import datetime as dt
> def date_range(d1, d2, step=1):
>     d=d1
>     while True:
>         if d+dt.timedelta(days=step)>d2:
>             break
>         yield d
>         d+=dt.timedelta(days=step)
> [d.isoformat() for d in date_range(dt.date(1815,11,2),dt.date(2215,12,14)) if d.weekday() in (1,2)]
> '
10 loops, best of 3: 214 msec per loop
``````

Calculating 400 years of Tuesdays and Wednesdays takes 1/4 second and about 100 millisec for a 1 year range. Cheers.

Here it is with the third party module `python-dateutils`:

``````from dateutil.rrule import rrule, DAILY, TU, WE
import datetime as dt

results = rrule(DAILY,
dtstart = dt.datetime(2015,11,2),
until = dt.datetime(2015, 12, 14),
byweekday = (TU, WE),
)

for result in results:
print(result)

--output:--
2015-11-03 00:00:00
2015-11-04 00:00:00
2015-11-10 00:00:00
2015-11-11 00:00:00
2015-11-17 00:00:00
2015-11-18 00:00:00
2015-11-24 00:00:00
2015-11-25 00:00:00
2015-12-01 00:00:00
2015-12-02 00:00:00
2015-12-08 00:00:00
2015-12-09 00:00:00
``````

As I already use `pandas`, this works:
``````import pandas as pd