Iterating Through a Range of Dates with Python’s datetime

A question frequently asked in programming forums concerns the use and iteration of dates in Python. Today I wish to introduce the basics of the datetime module in Python, which will allow us to output a range of dates, given a date input and the number of days needed. We will also look at creating a list of datetime objects allowing us greater flexibility in sorting and outputting specific date sequences.

Let’s begin by understanding some fundamental concepts regarding dates and times in Python before introducing the datetime module. You can also watch the in-depth video guide as you go through the article:

Standardized Date Formats

Different cultures represent dates in different ways. Half of the world’s population writes dates as Day, Month, Year; some countries use Year, Month, Day, and others use Month, Day, Year.

The International Organisation for Standardisation (ISO) implemented standard ISO 8601, requiring dates be written from most significant to the least significant digit to avoid confusion. The format used, therefore, is represented as YYYY-MM-DD HH:MM:SS. Later, we will code using the datetime class, and you will see this format in action.

Now let’s understand how computers measure time

Most computer systems use a system of timekeeping called Unix Time, or Epoch Time, and count seconds from the Unix Epoch, which is arbitrarily set at 00:00:00 UTC on 1 January 1970. UTC stands for Coordinated Universal Time which is the time at 0 degrees longitude.

Within Python, we can find out the current number of seconds since Epoch Time by importing the time module and using the class time(), as shown here.

import time

j = time.time()
print(j)

# Result
1620224781.2914214

Introducing the datetime Module

Working with dates and time can be a frustrating experience, what with leap years and time zones.  Python has three modules called calendar, datetime, and time that reduce complexity significantly. For today’s two problems, we will use only the datetime module. To read in-depth on the module, follow this link to python.org, where you will find far more information than you are likely to need.

The three most common classes of the datetime module are:

datetime.date()
This class uses the Gregorian calendar, which started in 1582 following a slight adjustment to the Julian calendar. However, datetime.date() assumes the Gregorian calendar extends infinitely into the past and the future, and it takes the attributes, year, month, and day.


datetime.time()
This class is independent of any particular day and assumes that a day consists of 24 x 60 x 60 seconds and ignores leap seconds. It accepts attributes, hour, minute, second, microsecond, and time zone information, abbreviated to tzinfo.


datetime.datetime()
This class is a combination of the two classes above and utilizes the same attributes of both.

Of these three, today we’ll be using datetime.date() and datetime.datetime().

We’ll also use another class called datetime.timedelta(), which outputs a duration showing the difference between two date, time, or datetime instances down to microsecond resolution.

Putting it into practice

Let’s import the date and datetime classes and convert a date to see the output. Note the order of the attributes we pass to datetime().

from datetime import date,  datetime

a = datetime(2021, 5, 5)
print(a)

# Result
2021-05-05 00:00:00

There are two things to note here. First, you can see the ISO 8601 date format as previously described, and secondly, note that as we didn’t enter any attributes for a time, the output defaults to 00:00:00. We can, however, enter time attributes also.

from datetime import date,  datetime

a = datetime(2021, 5, 5, 16, 3, 26)
print(a)

# Result
2021-05-05 16:03:26

We’ll convert this date and time into a timestamp and then show you the code to convert it back to the date using the fromtimestamp class.

from datetime import date,  datetime

a = datetime(2021, 5, 5, 16, 3, 26)
print(a)

b = datetime.timestamp(a)
print(b)

d = datetime.fromtimestamp(b)
print(d)

# Result
2021-05-05 16:03:26  # Date correctly formatted
1620227006.0  # Above date as timestamp
2021-05-05 16:03:26  # Timestamp converted back to date

Before we move on to solving our two problems, here’s one more helpful piece of information. Once you have dates in the correct datetime format, you may add and subtract them to get a precise count of days, hours, and minutes elapsed. See the following example.

from datetime import date,  datetime

a = datetime(2021, 5, 5, 16, 3, 26)
print(a)

e = datetime(2020, 4, 10, 9, 52, 45)
print(e)

z = a - e
print(z)

# Result
2021-05-05 16:03:26
2020-04-10 09:52:45
390 days, 6:10:41  #  Days, hours and minutes between the two dates

Let’s move on to iterating through dates

We’ll assume that we have a date and a duration to work with for these two examples. Perhaps you need to output individual dates to a report from a start date or end date and advance or backtrack a known number of days. We’ll capture those two bits of information into variables, and then we’ll create a small for-loop to iterate through a range and print out the individual days sequentially.

Two things to note, the first is that we’re using only the date() class in this example as we don’t need the time component in the returned values, and secondly, I’ve truncated the returned values in the code below to save some space.

from datetime import date, timedelta

first_date = date(2021, 5, 15)

duration = timedelta(days=30)

for d in range(duration.days + 1):
    day = first_date + timedelta(days=d)
    print(day)

# Result
2021-05-15 
2021-05-16 
2021-05-17 
2021-05-18 
2021-05-19 
… # Truncated to save space
2021-06-07 
2021-06-08 
2021-06-09 
2021-06-10
2021-06-11 
2021-06-12 
2021-06-13 
2021-06-14 

So what’s happening in that for-loop? We created a duration variable that is passed a datetime.timedelta object containing only 30 days in this example; however, we could use any of the attributes that the class accepts. We used the duration.days to create the range for the for-loop. Remember to add the + 1 to ensure you get the last day of the range. Also, to begin the day after the first_date, add the digit 1 and a comma to the range indexing:

for d in range(1, duration.days + 1):
    day = first_date + timedelta(days=d)
    print(day)

# Result
2021-05-16 
2021-05-17 etc

The next line then simply increments the first date by days based on the number of iterations and prints the resulting date.

If you wish to backtrack from the start date and print the range of dates prior, change the + symbol in the equation to a minus symbol as follows.

for d in range(1, duration.days + 1):
    day = first_date - timedelta(days=d)
    print(day)

# Result
2021-05-15
2021-05-14
2021-05-13 etc

This example has solved our problem by allowing us to iterate through a range of dates given a start or endpoint. I’ll now show another example of some code that contains much of the same logic we’ve just seen, but it uses lists and list comprehension, which may better suit your application.  In this example, once again, we dictate the number of days we need as output, and we create a start (or end) date using either the date() or datetime() class. If you don’t need the time element, use date() as I have.  We then write a line of code that creates a list, incrementing the base date using the timedelta(days=x) syntax. Using timedelta is necessary as should you try to increment that base date using integers, you’ll get a TypeError. Let’s take a look at the output.

from datetime import date, timedelta

numdays = 30
base = date(2021, 5, 1)

date_list = [base + timedelta(days=x) for x in range(numdays)]

print(date_list)
# Result
[datetime.date(2021, 5, 1), datetime.date(2021, 5, 2), datetime.date(2021, 5, 3), datetime.date(2021, 5, 4), datetime.date(2021, 5, 5), datetime.date(2021, 5, 6), datetime.date(2021, 5, 7), datetime.date(2021, 5, 8), datetime.date(2021, 5, 9), datetime.date(2021, 5, 10), datetime.date(2021, 5, 11), datetime.date(2021, 5, 12), datetime.date(2021, 5, 13), datetime.date(2021, 5, 14), datetime.date(2021, 5, 15), datetime.date(2021, 5, 16), datetime.date(2021, 5, 17), datetime.date(2021, 5, 18), datetime.date(2021, 5, 19), datetime.date(2021, 5, 20), datetime.date(2021, 5, 21), datetime.date(2021, 5, 22), datetime.date(2021, 5, 23), datetime.date(2021, 5, 24), datetime.date(2021, 5, 25), datetime.date(2021, 5, 26), datetime.date(2021, 5, 27), datetime.date(2021, 5, 28), datetime.date(2021, 5, 29), datetime.date(2021, 5, 30)]

What we have back is a list of datetime.date() classes, each incremented from the base date by one day. This method may be helpful if you wish to carry out other activities using list comprehension and indexing. Otherwise, to receive the same output as the previous example, we run a for-loop. Once again I’ve truncated the results for space.

from datetime import date, datetime, timedelta

numdays = 30

base = date(2021, 5, 1)

date_list = [base + timedelta(days=x) for x in range(numdays)]

for i in date_list:
    print(i)

# Result
2021-05-01
2021-05-02
2021-05-03
2021-05-04
2021-05-05
2021-05-06
2021-05-07
2021-05-08
… # Truncated to save space
2021-05-24
2021-05-25
2021-05-26
2021-05-27
2021-05-28
2021-05-29
2021-05-30


Again, to reverse back from the base date, change the “[base + timedelta(days=x)...” operator to a subtraction rather than an addition.

date_list = [base - timedelta(days=x) for x in range(numdays)]

for i in date_list:
    print(i)

# Result
2021-05-01
2021-04-30
2021-04-29 etc

Summary

Today we introduced standardized date formats and the concept of Unix Time.

We introduced the datetime module in Python and used the datetime classes, date(), datetime(), and timedelta() to output sequential dates given a base date and the number of days needed.

We then used list comprehension to create a list containing datetime.date() classes with the day attribute incremented by the number of days stipulated. Such a list may be helpful in further indexing or list comprehension.

Finally, we used a for-loop to output sequential dates given the base date and the number of days needed, as we did in the first example.

I hope this article has been helpful and prompts you to further experiment with the datetime module in Python. Thank you for reading.