56

I'm trying to understand why I'm getting these results when converting time zones to UTC:

In [74]: d1 = datetime(2007, 12, 5, 6, 30,tzinfo=pytz.timezone('US/Pacific'))
In [75]: d1
Out[75]: datetime.datetime(2007, 12, 5, 6, 30, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, **16:07:00 STD**>)
In [76]: d1.astimezone(pytz.utc)
Out[76]: datetime.datetime(2007, 12, 5, 14, 23, tzinfo=<UTC>)

Why did 6:30am become 2:23pm?

On the other hand, if I use the following approach, I get the expected result:

In [90]: d2 = datetime(2007, 12, 5, 6, 30)
In [91]: uspac = pytz.timezone('US/Pacific')
In [92]: d2_aware = uspac.localize(d2)
In [94]: d2_aware.astimezone(pytz.utc)
Out[94]: datetime.datetime(2007, 12, 5, 14, 30, tzinfo=<UTC>)
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
jxstanford
  • 3,339
  • 3
  • 27
  • 39
  • some older versions of this question: [Python datetime object show wrong timezone offset](https://stackoverflow.com/q/6410971/10197418), [Weird timezone issue with pytz](https://stackoverflow.com/q/11473721/10197418) – FObersteiner Jun 04 '23 at 10:20

5 Answers5

63

What I got is just a workaround, the simple rule is Never create datetime with timezone info by using datetime().

This sample would give you a hint for this. As you see, you could avoid the unexpected difference, once and only you make "naive" datetime (it is, datetime without timezone info) and then localize it (it is not applied when you create datetime on UTC though) :

import pytz
from datetime import datetime

# make Jan 1 on PDT -> UTC
pdt = pytz.timezone("America/Los_Angeles")
pdtnow1 = datetime(2014,1,1, tzinfo=pdt)
pdtnow2 = pdt.localize(datetime(2014,1,1))
pytz.utc.normalize(pdtnow1)
# > datetime.datetime(2014, 1, 1, 7, 53, tzinfo=<UTC>)
pytz.utc.normalize(pdtnow2)
# > datetime.datetime(2014, 1, 1, 8, 0, tzinfo=<UTC>)

# make Jan 1 on UTC -> PDT
utcnow1 = datetime(2014,1,1, tzinfo=pytz.utc)
utcnow2 = pytz.utc.localize(datetime(2014,1,1))
pdt.normalize(utcnow1)
# > datetime.datetime(2013, 12, 31, 16, 0, 
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
pdt.normalize(utcnow2)
# > datetime.datetime(2013, 12, 31, 16, 0, 
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
Kenial
  • 2,510
  • 23
  • 26
  • 12
    I thought I was losing my mind when I kept seeing my results off by a few minutes. Thank you so much for this example. – punkrockpolly May 19 '16 at 14:41
  • @punkrockpolly My pleasure! – Kenial May 26 '16 at 22:28
  • 4
    It was websearching for the offset 7:53 that helped me find this question. I suppose that, now that daylight savings have started for US Pacific time, seven minutes somehow got added to the offset, instead of an hour? How bizarre! – Michael Scheper Mar 14 '17 at 22:35
  • this part of python seems to be to messy to be pythonic for me – Qback Nov 30 '18 at 14:38
  • It's still problematic on convert timezone twice `pdt.localize(datetime(2014, 1, 1) \ .astimezone(other_timezone)` – Falldog Hsieh Mar 10 '19 at 15:38
  • Thanks. This is incredible. I couldn't understand why my code was 7 minutes off... – Aerodyno Aug 12 '19 at 20:46
  • If the year of your date is before 1901, you will still have issues with the minutes being off Eg: `pdt.localize(datetime(1900,1,1))` Source: https://www.reddit.com/r/Python/comments/2wd35a/comment/copuhtx/?utm_source=share&utm_medium=web2x&context=3 – Roshin Jay Jan 06 '22 at 02:51
43

From the partial documentation: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic

Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones. [...] It is safe for timezones without daylight saving transitions though, such as UTC. [...] The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.

  • Thanks! Couldn't figure this out for the life of me! – Inti Feb 20 '17 at 16:19
  • 1
    Unfortunately the statement "it is safe for timezones without daylight saving transitions" is too optimistic. It also fails for timezones whose fixed offset has changed over the years. Fortunately UTC hasn't changed. – Mark Ransom Jun 26 '20 at 18:16
9

Unfortunately, creating timezone aware dates using this method doesn't work.

If you are using Django, they have a utility function, make_aware, that does this correctly.

from django.utils.timezone import make_aware
from pytz import timezone

unaware_datetime = datetime(2007, 12, 5)
local_datetime = make_aware(datetime(2007, 12, 5))
specific_datetime = make_aware(datetime(2007, 12, 5), timezone("Australia/Melbourne"))

If you're not using Django, then the source code for the make_aware function may give you inspiration.

Shadow
  • 8,749
  • 4
  • 47
  • 57
4

I'm revisiting some questions about date and time to see if some of the newer libraries prove more helpful in situations like this (or not). pendulum is one that stores timezone with date and time, making it particularly valuable in situations such as this.

>>> import pendulum
>>> d1 = pendulum.datetime(2007,12,5,6,30, tzinfo='US/Pacific')
>>> d1
<Pendulum [2007-12-05T06:30:00-08:00]>
>>> d1.timezone
<Timezone [US/Pacific]>
>>> d1.astimezone(tz='UTC')
<Pendulum [2007-12-05T14:30:00+00:00]>

Lots of other sweet features too.

Bill Bell
  • 21,021
  • 5
  • 43
  • 58
0

Print d2_aware before .astimezone and you see PST-1 (Pacific Standard Time) but in first example you have LMT-1 (Local Mean Time) - and probably it can give 7 minutes difference.

But I don't know why pytz use different timezones.

furas
  • 134,197
  • 12
  • 106
  • 148