Timezone lookup and conversion

Most datetime objects returned from the dateutil parser are naïve, meaning they don't have an explicit tzinfo, which specifies the timezone and UTC offset. In the previous recipe, only one of the examples had a tzinfo, and that's because it's in the standard ISO format for UTC datetime strings. UTC is the coordinated universal time, and is basically the same as GMT. ISO is the International Standards Organization, which among other things, specifies standard datetime formatting.

Python datetime objects can either be naïve or aware. If a datetime object has a tzinfo, then it is aware. Otherwise, the datetime is naïve. To make a naïve datetime object timezone aware, you must give it an explicit tzinfo. However, the Python datetime library only defines an abstract baseclass for tzinfo, and leaves it up to others to actually implement tzinfo creation. This is where the tz module of dateutil comes in—it provides everything you need to look up timezones from your OS timezone data.

Getting ready

dateutil should be installed using pip or easy_install. You should also make sure your operating system has timezone data. On Linux, this is usually found in /usr/share/zoneinfo, and the Ubuntu package is called tzdata. If you have a number of files and directories in /usr/share/zoneinfo, such as America/ and Europe/, then you should be ready to proceed. The upcoming examples show directory paths for Ubuntu Linux.

How to do it...

Let's start by getting a UTC tzinfo object. This can be done by calling tz.tzutc(), and you can check that the offset is 0 by calling the utcoffset() method with a UTC datetime object:

>>> from dateutil import tz
>>> tz.tzutc()
tzutc()
>>> import datetime
>>> tz.tzutc().utcoffset(datetime.datetime.utcnow())
datetime.timedelta(0)

To get tzinfo objects for other timezones, you can pass in a timezone file path to the gettz() function:

>>> tz.gettz('US/Pacific')
tzfile('/usr/share/zoneinfo/US/Pacific')
>>> tz.gettz('US/Pacific').utcoffset(datetime.datetime.utcnow())
datetime.timedelta(-1, 61200)
>>> tz.gettz('Europe/Paris')
tzfile('/usr/share/zoneinfo/Europe/Paris')
>>> tz.gettz('Europe/Paris').utcoffset(datetime.datetime.utcnow())
datetime.timedelta(0, 7200)

You can see that the UTC offsets are timedelta objects, where the first number is days and the second number is seconds.

Tip

If you're storing datetimes in a database, it's a good idea to store them all in UTC to eliminate any timezone ambiguity. Even if the database can recognize timezones, it's still good practice.

To convert a non-UTC datetime object to UTC, it must be made timezone aware. If you try to convert a naïve datetime to UTC, you'll get a ValueError exception. To make a naïve datetime timezone aware, you simply call the replace() method with the correct tzinfo. Once a datetime object has a tzinfo, then UTC conversion can be performed by calling the astimezone() method with tz.tzutc().

>>> pst = tz.gettz('US/Pacific')
Y
>>> dt = datetime.datetime(2010, 9, 25, 10, 36)
>>> dt.tzinfo
>>> dt.astimezone(tz.tzutc())
Traceback (most recent call last):
  File "/usr/lib/python2.6/doctest.py", line 1248, in __run
  compileflags, 1) in test.globs
  File "<doctest __main__[22]>", line 1, in <module>
  dt.astimezone(tz.tzutc())
ValueError: astimezone() cannot be applied to a naive datetime
>>> dt.replace(tzinfo=pst)
datetime.datetime(2010, 9, 25, 10, 36, tzinfo=tzfile('/usr/share/zoneinfo/US/Pacific'))
>>> dt.replace(tzinfo=pst).astimezone(tz.tzutc())
datetime.datetime(2010, 9, 25, 17, 36, tzinfo=tzutc())

Note

The tzfile paths vary across operating systems, so your tzfile paths may differ from the examples. There is no cause for concern, unless you are getting different datetime values.

How it works...

The tzutc and tzfile objects are both subclasses of tzinfo. As such, they know the correct UTC offset for timezone conversion (which is 0 for tzutc). A tzfile object knows how to read your operating system's zoneinfo files to get the necessary offset data. The replace() method of a datetime object does what the name implies—it replaces attributes. Once a datetime has a tzinfo, the astimezone() method will be able to convert the time using the UTC offsets, and then replace the current tzinfo with the new tzinfo.

Note

Note that both replace() and astimezone() return new datetime objects. They do not modify the current object.

There's more...

You can pass a tzinfos keyword argument into the dateutil parser to detect the otherwise unrecognized timezones:

>>> parser.parse('Wednesday, Aug 4, 2010 at 6:30 p.m. (CDT)', fuzzy=True)
datetime.datetime(2010, 8, 4, 18, 30)
>>> tzinfos = {'CDT': tz.gettz('US/Central')}
>>> parser.parse('Wednesday, Aug 4, 2010 at 6:30 p.m. (CDT)', fuzzy=True, tzinfos=tzinfos)
datetime.datetime(2010, 8, 4, 18, 30, tzinfo=tzfile('/usr/share/zoneinfo/US/Central'))

In the first instance, we get a naïve datetime since the timezone is not recognized. But when we pass in the tzinfos mapping, we get a timezone-aware datetime.

Local timezone

If you want to look up your local timezone, you can call tz.tzlocal(), which will use whatever your operating system thinks is the local timezone. In Ubuntu Linux, this is usually specified in the /etc/timezone file.

Custom offsets

You can create your own tzinfo object with a custom UTC offset using the tzoffset object. A custom offset of 1 hour could be created as follows:

>>> tz.tzoffset('custom', 3600)
tzoffset('custom', 3600)

You must provide a name as the first argument and the offset time in seconds as the second argument.

See also

The previous recipe, Parsing dates and times with dateutil, covers parsing datetime strings with dateutil.parser.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset