_FixedOffset.normalize() doesn't correctly change the timezone

Bug #1595253 reported by Dale Hui
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
pytz
Fix Released
High
Stuart Bishop

Bug Description

In _FixedOffset:

    def normalize(self, dt, is_dst=False):
        '''Correct the timezone information on the given datetime'''
        if dt.tzinfo is None:
            raise ValueError('Naive time - no tzinfo set')
        return dt.replace(tzinfo=self)

should be:

    def normalize(self, dt, is_dst=False):
        '''Correct the timezone information on the given datetime'''
        if dt.tzinfo is self:
            return dt
        if dt.tzinfo is None:
            raise ValueError('Naive time - no tzinfo set')
        return dt.astimezone(self)

Revision history for this message
Stuart Bishop (stub) wrote :

In what circumstances does the current implementation fail? datetimes with a FixedOffset tzinfo should never need adjustment by normalize(),

Changed in pytz:
status: New → Incomplete
Revision history for this message
Dale Hui (dalehui) wrote :

We have some datetimes that stored in a custom format where the timezone offset is stored separately from the date and time. We're converting that timezone offset to a pytz compatible tzinfo using FixedOffset and constructing a native Python datetime object. We need a pytz compatible tzinfo so we can use normalize() and work with our datetime objects in UTC. We'd also like to be consistent with our usage of pytz compatible tzinfos and always use localize() and normalize().

Is there a better approach to this problem than using FixedOffset? Thanks!

Revision history for this message
Stuart Bishop (stub) wrote :

When calling mytzinfo.normalize(dt), if mytzinfo is a _FixedOffset then dt.tzinfo should always be the same _FixedOffset instance. normalize() is there to correct drift after datetime calculations, and not convert to a different timezone.

>>> from datetime import datetime
>>> from pytz import FixedOffset, timezone, UTC
>>> dt = datetime.now()
>>> offset = 7*60
>>> loc_tz = FixedOffset(offset)
>>> loc_dt = dt.replace(tzinfo=loc_tz))
>>> utc_dt = loc_dt.astimezone(UTC)
>>> loc_tz.normalize(loc_dt + timedelta(days=1))

Revision history for this message
Stuart Bishop (stub) wrote :

However, looking at the docstring for StaticTzInfo:

    def normalize(self, dt, is_dst=False):
       '''
       [...]
       The supported method of converting between timezones is to use
       datetime.astimezone(). Currently normalize() also works:
       [...]

So yes, I should use your implementation for consistency and to support normalize converting between timezones.

Changed in pytz:
status: Incomplete → Triaged
Stuart Bishop (stub)
Changed in pytz:
importance: Undecided → High
assignee: nobody → Stuart Bishop (stub)
status: Triaged → Fix Committed
Stuart Bishop (stub)
Changed in pytz:
status: Fix Committed → Fix Released
Revision history for this message
Dale Hui (dalehui) wrote :

Thanks for the quick fix!

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.