Asked  7 Months ago    Answers:  5   Viewed   99 times

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:

datetime.utcfromtimestamp(timestamp)

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:

"2011-01-21 02:37:21"

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.

 Answers

15

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 strptime usage

Edit 2 Fixed API usage to show better entry point method

Edit 3 Included auto-detect methods for timezones (Yarin)

Tuesday, June 1, 2021
 
letrollpoilu
answered 7 Months ago
86

Append 'UTC' to the string before converting it to a date in javascript:

var date = new Date('6/29/2011 4:52:48 PM UTC');
date.toString() // "Wed Jun 29 2011 09:52:48 GMT-0700 (PDT)"
Tuesday, June 1, 2021
 
uiroshan
answered 7 Months ago
57

Thanks @rofly, the full conversion from string to string is as follows:

time.strftime("%Y-%m-%d %H:%M:%S", 
              time.gmtime(time.mktime(time.strptime("2008-09-17 14:04:00", 
                                                    "%Y-%m-%d %H:%M:%S"))))

My summary of the time/calendar functions:

time.strptime
string --> tuple (no timezone applied, so matches string)

time.mktime
local time tuple --> seconds since epoch (always local time)

time.gmtime
seconds since epoch --> tuple in UTC

and

calendar.timegm
tuple in UTC --> seconds since epoch

time.localtime
seconds since epoch --> tuple in local timezone

Tuesday, June 1, 2021
 
Hexaholic
answered 7 Months ago
15

The key thing to notice when working with datetime objects and their POSIX timestamps at the same time is that naive datetime objects (the ones without timezone information) are assumed by Python to refer to local time (OS setting). In contrast, a POSIX timestamp (should) always refer to UTC seconds since the epoch. You can unambiguously obtain it by time.time(). In your example, not-so-obvious things happen:

1) datetime.now().timestamp() - now() gives you a naive datetime object that resembles local time. If you call for the timestamp(), Python converts the datetime to UTC and calculates the timestamp for that.

2) datetime.utcnow().timestamp() - utcnow() gives you a naive datetime object that resembles UTC. However, if you call timestamp(), Python assumes (since naive) that the datetime is local time - and converts to UTC again before calculating the timestamp! The resulting timestamp is therefore off from UTC by twice your local time's UTC offset...

A code example. Let's make some timestamps. Note that I'm on UTC+2, so offset is -7200 s.

import time
from datetime import datetime, timezone

ts_ref = time.time() # reference POSIX timestamp

ts_utcnow = datetime.utcnow().timestamp() # dt obj UTC but naive - so also assumed local

ts_now = datetime.now().timestamp() # dt obj naive, assumed local

ts_loc_utc = datetime.now(tz=timezone.utc).timestamp() # dt obj localized to UTC

print(int(ts_utcnow - ts_ref))
# -7200 # -> ts_utcnow doesn't refer to UTC!
print(int(ts_now - ts_ref))
# 0 # -> correct
print(int(ts_loc_utc - ts_ref))
# 0 # -> correct

I hope this clarifies that if you do datetime.utcfromtimestamp(ts_utcnow), you get double the local time's UTC offset. Python assumes (which I think is pretty sane) that the timestamp refers to UTC - which in fact, it does not.

My suggestion would be to use timezone-aware datetime objects; like datetime.now(tz=timezone.utc). The dateutil library can also be very helpful when working with datetime and timezones. And if you want to dig deep, have a look at the datetime src code. That could also help clarifying the issue you encountered.

Wednesday, August 25, 2021
 
Timur Mustafaev
answered 4 Months ago
43

In your RowCreatedEvent, you should be able to use the function: TimeZoneInfo.ConvertTimeFromUtc

See the Microsoft documentation for some good examples (in both Vb & C#):

http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttimefromutc.aspx

Thursday, August 26, 2021
 
ancad
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share