Search code examples
pythondjangodatetimepytzpython-datetime

Python Django Time Zone Conversion Incorrect Time for 'US/Pacific' Time Zone


While I read just about every post related to timezone conversions I'm still having some issues and my converted time is incorrect

settings.py

TIME_ZONE = 'UTC'
USE_TZ = True

views.py

utc = datetime.utcnow()
instance_time_zone = pytz.timezone(instance.timezone) # 'US/Pacific'
start_date = instance_time_zone.localize(datetime.utcnow(), is_dst=None)

template.html

utc: Oct. 2, 2015, 5:32 p.m. #correct time
start_date: Oct. 3, 2015, 1:32 a.m. #incorrect time

For some reason, the converted time is wrong and 15 hours ahead of the Pacific Time and 8 hours ahead of the UTC time.


Solution

  • timezone.localize() should be used for naive datetime objects (objects with no timezone of their own). The timezone is attached to that datetime as if the date and time are correct for that timezone. So in your case you 'localised' UTC as if it is your local time without DST, shifting it 8 hours in the wrong direction.

    You used a UTC timestamp however, so you need to attach the UTC timezone to that, then move the timestamp to the desired timezone:

    utc = pytz.utc.localize(datetime.utcnow())
    instance_time_zone = pytz.timezone(instance.timezone) # 'US/Pacific'
    start_date = utc.astimezone(instance_time_zone)
    

    Note that the utc value is now a datetime object with timezone, so you can then use the datetime.astimezone() method to produce a value in the desired target timezone from it.

    Demo:

    >>> from datetime import datetime
    >>> utc = pytz.utc.localize(datetime.utcnow())
    >>> utc
    datetime.datetime(2015, 10, 2, 17, 58, 10, 168575, tzinfo=<UTC>)
    >>> instance_time_zone = pytz.timezone('US/Pacific')
    >>> utc.astimezone(instance_time_zone)
    datetime.datetime(2015, 10, 2, 10, 58, 10, 168575, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
    

    Now the produced datetime is properly 5 hours removed from UTC.

    If you are outputting these values into a Django template, however, note that Django will also transform the timezone. See the Django timezone documentation, specifically the section on using aware datetime objects in templates:

    When you enable time zone support, Django converts aware datetime objects to the current time zone when they’re rendered in templates. This behaves very much like format localization.

    and from the current time zone section:

    You should set the current time zone to the end user’s actual time zone with activate(). Otherwise, the default time zone is used.

    It then doesn't matter what timezone you moved the datetime object to; it'll use whatever is the current timezone to display the value. You generally want to use aware datetime objects in the UTC timezone, then use activate() to switch what timezone everything is displayed in.

    So in Django, just use timezone.now() everywhere, and let the templating system worry about converting that to a given timezone.