Converting timezone-aware datetime to local time in Python

kes picture kes · Mar 27, 2011 · Viewed 60.8k times · Source

How do you convert a timezone-aware datetime object to the equivalent non-timezone-aware datetime for the local timezone?

My particular application uses Django (although, this is in reality a generic Python question):

import iso8601

....

date_str="2010-10-30T17:21:12Z"

....

d = iso8601.parse_date(date_str)

foo = app.models.FooModel(the_date=d)
foo.save()

This causes Django to throw an error:

raise ValueError("MySQL backend does not support timezone-aware datetimes.")

What I need is:

d = iso8601.parse_date(date_str)
local_d = SOME_FUNCTION(d)
foo = app.models.FooModel(the_date=local_d)

What would SOME_FUNCTION be?

Answer

unutbu picture unutbu · Mar 27, 2011

In general, to convert an arbitrary timezone-aware datetime to a naive (local) datetime, I'd use the pytz module and astimezone to convert to local time, and replace to make the datetime naive:

In [76]: import pytz

In [77]: est=pytz.timezone('US/Eastern')

In [78]: d.astimezone(est)
Out[78]: datetime.datetime(2010, 10, 30, 13, 21, 12, tzinfo=<DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>)

In [79]: d.astimezone(est).replace(tzinfo=None)
Out[79]: datetime.datetime(2010, 10, 30, 13, 21, 12)

But since your particular datetime seems to be in the UTC timezone, you could do this instead:

In [65]: d
Out[65]: datetime.datetime(2010, 10, 30, 17, 21, 12, tzinfo=tzutc())

In [66]: import datetime

In [67]: import calendar

In [68]: datetime.datetime.fromtimestamp(calendar.timegm(d.timetuple()))
Out[68]: datetime.datetime(2010, 10, 30, 13, 21, 12)

By the way, you might be better off storing the datetimes as naive UTC datetimes instead of naive local datetimes. That way, your data is local-time agnostic, and you only convert to local-time or any other timezone when necessary. Sort of analogous to working in unicode as much as possible, and encoding only when necessary.

So if you agree that storing the datetimes in naive UTC is the best way, then all you'd need to do is define:

local_d = d.replace(tzinfo=None)