Python:将UTC时间元组转换为UTC时间戳

19 投票
1 回答
26751 浏览
提问于 2025-04-17 19:18

我的问题是:我需要把一个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秒。

最后,回到我们的函数mktimetimegm。它们接受一个时间元组,这只是“某个时间”的另一种表示。记住,我们传递给它们的是一个天真的时间,没有本地或UTC的概念。

假设X是一个表示天真的“某个时间”的时间元组。那么:

  • mktime(X)将返回需要经过的秒数,以便你的本地当前时间变成那个“某个时间”;
  • timegm(X)将返回需要经过的秒数,以便格林威治的当前时间变成那个“某个时间”。

在上面的例子中,nowutc_now表示天真的“某个时间”,当我们把这些“某个时间”的值传递给mktimetimegm时,它们简单地返回了对应地点(你的地点和格林威治)当前时间变成那个“某个时间”所需经过的秒数。


最后,回到我的问题:我需要把一个UTC时间元组转换成UTC时间戳。

首先,没有“UTC时间元组”这个概念——它只是“某个时间”。如果我需要把它转换为UTC,我只需使用timegm

cal.timegm(datetime.timetuple(utc_now))

这将给我当前UTC时间的时间戳(即格林威治的当前“某个时间”)。

1 个回答

8

实际上,这里只有个不同的值。这两个值:

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时间戳,这样你就可以和你的输出进行比较。

撰写回答