Introduction
Working with datetime can be a bit daunting and challenging, and handling datetime in Python is no exception. Python's built-in datetime
module introduced us to several classes - date
, datetime
, time
, timezone
and timedelta
, and several external libraries have spawned to address the issues present in the official module, such as Arrow.
In this guide, we'll take a look at how to create and parse datetime objects in Delorean - a library built upon pytz which is the standard library for solving problems relating to time zones, and dateutil which is used to calculate deltas between any 2 given datetime objects.
Installing Delorean Setting Up a Virtual Environment
Delorean is available for installation via pip
. It is generally advisable to work in a virtual environment because it enables you to organize dependencies required by different projects isolated.
On Linux, or MacOS to create a virtual environment, we'd run:
$ python3 -m venv env
$ source env/bin/activate
$ python3 -m pip install delorean
Alternatively, on Windows we can run:
$ virtualenv env
$ .\env\Scripts\activate
$ python3 -m pip install delorean
Creating a Delorean Datetime Object
The main class we'll be working with, which represents all datetime objects is the Delorean()
class. Let's go ahead and import it from the delorean
module, and instantiate a datetime object:
from delorean import Delorean
dt_tm = Delorean()
print("Datetime: ", dt_tm)
After running the code, you should see the following on your terminal/command line:
Datetime: Delorean(datetime=datetime.datetime(2021, 7, 11, 18, 40, 43, 760187), timezone='UTC')
As usual, it's a wrapper for the standard datetime
object, which is situated inside the Delorean
object assigned to it. The default timezone is 'UTC'
, though, you can easily switch this by either defining the timezone while instantiating the object or by shifting the time to a different timezone.
Since the output is a bit hard to parse by humans - it makes sense to extract some of the data from the wrapper to make it easier to interpret. While the descending hierarchy of time is clear - it takes too long to go through it and parse it with our eyes. Let's get the date
of this object and print just that:
from delorean import Delorean
dt_tm = Delorean()
dt = Delorean().date
print("Date: ", dt)
This results in:
Date: 2021-07-11
If you're interested in just the time, without much regard for the date itself, you can get both the timezone-aware time, as well as the timezone-naive time fairly easily:
from delorean import Delorean
dt_tm = Delorean()
tm = dt_tm.datetime.time()
print("Timezone-aware time: ", tm)
naive_dt_tm = dt_tm.naive
print("Timezone-naive datetime: ", naive_dt_tm)
This results in:
Timezone-aware time: 18:40:21.235708
Timezone-naive datetime: 2021-07-11 18:40:21.235708
To change the timezone, we either supply it to the constructor call, or shift the time:
from delorean import Delorean
dt_tm = Delorean(timezone='Europe/Paris')
print("Datetime Object: ", dt_tm)
print("Time: ", dt_tm.datetime.time())
dt_tm.shift('US/Pacific')
print("Shifted time: ", dt_tm.datetime.time())
Datetime Object: Delorean(datetime=datetime.datetime(2021, 7, 11, 20, 43, 26, 990117), timezone='Europe/Paris')
Time: 20:43:26.990117
Shifted time: 11:43:26.990117
To take a look at all the available timezones, since Delorean uses pytz
under the hood - we can simply print them out:
import pytz
timezones = pytz.all_timezones
num = len(timezones)
print(f"There are {num} timezones: \n")
for tz in pytz.all_timezones:
print(tz)
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Which results in:
There are 593 timezones:
Africa/Abidjan
Africa/Accra
...
US/Michigan
US/Mountain
UTC
Universal
W-SU
WET
Zulu
Convert String to Delorean Datetime Object
No library would be complete without the ability to parse strings into datetime objects. Delorean is versatile with the string formats and assumes that the day comes first if there is any ambiguity in the parsed string. The parse()
method is used to parse a string, and it optionally accepts timezone information - otherwise, 'UTC'
is assumed:
import delorean
# Datetime strings of differing formats
datetime_strings = ["Mon May 12 2021 00:01:02",
"25-12-2021", "8/6/2019",
"15-12-1987 7:00:32",
"June 5th, 2021",
"5th of April, 2012",
"Thu 13 of July"]
for date in datetime_strings:
delorean_object = delorean.parse(date)
print(delorean_object)
Running this code results in:
Delorean(datetime=datetime.datetime(2021, 5, 12, 0, 1, 2), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 12, 25, 0, 0), timezone='UTC')
Delorean(datetime=datetime.datetime(2019, 6, 8, 0, 0), timezone='UTC')
Delorean(datetime=datetime.datetime(1987, 12, 15, 7, 0, 32), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 6, 5, 0, 0), timezone='UTC')
Delorean(datetime=datetime.datetime(2012, 4, 5, 0, 0), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 7, 13, 0, 0), timezone='UTC')
You can notice how in the ambiguous case of 8/6/2019
, it was assumed that the day comes first, hence it was parsed as the 8th of June, instead of the 6th of August. Also, since we didn't provide the year of the final date - it's automatically assigned to the current year.
If you'd like to turn this behavior around, you can set the dayfirst
argument to False
. Additionally, you can also set the yearfirst
argument to True
, which is False
by default, in which case, the first expected value will be the year:
delorean_object = delorean.parse("8/6/2019", dayfirst=False)
print(delorean_object)
This results in:
Delorean(datetime=datetime.datetime(2019, 8, 6, 0, 0), timezone='UTC')
Epoch Timestamp to Delorean Object
Since all modern computers adopted the use of UNIX time, also known as Epoch time - it's needless to say that we can convert a UNIX/Epoch timestamp into a Delorean object. This is the underlying mechanism that allows for working with time anyway. To convert a Unix timestamp into a Delorean datetime object, we use the epoch()
method of the delorean
module:
import delorean
timestamp = 3141592653
delorean_object = delorean.epoch(timestamp)
print(delorean_object)
We've inconspicuously used the first 10 digits of Pi to form a timestamp into the future, resulting in a time well beyond that of this guide:
Delorean(datetime=datetime.datetime(2069, 7, 21, 0, 37, 33), timezone='UTC')
Generating a Sequence of Dates
A great way to produce a sequence of Delorean objects is via the stops()
generator. You can generate N dates following a pattern, such as every Tuesday, or every hour or every 10 weeks. This is useful for creating, say, monthly payment plans or calculating ROI plans for instance.
Using this approach, you can also generate a series of dates on which something should occur, such as running a script every week at the same time, to gather data from an application for aggregation. Though, you can also use the python-crontab library for that, or the underlying crontab utility tool instead.
The stops()
generator accepts a freq
argument, denoting the frequency, a timezone
and a count
, denoting how many dates it should generate. The frequency can be set to any valid Delorean constant - SECONDLY
, MINUTELY
, HOURLY
, DAILY
, WEEKLY
, MONTHLY
or YEARLY
:
import delorean
for stop in delorean.stops(freq=delorean.HOURLY, timezone='UTC', count=10):
print(stop)
This generates a sequence of datetime objects following this pattern:
Delorean(datetime=datetime.datetime(2021, 7, 12, 13, 35, 12), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 7, 12, 14, 35, 12), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 7, 12, 15, 35, 12), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 7, 12, 16, 35, 12), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 7, 12, 17, 35, 12), timezone='UTC')
If you're unsure how many datetime objects you want exactly but do have a target datetime in mind, you can also set it to loop until a given date occurs:
import delorean
dt1 = delorean.Delorean().naive
dt2 = delorean.Delorean(datetime=datetime.datetime(2022, 1, 1), timezone='UTC').naive
for stop in delorean.stops(freq=delorean.MONTHLY, start=dt1, stop=dt2):
print(stop)
Note: The stops()
method only accepts timezone-naive datetime instances for the start
and stop
arguments, and returns timezone-aware dates. But it also requires you to specify a timezone
when instantiating a Delorean
instance using the constructor. What we're left with is - defining a timezone
for the instance, and then using the naive
datetime for both instead.
Running this code will give us a date for each month until it reaches the first of January, 2022:
Delorean(datetime=datetime.datetime(2021, 7, 12, 13, 46, 1), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 8, 12, 13, 46, 1), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 9, 12, 13, 46, 1), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 10, 12, 13, 46, 1), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 11, 12, 13, 46, 1), timezone='UTC')
Delorean(datetime=datetime.datetime(2021, 12, 12, 13, 46, 1), timezone='UTC')
Conclusion
In this guide, we've taken a look at how to create and parse Delorean objects in Python. We've seen how to convert a string to datetime in various formats, how to convert an epoch timestamp into datetime, and how to generate a sequence of dates using the stops()
generator.