Is normalize still needed when converting to local time zones?

Asked by Dale on 2014-05-24

pytz's documentation contains this example when explaining that you need to use normalize() when converting to local time:

>>> utc_dt = utc.localize(datetime.utcfromtimestamp(1143408899))
>>> utc_dt.strftime(fmt)
'2006-03-26 21:34:59 UTC+0000'
>>> au_tz = timezone('Australia/Sydney')
>>> au_dt = au_tz.normalize(utc_dt.astimezone(au_tz))
>>> au_dt.strftime(fmt)
'2006-03-27 08:34:59 EST+1100'

However, if I don't use normalize(), I get the same result:

>>> utc_dt.astimezone(au_tz).strftime(fmt)
'2006-03-27 08:34:59 EST+1100'

normalize() is definitely still needed when doing arithmetic on local times, but I'm wondering if the code has changed over the years such that normalize() is no longer needed when merely converting to local time? It seems like pytz's fromutc() method has an opportunity to attach the correct tzinfo instance within astimezone(), such that normalize() afterwards wouldn't be necessary.

If normalize() is still needed, I would very much appreciate an example showing when it's necessary, so that I can include it in a write-up I'm doing of handling time zones in Python.

Thank you very much, and thank you for pytz!

Question information

English Edit question
pytz Edit question
No assignee Edit question
Last query:
Last reply:
Stuart Bishop (stub) said : #1

From the README:

    The second way of building a localized time is by converting an existing
    localized time using the standard ``astimezone()`` method:

    >>> ams_dt = loc_dt.astimezone(amsterdam)
    >>> ams_dt.strftime(fmt)
    '2002-10-27 12:00:00 CET+0100'

In your example, utc_dt is a localized time, so you can create au_dt using just utc_dt.astimezone(au_tz). There is no need for the normalize call. I think this dates back to 2008.

Dale (dsed) said : #2

Thanks for the response! My example was taken from the pytz documentation, where it says, "Converting between timezones also needs special attention. We also need to use the normalize() method to ensure the conversion is correct." So that I am sure I understand, are you saying that the documentation is incorrect on this point, and use of normalize is no longer necessary after astimezone, when merely converting between time zones?

Launchpad Janitor (janitor) said : #3

This question was expired because it remained in the 'Open' state without activity for the last 15 days.

T J (tjhnson) said : #4

Can we get an official response on this question? The documentation is confusing in light of the fact that not using normalize() seems to give the same answer.

jfs (jfs+lp) said : #5


1. pytz docs say that you don't need utc.normalize()
2. stdlib's .astimezone(tz) is documented to use utc internally

Therefore you don't need tz.normalize() after .astimezone(tz) for any pytz timezone, not just utc.


pytz uses stdlib's .astimezone() implementation that is *documented* to call tz.fromutc() [1]:

  def astimezone(self, tz):
      if self.tzinfo is tz:
          return self
      # Convert self to UTC, and attach the new time zone object.
      utc = (self - self.utcoffset()).replace(tzinfo=tz) # always works for an aware self
      # Convert from UTC to tz's local time.
      return tz.fromutc(utc) # always works for pytz tz (if in range for the tz db)


Thus this quote from the pytz docs [2] is incorrect: *"We also need to use the normalize() method to ensure the conversion is correct."* -- we do not "need" it.

pytz provides its own fromutc() implementation that finds the right tzinfo object for a given utc time i.e.,
*you don't need .normalize() after .astimezone().*

Note: if the source timezone is UTC then the pytz docs say explicitly that utc.normalize() is not necessary:

> You can take shortcuts when dealing with the UTC side of timezone conversions. normalize() and localize() are not really necessary when there are no daylight saving time transitions to deal with.

And the code example that uses utc.localize(), utc.normalize() is misleading according to this quote.

btw, the current .normalize() implementation does the same: 1. convert input dt to utc 2. call fromutc
i.e., it behaves like .astimezone() that only confirms that .normalize() after .astimezone() is redundant.


Usually, I would be hesitant to claim something about timezones categorically -- there are too many edge cases -- it is epicycle upon epicycle. But this case seems clear:

1. utc.localize(), utc.normalize() are not necessary -- the code example in the docs [2] could be updated to use a timezone with a variable utc offset instead
2. .normalize() after .astimezone() is not necessary: we do not need to use the normalize() method to ensure the conversion is correct -- the corresponding quote should be removed

Stuart Bishop (stub) said : #6

Documentation has been updated for 2016.4. When converting between timezones, .astimezone() should be all that is needed.