# Iterating through a range of dates in Python

I have the following code to do this, but how can I do it better? Right now I think it's better than nested loops, but it starts to get Perl-one-linerish when you have a generator in a list comprehension.

``````day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
print strftime("%Y-%m-%d", single_date.timetuple())
``````

## Notes

• I'm not actually using this to print. That's just for demo purposes.
• The `start_date` and `end_date` variables are `datetime.date` objects because I don't need the timestamps. (They're going to be used to generate a report).

## Sample Output

For a start date of `2009-05-30` and an end date of `2009-06-09`:

``````2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09
``````

77

Why are there two nested iterations? For me it produces the same list of data with only one iteration:

``````for single_date in (start_date + timedelta(n) for n in range(day_count)):
print ...
``````

And no list gets stored, only one generator is iterated over. Also the "if" in the generator seems to be unnecessary.

After all, a linear sequence should only require one iterator, not two.

## Update after discussion with John Machin:

Maybe the most elegant solution is using a generator function to completely hide/abstract the iteration over the range of dates:

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

def daterange(start_date, end_date):
for n in range(int((end_date - start_date).days)):
yield start_date + timedelta(n)

start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
print(single_date.strftime("%Y-%m-%d"))
``````

NB: For consistency with the built-in `range()` function this iteration stops before reaching the `end_date`. So for inclusive iteration use the next day, as you would with `range()`.

Tuesday, June 1, 2021

53

Marginally better...

``````base = datetime.datetime.today()
date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
``````
Tuesday, June 1, 2021

90

According https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html Z has special meaning:

``````Z       zone-offset
``````

If you want to escape Z quote Z with ':

``````yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
``````

For example:

``````java.time.LocalDateTime date = java.time.LocalDateTime.now();
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
java.lang.String text = date.format(formatter);
System.out.println(text);
``````

prints

``````2016-06-11T18:39:41.962Z
``````
Saturday, July 31, 2021

15

Using an `IntervalIndex`, which is new in Pandas 0.20.0. This looks to still be in the experimental phase though, so other solutions may be more reliable.

``````# Get the 'id' column indexed by the 'start'/'end' intervals.
s = pd.Series(df_b['id'].values, pd.IntervalIndex.from_arrays(df_b['start'], df_b['end']))

# Map based on the date of df_a.
df_a['id'] = df_a['date'].map(s)
``````

The resulting output:

``````        date values  id
0 2017-05-16      x  34
1 2017-04-12      Y  32
``````

Alternatively, if you don't mind altering the index of `df_b`, you could just directly convert to an `IntervalIndex` on it:

``````# Create an IntervalIndex on df_b.
df_b = df_b.set_index(['start', 'end'])
df_b.index = pd.IntervalIndex.from_tuples(df_b.index)

# Map based on the date of df_a.
df_a['id'] = df_a['date'].map(df_b['id'])
``````
Sunday, August 15, 2021

73

This should do it:

``````from datetime import datetime

def is_third_friday(s):
d = datetime.strptime(s, '%b %d, %Y')
return d.weekday() == 4 and 15 <= d.day <= 21
``````

Test:

``````print is_third_friday('Jan 18, 2013')  # True
print is_third_friday('Feb 22, 2013')  # False
print is_third_friday('Jun 21, 2013')  # True
print is_third_friday('Sep 20, 2013')  # True
``````
Monday, September 6, 2021