Skip to content

Commit

Permalink
Fix RecursionError when adding to a DateTime with a FixedTimezone (#431)
Browse files Browse the repository at this point in the history
This seems to have been caused by `bpo-32417`. Before that change,
adding a timedelta to a date/datetime subclass would always return an
instance of date/datetime instead of the subclass. After the change, the
subclass is preserved.

The RecursionError was caused by adding a timedelta to a DateTime. Doing
this uses the `convert` method of the DateTime's timezone to convert the
new DateTime into the correct timezone. In the case of FixedTimezones,
this requires adding the UTC offset of the timezone (a timedelta) to the
DateTime, causing the recursion.

Before bpo-32417, the subclass of the DateTime was dropped while calling
`astimezone`. This meant that the object that was passed into `fromutc`
by `astimezone` was a stdlib datetime, not a Pendulum DateTime. Calling
the stdlib datetime's add function would then do the addition and return
the result (which would then be upconverted back into a Pendulum
DateTime instance). Now, due to the subclass being preserved, the
Pendulum DateTime's add function is being called instead, causing the
recursion.

This commit fixes the RecursionError by always using the stdlib
datetime's addition function to add the offset to the DateTime when
calling fromutc.

bpo-32417: https://bugs.python.org/issue32417
commit: python/cpython@89427cd

Fixes #422
  • Loading branch information
pR0Ps authored and sdispater committed Jan 25, 2020
1 parent bc32743 commit e4cbd28
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 2 deletions.
3 changes: 2 additions & 1 deletion pendulum/tz/timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ def dst(self, dt): # type: Optional[datetime] # type: (...) -> timedelta
return timedelta()

def fromutc(self, dt): # type: (datetime) -> datetime
return (dt + self._utcoffset).replace(tzinfo=self)
# Use the stdlib datetime's add method to avoid infinite recursion
return (datetime.__add__(dt, self._utcoffset)).replace(tzinfo=self)

def tzname(self, dt): # type: Optional[datetime] # type: (...) -> Union[str, None]
return self._name
Expand Down
3 changes: 2 additions & 1 deletion tests/datetime/test_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ def test_addition_invalid_type():
def test_add_to_fixed_timezones():
dt = pendulum.parse("2015-03-08T01:00:00-06:00")
dt = dt.add(weeks=1)
dt = dt.add(hours=1)

assert_datetime(dt, 2015, 3, 15, 1, 0, 0)
assert_datetime(dt, 2015, 3, 15, 2, 0, 0)
assert dt.timezone_name == "-06:00"
assert dt.offset == -6 * 3600

Expand Down

1 comment on commit e4cbd28

@czchen
Copy link

@czchen czchen commented on e4cbd28 Feb 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sdispater Any plan for new release for this bug? This bug block us from upgrading to Python 3.8, so it would be nice for us to release new version of pendulum.

Please sign in to comment.