Skip to content

Commit 88bf52d

Browse files
committed
Makes PRE_TRANSITION rule no longer create a time in a DST gap.
1 parent bf3f442 commit 88bf52d

File tree

5 files changed

+23
-13
lines changed

5 files changed

+23
-13
lines changed

docs/_docs/timezones.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ given timezone to properly handle any transition that might have occurred.
4343
pendulum.set_transition_rule(pendulum.PRE_TRANSITION)
4444
4545
pendulum.create(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris')
46-
'2013-03-31T02:30:00+01:00'
46+
'2013-03-31T01:30:00+01:00'
4747
pendulum.create(2013, 10, 27, 2, 30, 0, 0, 'Europe/Paris')
4848
'2013-10-27T02:30:00+02:00'
4949
@@ -75,7 +75,7 @@ given timezone to properly handle any transition that might have occurred.
7575
7676
dt = Pendulum(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris', fold=0)
7777
dt.isoformat()
78-
'2013-03-31T02:30:00+01:00'
78+
'2013-03-31T01:30:00+01:00'
7979
8080
dt = Pendulum(2013, 3, 31, 2, 30, 0, 0, 'Europe/Paris', fold=1)
8181
dt.isoformat()
@@ -94,7 +94,7 @@ Shifting time to transition
9494

9595
So, what happens when you add time to a ``Pendulum`` instance and stumble upon
9696
a transition time?
97-
Well, ``Pendulum``, provided with the context of the previous instance, will
97+
Well ``Pendulum``, provided with the context of the previous instance, will
9898
adopt the proper behavior and apply the transition accordingly.
9999

100100
.. code-block:: python
@@ -161,7 +161,7 @@ when adding and subtracting time around transition times.
161161
# By default, fold is set to 0
162162
dt = paris.convert(dt)
163163
dt.isoformat()
164-
'2013-03-31T02:30:00+01:00'
164+
'2013-03-31T01:30:00+01:00'
165165
166166
dt = datetime(2013, 3, 31, 2, 30, fold=1)
167167
dt = paris.convert(dt)
@@ -229,7 +229,7 @@ when adding and subtracting time around transition times.
229229
dt = datetime(2013, 3, 31, 2, 30)
230230
dt = tz.convert(dt, dst_rule=tz.PRE_TRANSITION)
231231
dt.isoformat()
232-
'2013-03-31T02:30:00+01:00'
232+
'2013-03-31T01:30:00+01:00'
233233
tz.convert(dt, dst_rule=tz.TRANSITION_ERROR)
234234
# NonExistingTime: The datetime 2013-03-31 02:30:00 does not exist.
235235

pendulum/tz/timezone.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def _normalize(self, dt, dst_rule=None):
219219
elif dst_rule == self.PRE_TRANSITION:
220220
# We do not apply the transition
221221
(unix_time,
222-
tzinfo_index) = self._get_previous_transition_time(tr, dt)
222+
tzinfo_index) = self._get_previous_transition_time(tr, dt, skipped=True)
223223
else:
224224
unix_time = tr.unix_time - (tr.pre_time - dt).total_seconds()
225225
elif tr is end:
@@ -246,7 +246,7 @@ def _normalize(self, dt, dst_rule=None):
246246
elif dst_rule == self.PRE_TRANSITION:
247247
# We do not apply the transition
248248
(unix_time,
249-
tzinfo_index) = self._get_previous_transition_time(tr, dt)
249+
tzinfo_index) = self._get_previous_transition_time(tr, dt, skipped=True)
250250
else:
251251
unix_time = tr.unix_time - (tr.pre_time - dt).total_seconds()
252252
elif tr.time <= dt <= tr.pre_time:
@@ -349,7 +349,7 @@ def _find_transition_index(self, dt, prop='_time'):
349349

350350
return lo
351351

352-
def _get_previous_transition_time(self, tr, dt):
352+
def _get_previous_transition_time(self, tr, dt, skipped=False):
353353
"""
354354
Returns the time before the transition
355355
as a (unix_time, tzinfo_index) tuple.
@@ -360,6 +360,9 @@ def _get_previous_transition_time(self, tr, dt):
360360
:param dt: The datetime
361361
:type dt: datetime
362362
363+
:param skipped: Whether we are in a gap or not
364+
:type skipped: bool
365+
363366
:rtype: tuple
364367
"""
365368
diff = self._get_diff(tr.pre_time, dt)
@@ -370,6 +373,13 @@ def _get_previous_transition_time(self, tr, dt):
370373

371374
unix_time = tr.unix_time + diff
372375

376+
if skipped:
377+
# If skipped time, we round down
378+
# Only occurs when PRE_TRANSITION is used
379+
forward = (self._tzinfos[tr.tzinfo_index].offset
380+
- self._tzinfos[tzinfo_index].offset)
381+
unix_time -= forward
382+
373383
return unix_time, tzinfo_index
374384

375385
def tzname(self, dt):

tests/pendulum_tests/test_construct.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,12 @@ def test_init_fold_is_honored_if_explicit(self):
196196
Pendulum.set_transition_rule(PRE_TRANSITION)
197197

198198
d = Pendulum(2013, 3, 31, 2, 30, tzinfo='Europe/Paris')
199-
self.assertPendulum(d, 2013, 3, 31, 2, 30)
199+
self.assertPendulum(d, 2013, 3, 31, 1, 30)
200200

201201
Pendulum.set_transition_rule(POST_TRANSITION)
202202

203203
d = Pendulum(2013, 3, 31, 2, 30, tzinfo='Europe/Paris', fold=0)
204-
self.assertPendulum(d, 2013, 3, 31, 2, 30)
204+
self.assertPendulum(d, 2013, 3, 31, 1, 30)
205205

206206
d = Pendulum(2013, 3, 31, 2, 30, tzinfo='Europe/Paris', fold=1)
207207
self.assertPendulum(d, 2013, 3, 31, 3, 30)

tests/pendulum_tests/test_fluent_setters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_replace_tzinfo_dst_with_pre_transition_rule(self):
174174
d = Pendulum.create(2013, 3, 31, 2, 30)
175175
new = d.replace(tzinfo='Europe/Paris')
176176

177-
self.assertPendulum(new, 2013, 3, 31, 2, 30)
177+
self.assertPendulum(new, 2013, 3, 31, 1, 30)
178178
self.assertFalse(new.is_dst)
179179
self.assertEqual(new.offset, 3600)
180180
self.assertEqual(new.timezone_name, 'Europe/Paris')

tests/tz_tests/test_timezone.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_skipped_time_with_pre_rule(self):
5454
self.assertEqual(2013, dt.year)
5555
self.assertEqual(3, dt.month)
5656
self.assertEqual(31, dt.day)
57-
self.assertEqual(2, dt.hour)
57+
self.assertEqual(1, dt.hour)
5858
self.assertEqual(30, dt.minute)
5959
self.assertEqual(45, dt.second)
6060
self.assertEqual(123456, dt.microsecond)
@@ -143,7 +143,7 @@ def test_create_uses_transition_rule(self):
143143
self.assertEqual(2013, dt.year)
144144
self.assertEqual(3, dt.month)
145145
self.assertEqual(31, dt.day)
146-
self.assertEqual(2, dt.hour)
146+
self.assertEqual(1, dt.hour)
147147
self.assertEqual(30, dt.minute)
148148
self.assertEqual(45, dt.second)
149149
self.assertEqual(123456, dt.microsecond)

0 commit comments

Comments
 (0)