Python's standard library (as of 2.7.2) provides a timezone framework in datetime.tzinfo but leaves the convenience of working with real timezones as an exercise for the reader. Following is a summarized fork of Armin Ronacher's advice in http://lucumr.pocoo.org/2011/7/15/eppur-si-muove.
Use a timezone module like pytz.
>>> import pytz >>> from datetime import datetime
Capture the current time in UTC as a naive datetime (in this context, 'naive' means that timezone information is not included in the datetime object):
>>> now = datetime.utcnow()
Here, a datetime is converted to and from a UNIX a timestamp to demonstrate the equivalence of the representations. 
>>> from calendar import timegm >>> ts = timegm( now.utctimetuple() ) >>> # store timestamp ts somewhere >>> assert now.replace(microsecond=0) == datetime.utcfromtimestamp(ts)
Store naive datetimes (or derivatives) and assume they are in UTC. It may help to use field names like utc_start_time and imply UTC values instead of field names like start_time and embed different timezones and offsets (and potentially different styles of timezones and offsets) within the values. 
After retrieving a naive datetime, format it for local use by first asserting its UTC pedigree before reflecting the universal time from another timezone's point of view.
>>> tmp = now.replace(tzinfo=pytz.timezone('UTC')) >>> hnl = tmp.astimezone(pytz.timezone('Pacific/Honolulu'))
The reason behind this approach boils down to: anything else is likely to bite. In general, use the *gm* and *utc* methods in the datetime, time, and calendar modules for gathering, storing, retrieving, and manipulating universal time.
Be aware of functions like datetime.fromtimestamp(), time.mktime(), and the hidden %s format in GNU's strftime  which all consult the local timezone.
The difference between utcfromtimestamp() and fromtimestamp():
>>> from time import timezone >>> now_utc_tuple = now.utctimetuple() >>> utc_timestamp = timegm(now_utc_tuple) >>> dt1 = datetime.utcfromtimestamp(utc_timestamp) >>> dt2 = datetime.fromtimestamp(utc_timestamp) >>> tdelta = (dt1 - dt2) if (dt1 > dt2) else (dt2 - dt1) >>> assert (timezone == 0) or (abs(tdelta.seconds) == abs(timezone))
The difference between UTC timestamps and timestamps that used time.mktime() or GNU's strftime() %s format:
>>> import time >>> diff2 = utc_timestamp - time.mktime(now_utc_tuple) >>> diff1 = utc_timestamp - int(now.strftime('%s')) >>> assert (timezone == 0) or (abs(diff1) == abs(diff2) == abs(timezone))
The equivalence of fromtimestamp() and GNU's strftime() %s format:
>>> out = datetime.fromtimestamp(float(now.strftime('%s.%f'))) >>> assert out == now
This example is modified from http://www.enricozini.org/2009/debian/using-python-datetime/. We derive a Europe/Rome time from a UTC time:
>>> utc = pytz.timezone('UTC') >>> rome = pytz.timezone('Europe/Rome') >>> a = datetime(2008, 7, 6, 5, 4, 3, tzinfo=utc) >>> b = a.astimezone(rome) >>> str(a) '2008-07-06 05:04:03+00:00' >>> str(b) '2008-07-06 07:04:03+02:00'
Rome uses a +1hr daylight savings and a normal UTC +1hr offset, therefore in July (month 7), the offset is two hours.
strftime() produces timestamps that differ by two hours:
>>> ts_stf = lambda t: int(t.strftime('%s')) >>> assert ts_stf(b) - ts_stf(a) == 7200
But there is no difference in the UTC timestamps:
>>> ts_utc = lambda t: timegm(t.utctimetuple()) >>> assert ts_utc(a) == ts_utc(b)
Distinguish these time representations:
- A - a naive datetime that may as well represent UTC
- B - an aware datetime that asserts a UTC timezone
- C - an aware datetime asserting the same year, month, day, hour, minute, and second values as A and B when they occurred in Rome
- D - an aware datetime that reflects the datetime in A and B from Rome's point of view
>>> A = datetime(2008, 7, 6, 5, 4, 3) >>> B = A.replace(tzinfo=utc) >>> C = A.replace(tzinfo=rome) >>> D = B.astimezone(rome) >>> # UTC timestamp comparison >>> assert ts_utc(A) == ts_utc(B) == ts_utc(D) >>> assert (B - C).seconds == 3600
A and B are essentially the same datetime and zone. Although D has different time values and a different timezone than A and B, it has been adjusted relative to UTC; D reflects the same universal time as A and B from Rome's point of view. C asserts a time in Rome that just so happens to have the same time values as A and B; the same time values in UTC are at least an hour behind. The one-hour (as opposed to two-hour) difference between C and B is because daylight savings played no role in C's definition; C was not adjusted relative to anything, its time is what was assigned to it - no more or less.
>>> # GNU strftime() timestamp comparison >>> assert ts_stf(A) == ts_stf(B) == ts_stf(C) >>> assert (ts_stf(D) - ts_stf(B)) == 7200
Above, the GNU strftime() isn't considering timezones. A, B, and C share the same time values and produce the same timestamp. D remains two hours ahead due to lingering one-hour offsets for both UTC and daylight savings.
From the Python 2.7.3 docs:
datetime.astimezone(tz) Return a datetime object with new tzinfo attribute tz, adjusting the date and time data so the result is the same UTC time as self, but in tz‘s local time. datetime.replace([year[, month[, day[, hour[, minute[, second [, microsecond[, tzinfo]]]]]]]]) Return a datetime with the same attributes, except for those attributes given new values by whichever keyword arguments are specified. Note that tzinfo=None can be specified to create a naive datetime from an aware datetime with no conversion of date and time data.
Because something as simple as an invalid system time has never  caused me hours of painful debugging, I rarely find a need for these tools that take mere seconds to run:
date hwclock ntpdate -q 0.pool.ntp.org rdate -p nist.time.nosc.us
If you feel the need to use these tools, you may be interested in the helpful services at http://support.ntp.org/bin/view/Servers/NTPPoolServers and http://tf.nist.gov/tf-cgi/servers.cgi.
|||With the exception of finding support in the calendar module, timestamps are often convenient. When they aren't, explicit translations with strftime() and strptime() are the next best approach. The %s format harped on above is a tempting, non-standard hazard. Discover more alternatives, including the elusive xml.util.iso8601 module, at http://wiki.python.org/moin/WorkingWithTime.|
|||A first_name field that stores first name values is often preferable to a name field that stores names plus a middle initial offset. :)|
|||Python's strftime() does not document a %s format option (seconds past the epoch) but it does use the system's implementation (if available) which in many cases is the GNU strftime(). See http://www.gnu.org/software/libc/manual/html_node/Formatting-Calendar-Time.html.|