summaryrefslogtreecommitdiffstats
path: root/docs/docs/timezones.md
blob: 85ff1477ce06ac1bb2fb3a607fd134a221c4ac33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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'
```