diff options
Diffstat (limited to 'docs/docs/timezones.md')
-rw-r--r-- | docs/docs/timezones.md | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/docs/docs/timezones.md b/docs/docs/timezones.md new file mode 100644 index 0000000..85ff147 --- /dev/null +++ b/docs/docs/timezones.md @@ -0,0 +1,193 @@ +# 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. + +```python +>>> 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: + +```python +>>> 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. + +```python +>>> 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.999998+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()`` + +```python +>>> 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. + +```python +>>> 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. + +```python +>>> 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. + +```python +>>> 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. + +```python +>>> 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: + +```python +>>> import pendulum + +>>> tz = pendulum.timezone('Europe/Paris') +>>> dt = tz.datetime(2013, 3, 31, 2, 30) +>>> dt.isoformat() +'2013-03-31T03:30:00+02:00' +``` |