Arrow is a Python module for working with date and time. Given that there are several modules that do this, most notably the built-in
datetime module, what makes Arrow different?
In this guide, we'll take a look at some key features of Arrow, to see how it handles certain common tasks.
First, let's go ahead and install it:
$ pip install Arrow
The Arrow Class
Arrow class is an implementation of the
datetime interface, with additional functionalities. Also, it's timezone-aware by default - we'll go into this in a bit later, though.
You can easily create a new
Arrow instance by supplying its constructor with a few arguments:
import arrow arrow_object = arrow.Arrow(2021, 1, 1) print(arrow_object)
This results in:
You can also supply the hours, minutes and seconds through the constructor:
import arrow arrow_object = arrow.Arrow(2021, 1, 1, 14, 30, 21) print(arrow_object)
This results in:
Conversion Support with Arrow
Parsing a date and time from a string is a straightforward process with Arrow - you simply use the
get() method, and supply it with a valid string format. Also, Arrow lets you effortlessly convert between its own implementation of
datetime class and the built-in
Convert String to Datetime with Arrow
If a string is already formatted in the ISO 8601 format (
YYYY-MM-DDTHH:MM:SS.mmmmmm), it can be passed directly into the
import arrow datetime = arrow.get('2021/03/30 12:05') print(datetime)
This will print out the
Arrow instance, which is Arrow's own implementation of the
However, in practice, it is unlikely that we will be using correctly formatted strings, following the ISO specification.
Thankfully, we can still parse strings that don't adhere to conventions, by using correct Arrow format tokens. These are pre-defined and give Arrow the information needed to parse the string correctly:
import arrow datetime = arrow.get('March 30 2021 12:05', 'MMMM DD YYYY HH:mm') print(datetime)
Here, we've effectively told Arrow what the format is. It maps the supplied format tokens with the string we'd like to parse, and constructs an
Arrow object based on that info. Running this results in:
Convert Between Arrow and datetime Objects
So far, we've been working with
Arrow instances. However, many applications and libraries explicitly require you to use a
datetime object. Conversion between these two formats is crucial.
Let's take a look at the
type() of our variable:
To convert this to a
datetime instance, we simply extract the
datetime field from the
datetime = datetime.datetime print(datetime)
This results in a time-zone aware
datetime.datetime(2021, 3, 12, 12, 5, tzinfo=tzutc())
Even though we haven't specified the timezone when creating the original
Arrow object, the
datetime object has the
tzinfo defaulted to UTC. We will refer back to this in the next section, when taking a more detailed look into handling timezones.
For now, we can confirm it is indeed a
datetime object type:
This results in;
Similarly enough, you can easily convert
datetime objects into
Arrow objects, using the
datetime = datetime.datetime(2021, 1, 1, 0, 0) arrow_object = arrow.Arrow.fromdatetime(datetime) print(arrow_object)
This results in:
Dealing with Timezones
One of the major issues with the
datetime module is the way it handles timezones. It is considered timezone-naive, meaning it contains no timezone related data. Arrow on the other hand contains a
tzinfo parameter for each instantiation, which you can set through the constructor, or through methods. The
tzinfo defaults to UTC, regardless of the user's location.
In practical terms this means that with
datetime, a user in Hong Kong would be working in local Hong Kong time whereas a user in UK would be working in local UK time - unless otherwise specified:
import datetime datetime.datetime.now()
The output for the Hong Kong based user would be:
datetime.datetime(2021, 3, 31, 00, 35, 08, 114203)
Whereas someone based in the UK would see:
datetime.datetime(2021, 3, 30, 17, 35, 25, 119213)
Having a standard default timezone is increasingly important with the rise in remote working as well the globalization of projects. Having to explicitly set timezones for
datetime objects gets stale fast. Arrow automates this process, in a single, unified, default timezone. You can set it to other timezones, of course, or even local ones.
Irrespective of the user's geographical location, the following lines of code will give the same output:
We can confirm this corresponds to the UTC time:
We can set timezones on the
Arrow instance simply by passing in the timezone string in its constructor:
This works alongside other parameters that we've used before. Setting a timezone while constructing an
Arrow instance works both for converting strings and
datetime objects and calling the constructor explicitly:
import arrow import datetime as datetime date = '2021.01.01 00:00' datetime = datetime.datetime(2021, 1, 1, 0, 0) arrow_1 = arrow.Arrow(2021, 1, 1, 0, 0, tzinfo='US/Pacific') arrow_2 = arrow.get(date, tzinfo='US/Pacific') arrow_3 = arrow.Arrow.fromdatetime(datetime, tzinfo='US/Pacific') print(arrow_1) print(arrow_2) print(arrow_3)
Arrow instances will be set to the
US/Pacific timezone, and set to the 1st of January, 2021:
2021-01-01T00:00:00-08:00 2021-01-01T00:00:00-08:00 2021-01-01T00:00:00-08:00
As you can see, the objects now have a
-08:00 suffix, since the
US/Pacific timezone is 8 hours behind UTC.
You can also extract the naive versions of these datetimes by calling their
print(arrow_1.naive) print(arrow_2.naive) print(arrow_3.naive)
This will not strip them off their timezone awareness, resulting in what you'd expect when you're working with naive
2021-01-01 00:00:00 2021-01-01 00:00:00 2021-01-01 00:00:00
Converting Between Timezones with Arrow
Another area worth looking at is how we can convert between different timezones with Arrow by using the
to() method. Whilst Arrow is fully compatible with timezone modules, there is no need to import any additional modules for timezone conversion.
To start with we can get the current time and assign it to a variable:
Now, let's convert this object to another timezone:
This results in:
Or the time in Hong Kong:
We can even specify the time difference with number of hours:
This is a much more intuitive way to convert between timezones, especially if you don't have a list of the appropriate names handy:
Humanizing and Shifting Dates
Oftentimes, when dealing with time spans, we don't really need a date. When talking to colleagues, we say:
"I went shopping yesterday".
"I went shopping on 2021/12/03", which is the day before 2021/12/04", which is the day right now.
In a lot of cases, you might want to humanize dates, such as annotating when a notification arrived, an email was sent or when someone performed a certain logged action. Thankfully, Arrow allows us to really easily humanize any date via the
This function works wonders with the
shift() function, which can shift the dates by
0...n days. You might want to make a system that notifies a person of when a certain date is coming up, in human-speak:
import arrow now = arrow.now() tomorrow = now.shift(days=1) yesterday = now.shift(days=-1) next_week = now.shift(days=7) notification = now.shift(days=-2, hours=-5, minutes=-7) print(now.humanize()) print(tomorrow.humanize()) print(yesterday.humanize()) print(next_week.humanize()) print(notification.humanize(granularity=['day', 'hour', 'minute']))
Here, we've created an
Arrow object using the
now() function. Then, we've created a series of objects by shifting the values up or down. The
shift() function accepts arguments such as
seconds. We've created a
yesterday object by shifting up and down by one day (not in-place), and an object for
next_week and an arbitrary
notification object that happened 2 days, 5 hours and 7 minutes ago.
Finally, when humanizing, you can specify the
granularity, which lets Arrow know how detailed to be when it comes to reporting time in human-speak. By default, it'll set the granularity to
week, depending on the time range. In our case, we've left the default settings on, except for the final object where we specifically want a bit of a finer granularity:
just now in a day a day ago in a week 2 days 5 hours and 7 minutes ago
This is a very intuitive and human way to represent dates to your user - such as counting down days to an event, or counting days from an event.
Advantages of Using Arrow
The advantages of using Arrow could be summarized with the official statements from their documentation:
Sensible and human-friendly approach.
Supporting many common creation scenarios.
Help you work with dates and times with fewer imports and a lot less code.
In these, it appears to succeed, and Arrow overcomes the major issues with the
datetime library. From what we have seen in our examples above Arrow is certainly an improvement in terms of:
- Reducing the need for importing multiple modules
- Working in just one data type (Arrow)
- Being timezone-aware
- Simplifying the creation of the most commonly used date and time functions
- Easy conversion between various types
- Helpful humanization functions
- Easily shifting values into the past and future
In this guide, we've focused on some of the benefits of using the Arrow library to work with date and time in Python. It's inspired by Moment.js, and offers solutions to some of the known problems of the
However, it is worth bearing in mind there are many other date and time modules available. It is also worth noting that being a built-in module gives
datetime the advantage of needing users pro-actively looking to replace it.
It all comes down to how important the date and time element is to your project and your code.