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.
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.
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.
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())
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
.
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
.
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.
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.