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.