50

What is the right way to convert a naive time and a tzinfo into an UTC time? Say I have:

d = datetime(2009, 8, 31, 22, 30, 30)
tz = timezone('US/Pacific')

First way, pytz inspired:

d_tz = tz.normalize(tz.localize(d))
utc = pytz.timezone('UTC')
d_utc = d_tz.astimezone(utc)

Second way, from UTCDateTimeField

def utc_from_localtime(dt, tz):
    dt = dt.replace(tzinfo=tz)
    _dt = tz.normalize(dt)
    if dt.tzinfo != _dt.tzinfo:
        # Houston, we have a problem...
        # find out which one has a dst offset
        if _dt.tzinfo.dst(_dt):
            _dt -= _dt.tzinfo.dst(_dt)
        else:
            _dt += dt.tzinfo.dst(dt)
    return _dt.astimezone(pytz.utc)

Needless to say those two methods produce different results for quite a few timezones.

Question is - what's the right way?

Art
  • 23,747
  • 29
  • 89
  • 101

4 Answers4

40

Your first method seems to be the approved one, and should be DST-aware.

You could shorten it a tiny bit, since pytz.utc = pytz.timezone('UTC'), but you knew that already :)

tz = timezone('US/Pacific')
def toUTC(d):
    return tz.normalize(tz.localize(d)).astimezone(pytz.utc)

print "Test: ", datetime.datetime.utcnow(), " = ", toUTC(datetime.datetime.now())
Adam Parkin
  • 17,891
  • 17
  • 66
  • 87
ewall
  • 27,179
  • 15
  • 70
  • 84
  • 1
    Why normalize()? is it really necessary? – kolypto Jan 30 '14 at 21:30
  • 4
    @kolypto: Apparently `tz.normalize()` will accounts for Daylight Savings Time and other nuisances that `tz.localize()` doesn't, as explained in other SO answers like [this one](https://stackoverflow.com/a/1379874/147320). – ewall Jul 09 '14 at 00:17
  • 2
    @kolypto: some local times do not exist e.g., when the local clock jumps forward in Spring during the transition to summer time in some countries (northern hemisphere). `tz.localize()` respects given `d` object (it doesn't change the time, it only tries to add an appropriate tzinfo object) therefore `tz.normalize()` is necessary to adjust non-existing times. Though both times (before/after the adjustment) should correspond to the same UTC time i.e., `tz.normalize()` might be unnecessary if all we do is converting the time to UTC as in this case. – jfs Oct 04 '14 at 07:10
8

What is the right way to convert a naive time and a tzinfo into an utc time?

This answer enumerates some issues with converting a local time to UTC:

from datetime import datetime
import pytz # $ pip install pytz

d = datetime(2009, 8, 31, 22, 30, 30)
tz = pytz.timezone('US/Pacific')

# a) raise exception for non-existent or ambiguous times
aware_d = tz.localize(d, is_dst=None)
## b) assume standard time, adjust non-existent times
#aware_d = tz.normalize(tz.localize(d, is_dst=False))
## c) assume DST is in effect, adjust non-existent times
#aware_d = tz.normalize(tz.localize(naive_d, is_dst=True))

# convert to UTC
utc_d = aware_d.astimezone(pytz.utc)
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

Use the first method. There's no reason to reinvent the wheel of timezone conversion

Jon W
  • 15,480
  • 6
  • 37
  • 47
-2
    import pytz
    from django.utils import timezone

    tz = pytz.timezone('America/Los_Angeles')
    time = tz.normalize(timezone.now())
Antony
  • 1,608
  • 1
  • 20
  • 27
  • 1
    it is incorrect. If `USE_TZ=True` then `timezone.now()` returns an aware datetime in UTC and therefore you should not call `tz.normalize()` on it. If `USE_TZ=False` then django uses default timezone that may be different from America/Los_Angeles and the code is wrong in this case too. – jfs Jul 23 '15 at 21:39