With the basics of date and time covered, we can look at how the dateutil module can help us with relative dates.
This post is part of my journey to learn Python. You can find the other parts of this series here. You find the code for this post in my PythonFriday repository on GitHub.
The dateutil module
The dateutil module is great to compute relative deltas (next month, next year, next Monday, last week of month, etc), calculate easter and working with time zones. We can install it with this command:
1 |
pip install -U python-dateutil |
Relative deltas
The relativedelta() method allows us to be more specific with the duration of the delta we want to add to our date or datetime object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>> from dateutil.relativedelta import * >>> today = date.today() >>> today datetime.date(2022, 8, 19) >>> today + relativedelta(weeks=+1) datetime.date(2022, 8, 26) >>> today + relativedelta(months=+1) datetime.date(2022, 9, 19) >>> today + relativedelta(years=+1) datetime.date(2023, 8, 19) >>> today + relativedelta(years=+1, months=+1, weeks=+1) datetime.date(2023, 9, 26) |
When we add months and years, we quickly run into edge cases. What happens if we add one month to a date that does not exist for that month? Or how does arithmetic around leap years’ work? The dateutil module gives us a reasonable result:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
>>> end_of_july = date(2022, 7, 31) >>> end_of_july + relativedelta(months=+1) datetime.date(2022, 8, 31) >>> end_of_july + relativedelta(months=+2) datetime.date(2022, 9, 30) >>> end_of_july + relativedelta(months=+3) datetime.date(2022, 10, 31) >>> end_of_february_2020 = date(2020, 2, 29) >>> end_of_february_2020 + relativedelta(years=+1) datetime.date(2021, 2, 28) >>> end_of_february_2020 + relativedelta(years=+2) datetime.date(2022, 2, 28) >>> end_of_february_2020 + relativedelta(years=+3) datetime.date(2023, 2, 28) >>> end_of_february_2020 + relativedelta(years=+4) datetime.date(2024, 2, 29) |
Attention: the singular words (month, week, hour, etc.) replace the existing value, while the plural words (months, weeks, hours, etc.) add the amount to the existing value.
1 2 3 4 5 6 |
>>> today + relativedelta(year=+1) datetime.date(1, 8, 19) >>> today + relativedelta(month=+2) datetime.date(2022, 2, 19) >>> today + relativedelta(day=+15) datetime.date(2022, 8, 15) |
We can use the calendar object to find dates that are at a specific weekday, like next Sunday or the Friday in the next week:
1 2 3 4 5 6 7 |
>>> import calendar >>> today + relativedelta(weekday=calendar.FRIDAY) datetime.date(2022, 8, 19) >>> today + relativedelta(weekday=calendar.SUNDAY) datetime.date(2022, 8, 21) >>> today + relativedelta(days=+1, weekday=calendar.FRIDAY) datetime.date(2022, 8, 26) |
Easter
The calculation of easter is often used as an exercise. With the dateutil module we can use the easter() method to get the date in a specific year:
1 2 3 |
>>> from dateutil.easter import easter >>> easter(2022) datetime.date(2022, 4, 17) |
There are three algorithms you can use to calculate Easter in dateutil:
- Original calculation in Julian calendar, valid in dates after 326 AD
- Original method, with date converted to Gregorian calendar, valid in years 1583 to 4099
- Revised method, in Gregorian calendar, valid in years 1583 to 4099 as well
By default, dateutil uses method 3:
1 2 3 4 5 6 |
>>> easter(2022, method=3) datetime.date(2022, 4, 17) >>> easter(2022, method=2) datetime.date(2022, 4, 24) >>> easter(2022, method=1) datetime.date(2022, 4, 11) |
Testing support
Especially when you persist dates in a database, the database server may not have the full precision of the Python datetime implementation and cut off a part of the time. Therefore, comparing two datetime objects may fail even when they are basically the same. With the within_delta() method we can specify the timedelta in which we consider them as equal:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
>>> from datetime import datetime, timedelta >>> from dateutil import * >>> from dateutil.relativedelta import * >>> now = datetime.now() >>> now datetime.datetime(2022, 8, 19, 20, 17, 31, 899041) >>> later = now + relativedelta(seconds=+1) >>> later datetime.datetime(2022, 8, 19, 20, 17, 32, 899041) >>> now == later False >>> utils.within_delta(now, later, timedelta(seconds=1)) True >>> utils.within_delta(now, later, timedelta(microseconds=1)) False |
More to explore
The dateutil module offers a lot more than what I covered here. You can read up on the many features in the official documentation. So far, I had no need to work with time zones, but that topic is well covered in dateutil.
Conclusion
With date, datetime and the relative deltas of dateutil I can do everything I need with date and time. I hope this quick overview gives you a good starting point. As soon as I need time zones, I will blog about it.
1 thought on “Python Friday #136: Date and Time in Python (Part 3: dateutil)”