将UTC日期时间字符串转换为本地日期时间

320 投票
16 回答
616861 浏览
提问于 2025-04-16 10:26

我之前从来没有需要把时间转换成UTC(协调世界时间)或从UTC转换回来的经历。最近有人要求我的应用程序能够识别时区,这让我有点头疼。我找到很多关于如何把本地时间转换成UTC的信息,这部分我觉得还算简单(也许我做错了),但我找不到关于如何轻松地把UTC时间转换成用户所在时区的信息。

简单来说,一个安卓应用会给我(appengine应用)发送数据,这些数据中包含一个时间戳。为了把这个时间戳存储为UTC时间,我使用了:

datetime.utcfromtimestamp(timestamp)

这看起来是有效的。当我的应用存储数据时,存储的时间比我所在的时间快了5个小时(我在东部标准时间,UTC-5)。

这些数据存储在appengine的BigTable中,当我取出数据时,它会以字符串的形式显示出来,像这样:

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

我该如何把这个字符串转换成用户正确时区的日期时间呢?

另外,存储用户时区信息的推荐方式是什么?(通常你是如何存储时区信息的,比如“-5:00”或者“EST”等等?)我相信我第一个问题的答案可能会包含一个参数来回答第二个问题。

16 个回答

38

请查看datetime文档中关于tzinfo对象的部分。你需要自己实现想要支持的时区。文档底部有一些示例。

这里有一个简单的例子:

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
77

这里有一种可靠的方法,不依赖任何外部库:

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

这样可以避免DelboyJay例子中的时间问题,以及Erik van Oosten修改版中的一些小时间问题。

有趣的是,上面计算的时区偏移量可能和下面这个看起来相同的表达式不同,这可能是因为夏令时规则的变化:

offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!

更新: 这个代码片段的一个缺点是,它使用了当前时间的UTC偏移量,这可能和输入的日期时间的UTC偏移量不同。可以查看这个答案的评论,了解另一种解决方案。

为了处理不同的时间,可以从传入的时间中获取纪元时间。以下是我所做的:

def utc2local(utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp(epoch) - datetime.utcfromtimestamp(epoch)
    return utc + offset
584

如果你不想自己提供 tzinfo 对象,可以看看 python-dateutil 这个库。它在一个叫做 zoneinfo (Olson) 数据库 的基础上提供了 tzinfo 的实现,这样你就可以用比较标准的名字来引用时区规则。

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)

编辑 扩展了示例,展示了 strptime 的用法

编辑 2 修正了 API 的使用,展示了更好的入口方法

编辑 3 包含了时区的自动检测方法(Yarin)

撰写回答