I’ve never had to convert time to and from utc. Recently had a request to have my app be timezone aware, and I’ve been running myself in circles. Lots of information on converting local time to utc, which I found fairly elementary (maybe I’m doing that wrong as well), but I can not find any information on easily converting the utc time to the end-users timezone.
In a nutshell, and android app sends me (appengine app) data and within that data is a timestamp. To store that timestamp to utc time I am using:
That seems to be working. When my app stores the data, it is being store as 5 hours ahead (I am EST -5)
The data is being stored on appengine’s BigTable, and when retrieved it comes out as a string like so:
How do I convert this string to a DateTime in the users correct time zone?
Also, what is the recommended storage for a users timezone information? (How do you typically store tz info ie: “-5:00” or “EST” etc etc ?) I’m sure the answer to my first question might contain a parameter the answers the second.
If you don’t want to provide your own
tzinfo objects, check out the python-dateutil library. It provides
tzinfo implementations on top of a zoneinfo (Olson) database such that you can refer to time zone rules by a somewhat canonical name.
from datetime import datetime from dateutil import tz # METHOD 1: Hardcode zones: from_zone = tz.gettz('UTC') to_zone = tz.gettz('America/New_York') # METHOD 2: Auto-detect zones: from_zone = tz.tzutc() to_zone = tz.tzlocal() # utc = datetime.utcnow() utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S') # Tell the datetime object that it's in UTC time zone since # datetime objects are 'naive' by default utc = utc.replace(tzinfo=from_zone) # Convert time zone central = utc.astimezone(to_zone)
Edit Expanded example to show
Edit 2 Fixed API usage to show better entry point method
Edit 3 Included auto-detect methods for timezones (Yarin)
Here’s a simple example:
from datetime import datetime,tzinfo,timedelta class Zone(tzinfo): def __init__(self,offset,isdst,name): self.offset = offset self.isdst = isdst self.name = name def utcoffset(self, dt): return timedelta(hours=self.offset) + self.dst(dt) def dst(self, dt): return timedelta(hours=1) if self.isdst else timedelta(0) def tzname(self,dt): return self.name GMT = Zone(0,False,'GMT') EST = Zone(-5,False,'EST') print datetime.utcnow().strftime('%m/%d/%Y %H:%M:%S %Z') print datetime.now(GMT).strftime('%m/%d/%Y %H:%M:%S %Z') print datetime.now(EST).strftime('%m/%d/%Y %H:%M:%S %Z') t = datetime.strptime('2011-01-21 02:37:21','%Y-%m-%d %H:%M:%S') t = t.replace(tzinfo=GMT) print t print t.astimezone(EST)
01/22/2011 21:52:09 01/22/2011 21:52:09 GMT 01/22/2011 16:52:09 EST 2011-01-21 02:37:21+00:00 2011-01-20 21:37:21-05:00a
Here’s a resilient method that doesn’t depend on any external libraries:
from datetime import datetime import time def datetime_from_utc_to_local(utc_datetime): now_timestamp = time.time() offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp) return utc_datetime + offset
This avoids the timing issues in DelboyJay’s example. And the lesser timing issues in Erik van Oosten’s amendment.
As an interesting footnote, the timezone offset computed above can differ from the following seemingly equivalent expression, probably due to daylight savings rule changes:
offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!
Update: This snippet has the weakness of using the UTC offset of the present time, which may differ from the UTC offset of the input datetime. See comments on this answer for another solution.
To get around the different times, grab the epoch time from the time passed in. Here’s what I do:
def utc2local (utc): epoch = time.mktime(utc.timetuple()) offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch) return utc + offset
If you want to get the correct result even for the time that corresponds to an ambiguous local time (e.g., during a DST transition) and/or the local utc offset is different at different times in your local time zone then use
#!/usr/bin/env python from datetime import datetime import pytz # $ pip install pytz import tzlocal # $ pip install tzlocal local_timezone = tzlocal.get_localzone() # get pytz tzinfo utc_time = datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)
If using django, you can use the timezone.localtime method (see https://docs.djangoproject.com/en/dev/topics/i18n/timezones/).
from django.utils import timezone date # datetime.datetime(2014, 8, 1, 20, 15, 0, 513000, tzinfo=<UTC>) timezone.localtime(date) # datetime.datetime(2014, 8, 1, 16, 15, 0, 513000, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
I traditionally defer this to the frontend — send times from the backend as timestamps or some other datetime format in UTC, then let the client figure out the timezone offset and render this data in the proper timezone.
Here is a quick and dirty version that uses the local systems settings to work out the time difference. NOTE: This will not work if you need to convert to a timezone that your current system is not running in. I have tested this with UK settings under BST timezone
from datetime import datetime def ConvertP4DateTimeToLocal(timestampValue): assert isinstance(timestampValue, int) # get the UTC time from the timestamp integer value. d = datetime.utcfromtimestamp( timestampValue ) # calculate time difference from utcnow and the local system time reported by OS offset = datetime.now() - datetime.utcnow() # Add offset to UTC time and return it return d + offset
You can use
calendar.timegm to convert your time to seconds since Unix epoch and
time.localtime to convert back:
import calendar import time time_tuple = time.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") t = calendar.timegm(time_tuple) print time.ctime(t)
Fri Jan 21 05:37:21 2011 (because I’m in UTC+03:00 timezone).
This answer should be helpful if you don’t want to use any other modules besides
datetime.utcfromtimestamp(timestamp) returns a naive
datetime object (not an aware one). Aware ones are timezone aware, and naive are not. You want an aware one if you want to convert between timezones (e.g. between UTC and local time).
If you aren’t the one instantiating the date to start with, but you can still create a naive
datetime object in UTC time, you might want to try this Python 3.x code to convert it:
import datetime d=datetime.datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") #Get your naive datetime object d=d.replace(tzinfo=datetime.timezone.utc) #Convert it to an aware datetime object in UTC time. d=d.astimezone() #Convert it to your local timezone (still aware) print(d.strftime("%d %b %Y (%I:%M:%S:%f %p) %Z")) #Print it with a directive of choice
Be careful not to mistakenly assume that if your timezone is currently MDT that daylight savings doesn’t work with the above code since it prints MST. You’ll note that if you change the month to August, it’ll print MDT.
Another easy way to get an aware
datetime object (also in Python 3.x) is to create it with a timezone specified to start with. Here’s an example, using UTC:
import datetime, sys aware_utc_dt_obj=datetime.datetime.now(datetime.timezone.utc) #create an aware datetime object dt_obj_local=aware_utc_dt_obj.astimezone() #convert it to local time #The following section is just code for a directive I made that I liked. if sys.platform=="win32": directive="%#d %b %Y (%#I:%M:%S:%f %p) %Z" else: directive="%-d %b %Y (%-I:%M:%S:%f %p) %Z" print(dt_obj_local.strftime(directive))
If you use Python 2.x, you’ll probably have to subclass
datetime.tzinfo and use that to help you create an aware
datetime object, since
datetime.timezone doesn’t exist in Python 2.x.