👉 Latest post:
"Why .every() on an empty list is true"
By Vincent Driessen
on Thursday, February 02, 2012

Lately I’ve been getting sick of working with datetimes and timezones in Python. The standard library offers many different conversion routines, but does not prescribe a best practice way to deal with them. Luckily, Armin Ronacher did in his article Dealing with Timezones in Python.

The summary is to never ever work with local datetimes. When a local datetime is input, immediately convert it to universal time and only ever store or calculate with those. Only when presenting datetimes to the end user, convert them to local time again.

This seems simple enough, alright. But to actually do it in Python, you still have to think about how to implement it correctly. Every. Single. Time. pytz does help a bit here, but it still isn’t trivial. It should be.

Meet Times, a very small Python library to deal with conversions from universal to local timezones and vice versa. It’s focused on simplicity and opinionated about what is good practice.

Example use

Imagine you’re building a web app that allows your users to set an alarm. Say that someone in the Netherlands sets an alarm to 9:30 am. You can use times to simplify this:

>>> import times
>>> import datetime
>>> 
>>> local_time = datetime.datetime(2012, 2, 3, 9, 30, 0)
>>> universal_time = times.to_universal(local_time, 'Europe/Amsterdam')
>>> universal_time
datetime.datetime(2012, 2, 3, 8, 30)

Now, this universal_time variable is safe to store or calculate with.

Once you want to show this date to the user again, simply format it for the given timezone:

>>> times.format(universal_time, 'Europe/Amsterdam') 
'2012-02-03 09:30:00+0100'

If your app allows users to share alerts, it is just as easy to present the alert date to an end user in New Zealand as well:

>>> times.format(universal_time, 'Pacific/Auckland') 
'2012-02-03 21:30:00+1300'

Current time

If you ever need to record the current time, you can use

>>> times.now()
datetime.datetime(2012, 2, 2, 16, 4, 40, 283090)

Which is actually just an alias to datetime.datetime.utcnow().

Converting from other sources

I’ve added the ability to create universal times from two other sources: UNIX timestamps and date strings. To use any of these, simply pass them to the to_universal function, like so:

>>> time.time()
1328729274.982
>>> times.to_universal(1328729274.982)
datetime.datetime(2012, 2, 8, 19, 27, 54, 982000)

Note that UNIX timestamps must be in UTC (which the output of time.time() is). Local UNIX timestamps are not accepted.

To create universal times from string representations, Times uses the advanced parser from the python-dateutil library. Time zones are automatically recognized if such info is encoded in the string representation. In any other case, you are required to provide it explicitly. Two examples to illustrate both variants:

>>> # Timezone-aware date formats don't require a source timezone
>>> date_str = '2012-02-08 19:27:54+0100'
>>> times.to_universal(date_str)
datetime.datetime(2012, 2, 8, 18, 27, 54)

>>> # Timezone-less date formats require an explicit source timezone
>>> date_str = '2012-02-08 19:27:54'
>>> times.to_universal(date_str, 'Asia/Singapore')
datetime.datetime(2012, 2, 8, 11, 27, 54)

Installing

Times is on PyPI (link), so just pip install times to use it.

Of course, you can fork me on GitHub.

As usual, Times is licensed under the liberal terms of the BSD license.

Other posts on this blog

If you want to get in touch, I'm @nvie on Twitter.