Python:将UTC时间元组转换为UTC时间戳
我的问题是:我需要把一个UTC时间元组转换成UTC时间戳,但我有些困惑。
先给点背景信息:
time.mktime(tuple)
:这个函数总是返回本地时间的时间戳。这是
localtime()
的反向函数。它的参数是一个结构化时间或完整的9元组,表示的是本地时间,而不是UTC时间。calendar.timegm(tuple)
:这个函数返回提供的时间元组对应的UTC时间戳。它接受一个时间元组,比如
gmtime()
函数返回的,然后返回相应的Unix时间戳值。实际上,time.gmtime()
和timegm()
是彼此的反向函数。
现在我们来做个测试:
>>> from datetime import datetime
>>> import time
>>> import calendar as cal
>>> utc_now = datetime.utcnow()
>>> now = datetime.now()
>>> utc_now
datetime.datetime(2013, 3, 16, 9, 17, 22, 489225)
>>> now
datetime.datetime(2013, 3, 16, 5, 17, 29, 736903)
>>> time.mktime(datetime.timetuple(utc_now)), time.mktime(datetime.timetuple(now))
(1363439842.0, 1363425449.0)
>>> cal.timegm(datetime.timetuple(utc_now)), cal.timegm(datetime.timetuple(now))
(1363425442, 1363411049)
为什么会有四个不同的值?当我想把一个UTC时间元组转换成UTC时间戳时,哪个才是正确的?
更新
我想我找到了困惑的答案,让我来解释一下。
首先,我们需要知道一些重要的事情:
日期和时间对象有两种类型:“天真”(naive)和“有意识”(aware)。
有意识的对象对适用的算法和政治时间调整有足够的了解,比如时区和夏令时信息,可以相对于其他有意识的对象定位自己。有意识的对象用于表示一个特定的时间点,不会有歧义。
而天真的对象则没有足够的信息来明确地定位自己相对于其他日期/时间对象。一个天真的对象是否表示协调世界时(UTC)、本地时间或其他时区的时间,完全取决于程序,就像程序决定一个特定的数字表示米、英里还是质量一样。天真的对象容易理解和使用,但忽略了一些现实方面。
从datetime.utcnow()
或datetime.now()
得到的都是“天真”对象。这意味着返回的datetime
对象并没有说明任何关于你的本地时间或UTC时间的信息——它只是表示“某个时间”。它只是封装了日期和时间信息(年、月、日、小时、分钟、秒等)。你需要自己去关联它是本地时间还是UTC时间。
所以,记住,天真的日期时间对象只是表示“某个时间”。datetime.now()
函数返回的是与你当前时间相等的“某个时间”,而datetime.utcnow()
函数返回的是格林威治(即UTC)当前的“某个时间”。
这个“某个时间”只是一个日期和时间的值。注意,在地球上的不同地点,这个“某个时间”会在不同的时间发生。例如,如果“某个时间”的值是1月1日10:30,那么在格林威治的当前时间会比纽约的当前时间早5小时。
因此,我们可以看到有两件事:一个通用的“某个时间”值,以及这个“某个时间”在不同地点会在不同的“时间”变成当前时间。(没有双关,继续阅读)
现在,让我们先定义一下“纪元”(epoch)。我们知道“某个时间”只是一个通用的时间值。那么,纪元是一个在格林威治发生的“某个时间”,其参数值为:1970年1月1日00:00:00
。
一个“时间戳”是自纪元以来经过的秒数。这意味着在格林威治的时间是1970年1月1日00:00:00
时,时间戳为0
。但在纽约的时间戳大约是(5 * 60 * 60)
秒。
>>> tt = datetime.timetuple(datetime(1970, 1, 1, 0, 0, 0))
>>> cal.timegm(tt)
0
因此,我们可以看到同一个“某个时间”值1970年1月1日00:00:00
在不同地点会有不同的时间戳。因此,当你谈论时间戳时,你还需要说明“这个时间戳与哪个地点相关”,以及这个地点与格林威治的关系是向东还是向西。这个地点用“时区”来表示。
现在,每个系统(计算机)都有一个配置好的时区,所有与该时区相关的时间戳实际上都成为了“本地”时间。UTC是全球的参考。
假设你有一个X
值表示“某个时间”,它转换为:
Y
是你本地时间的时间戳Z
是UTC的时间戳
这意味着要让“某个时间”变成你所在位置的当前时间,需要经过Y
秒,而要让格林威治的当前时间变成“某个时间”,需要经过Z
秒。
最后,回到我们的函数mktime
和timegm
。它们接受一个时间元组,这只是“某个时间”的另一种表示。记住,我们传递给它们的是一个天真的时间,没有本地或UTC的概念。
假设X
是一个表示天真的“某个时间”的时间元组。那么:
mktime(X)
将返回需要经过的秒数,以便你的本地当前时间变成那个“某个时间”;timegm(X)
将返回需要经过的秒数,以便格林威治的当前时间变成那个“某个时间”。
在上面的例子中,now
和utc_now
表示天真的“某个时间”,当我们把这些“某个时间”的值传递给mktime
和timegm
时,它们简单地返回了对应地点(你的地点和格林威治)当前时间变成那个“某个时间”所需经过的秒数。
最后,回到我的问题:我需要把一个UTC时间元组转换成UTC时间戳。
首先,没有“UTC时间元组”这个概念——它只是“某个时间”。如果我需要把它转换为UTC,我只需使用timegm
:
cal.timegm(datetime.timetuple(utc_now))
这将给我当前UTC时间的时间戳(即格林威治的当前“某个时间”)。
1 个回答
实际上,这里只有三个不同的值。这两个值:
1363425449.0 (time.mktime(datetime.timetuple(now))
1363425442 (cal.timegm(datetime.timetuple(utc_now)))
只相差7秒,这就是你最开始查看变量时看到的情况:
>>> utc_now
datetime.datetime(2013, 3, 16, 9, 17, 22, 489225)
>>> now
datetime.datetime(2013, 3, 16, 5, 17, 29, 736903)
(注意输出中的秒数部分22和29的区别。)
另外两个值其实是错误的,因为你传入了错误类型的参数——你用的是UTC时间值去调用time.mktime
,而用本地时间值去调用cal.timegm
。文档里已经清楚说明了需要什么样的值,所以一定要确保你使用的是正确的值。你看到的本地时间偏移(看起来是4小时)在这里不应该被应用(而且根据错误的不同方向,偏移的方向也会不同)。
在处理这种问题时,使用epochconverter.com会很有帮助,它可以给你当前的Unix时间戳,这样你就可以和你的输出进行比较。