Wednesday, December 2, 2009

Supporting Timezones in Google App Engine Pt. 1 - Pytz

I am covering how I added Timezone support to my web application My Web Brain in a series of posts.  Hopefully someone will find the discussion useful or even better contribute ways to achieve the same effect.
This post is Part 1, introducing Pytz. In my previous post I described how some of the core python classes - datetime.datetime and datetime.time - support timezones out of the box. For example, you can create datetime object with a timezone and then convert it into any other timezone:

my_datetime_in_est = datetime.datetime(day=1,month=12,year=2009, tzinfo=est_tz)
my_datetime_in_pst = my_datetime_in_est.astimezone(pst_tz)

But where do these timezone information (tzinfo) objects come from? Well, you can write your own, defining the algorithm for incorporating a UTC offset and accounting for daylight savings time depending on the time of year (if applicable). But no sensible person wants to adopt the challenge of maintaining their own timezone information for one timezone, let alone for the entire world. It would be nice if you could simply tap into a maintained and open library that had all of the major timezones from around the world.

That is exactly what the pytz library provides. Pytz provides you with a capability to generate any timezone (ie. tzinfo object) it knows about by name:

import pytz
est_tz = pytz.timezone('US/Eastern')

#datetime in eastern standard time
est_datetime = datetime.datetime(day=1, month=12, year=2009, tzinfo=est_tz)

Where do the names come from? Pytz has two lists, all_timezones and common_timezones, which you can use to source a set of valid timezones. all_timezones contains a great number of timezones, including many defunct and ununsed zones. common_timezones is a smaller set of just the timezones still existing today.

To demonstrate, to generate all possible tzinfo objects for the common_timezones list (for caching, obviously - you would not want to do this for every request) and store it in a handy dictionary, you could write:

tzinfo_dict = dict(
[(tzname, pytz.timezone(tzname)) for tzname in pytz.common_timezones]

For me, new to Python on anything but App Engine, getting pytz was one of the more trial and error parts of implementing timezone support. The downloadable source code I found on places such as Source Forge was out of date (and as you can imagine, timezone information changes considerably not only year to year but month to month). The solution for me was to use the easy-install command (from Setuptools) to download the Python Egg file to my local installation then again to unpack the source to a local directory. From their I could cherry pick the library folder itself and add it to my Google App Engine application. From the command line, installing the lastest version of pytz is easy:

easy_install --upgrade pytz

The latest version as I write is 2009r.

Unfortunately, although I managed to unpack the egg file, I can not remember how I did it (and I just spent some time trying to recreate the feat). Hopefully someone will present a clue (or a completely better way of getting the library into my App Engine source tree).

On that slightly demoralising note I will conclude this post on pytz. The series on adding timezone support will continue, however. There I will discuss writing a wrapper class for the DateTimeProperty to ensure consistency for timezone aware applications.

See you then - any comments more than welcome.

No comments:

Post a Comment