Timezones are an important part of every datetime library, and pendulum
tries to provide an easy and accurate system to handle them properly.
!!!note
The timezone system works best inside the `pendulum` ecosystem but
can also be used with the standard ``datetime`` library with a few limitations.
See [Using the timezone library directly](#using-the-timezone-library-directly).
When you create a DateTime instance, the library will normalize it for the
given timezone to properly handle any transition that might have occurred.
>>> import pendulum
>>> pendulum.datetime(2013, 3, 31, 2, 30, tz='Europe/Paris')
# 2:30 for the 31th of March 2013 does not exist
# so pendulum will return the actual time which is 3:30+02:00
'2013-03-31T03:30:00+02:00'
>>> pendulum.datetime(2013, 10, 27, 2, 30, tz='Europe/Paris')
# Here, 2:30 exists twice in the day so pendulum will
# assume that the transition already occurred
'2013-10-27T02:30:00+01:00'You can, however, control the normalization behavior:
>>> import pendulum
>>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris',
dst_rule=pendulum.PRE_TRANSITION)
'2013-03-31T01:30:00+01:00'
>>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris',
dst_rule=pendulum.PRE_TRANSITION)
'2013-10-27T02:30:00+02:00'
>>> pendulum.datetime(2013, 3, 31, 2, 30, 0, 0, tz='Europe/Paris',
dst_rule=pendulum.TRANSITION_ERROR)
# NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist
>>> pendulum.datetime(2013, 10, 27, 2, 30, 0, 0, tz='Europe/Paris',
dst_rule=pendulum.TRANSITION_ERROR)
# AmbiguousTime: The datetime 2013-10-27 02:30:00 is ambiguous.Note that it only affects instances at creation time. Shifting time around transition times still behaves the same.
So, what happens when you add time to a DateTime instance and stumble upon
a transition time?
Well pendulum, provided with the context of the previous instance, will
adopt the proper behavior and apply the transition accordingly.
>>> import pendulum
>>> dt = pendulum.datetime(2013, 3, 31, 1, 59, 59, 999999,
tz='Europe/Paris')
'2013-03-31T01:59:59.999999+01:00'
>>> dt = dt.add(microseconds=1)
'2013-03-31T03:00:00+02:00'
>>> dt.subtract(microseconds=1)
'2013-03-31T01:59:59.999999+01:00'
>>> dt = pendulum.datetime(2013, 10, 27, 2, 59, 59, 999999,
tz='Europe/Paris',
dst_rule=pendulum.PRE_TRANSITION)
'2013-10-27T02:59:59.999999+02:00'
>>> dt = dt.add(microseconds=1)
'2013-10-27T02:00:00+01:00'
>>> dt = dt.subtract(microseconds=1)
'2013-10-27T02:59:59.999999+02:00'You can easily change the timezone of a DateTime instance
with the in_timezone() method.
!!!note
You can also use the more concise ``in_tz()``
>>> in_paris = pendulum.datetime(2016, 8, 7, 22, 24, 30, tz='Europe/Paris')
'2016-08-07T22:24:30+02:00'
>>> in_paris.in_timezone('America/New_York')
'2016-08-07T16:24:30-04:00'
>>> in_paris.in_tz('Asia/Tokyo')
'2016-08-08T05:24:30+09:00'!!!warning
**You should avoid using the timezone library in Python < 3.6.**
This is due to the fact that Pendulum relies heavily on the presence
of the `fold` attribute which was introduced in Python 3.6.
The reason it works inside the Pendulum ecosystem is that it
backports the `fold` attribute in the `DateTime` class.
Like said in the introduction, you can use the timezone library
directly with standard datetime objects but with limitations, especially
when adding and subtracting time around transition times.
The value of the fold attribute will be used
by default to determine the transition rule.
>>> from datetime import datetime
>>> from pendulum import timezone
>>> paris = timezone('Europe/Paris')
>>> dt = datetime(2013, 3, 31, 2, 30)
# By default, fold is set to 0
>>> dt = paris.convert(dt)
>>> dt.isoformat()
'2013-03-31T01:30:00+01:00'
>>> dt = datetime(2013, 3, 31, 2, 30, fold=1)
>>> dt = paris.convert(dt)
>>> dt.isoformat()
'2013-03-31T03:30:00+02:00'Instead of relying on the fold attribute, you can use the dst_rule
keyword argument. This is especially useful if you want to raise errors
on non-existing and ambiguous times.
>>> import pendulum
>>> dt = datetime(2013, 3, 31, 2, 30)
# By default, fold is set to 0
>>> dt = paris.convert(dt, dst_rule=pendulum.PRE_TRANSITION)
>>> dt.isoformat()
'2013-03-31T01:30:00+01:00'
>>> dt = paris.convert(dt, dst_rule=pendulum.POST_TRANSITION)
>>> dt.isoformat()
'2013-03-31T03:30:00+02:00'
>>> paris.convert(dt, dst_rule=pendulum.TRANSITION_ERROR)
# NonExistingTime: The datetime 2013-03-31 02:30:00 does not existThis works as expected. However, whenever we add or subtract a timedelta
object, things get tricky.
>>> from datetime import datetime, timedelta
>>> from pendulum import timezone
>>> dt = datetime(2013, 3, 31, 1, 59, 59, 999999)
>>> dt = paris.convert(dt)
>>> dt.isoformat()
'2013-03-31T01:59:59.999999+01:00'
>>> dt = dt + timedelta(microseconds=1)
>>> dt.isoformat()
'2013-03-31T02:00:00+01:00'This is not what we expect. It should be 2013-03-31T03:00:00+02:00.
It is actually easy to retrieve the proper datetime by using convert()
again.
>>> dt = tz.convert(dt)
>>> dt.isoformat()
'2013-03-31T03:00:00+02:00'You can also get a normalized datetime object
from a Timezone by using the datetime() method:
>>> import pendulum
>>> tz = pendulum.timezone('Europe/Paris')
>>> dt = tz.datetime(2013, 3, 31, 2, 30)
>>> dt.isoformat()
'2013-03-31T03:30:00+02:00'