Skip to content

Latest commit

 

History

History
193 lines (147 loc) · 5.71 KB

File metadata and controls

193 lines (147 loc) · 5.71 KB

Timezones

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).

Normalization

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.

Shifting time to transition

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'

Switching timezones

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'

Using the timezone library directly

!!!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 exist

This 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'