Python - calendar.timegm()与time.mktime()的区别

74

我似乎很难理解这个问题。

calendar.timegm()time.mktime()之间有什么区别?

如果我有一个不带时区信息的datetime.datetime,那么两者不应该给出相同的输出吗?它们都不是给出从纪元到传递的日期之间的秒数吗?由于传递的日期没有时区信息,那么这个秒数不是相同的吗?

>>> import calendar
>>> import time
>>> import datetime
>>> d = datetime.datetime(2010, 10, 10)
>>> calendar.timegm(d.timetuple())
1286668800
>>> time.mktime(d.timetuple())
1286640000.0
>>> 

2
请参考此问题:https://dev59.com/tuo6XIcBkEYKwwoYNBpn - treecoder
3个回答

114

time.mktime() 假定传入的元组是本地时间,calendar.timegm() 假定传入的是GMT/UTC时间。根据解释,元组代表的是不同的时间,因此这两个函数返回不同的值(自纪元以来的秒数基于UTC)。

这两个值之间的差异应该等于您所在时区的时差偏移量。


哦,我明白了,基本上timegm假定我传递的是UTC时间,并简单地计算出我传递的时间与1970年1月1日UTC时间之间的差异,而mktime首先通过添加我的时区偏移量将我传递的时间转换为UTC时间,然后执行timegm所做的操作? - ibz
但是,如果我的datetime的tzinfo为None,为什么mktime会进行任何转换呢?它不应该保持原样吗?为什么它会假设它在本地时区? - ibz
2
@ibz:传递给mktime()timetuple参数不包含任何时区信息(它从来没有,timetuple中没有时区字段)。因此,该函数必须“猜测”可能是哪个时区,并且mktime()总是假定它是本地时间。这就是函数的行为方式。 - sth
2
此外,calendar.timegm() 硬编码了 1970 年 1 月 1 日UTC (posix纪元)。 time.mktime() 可能使用不同的纪元。 从 文档:对于Unix,时代是1970年。 要找出时代是什么,请查看gmtime(0)。尽管标准库的其余部分可能假定 posix 纪元。 - jfs

11

4
更准确地说,timegm将给定的日期解释为UTC,返回一个时间戳,而mktime将给定的日期解释为本地时间,返回一个时间戳。 - Greg Hewgill

1

让我们考虑下面的例子:

>>> import datetime
>>> import time
>>> import calendar
>>> utc_time_now = datetime.datetime.utcnow()
>>> utc_time_now
datetime.datetime(2022, 5, 21, 6, 47, 33, 929433)
>>> time.mktime(utc_time_now.timetuple())
1653095853.0
>>> calendar.timegm(utc_time_now.timetuple())
1653115653
>>> time.tzname
('IST', 'IST')
>>> time.strftime("%z", time.gmtime())
'+0530'
>>> (1653115653-1653095853)
19800

在解释开始之前,请注意,'datetime.datetime.utcnow()' 返回的是一个 DateTime 对象,它是“naive”的,并且没有关于您本地时区的任何信息。

time.mktime()
将上述函数应用于 DateTime 对象会考虑到“时区”。因此,根据上面的例子,当您向 time.gmtime() 提供时间“2022-05-21 06:47:33”时,它会认为这个时间实际上不是在 UTC 中,而是在您的本地时区(我的情况下是'IST')。

在我的情况下,时区是 'IST',它比 UTC 时区提前 +05:30(5 小时,30 分钟)。 所以为了返回 UTC 时区中的时代,它会从 datetime 对象的时间戳计算中减去 +05:30(19800 秒)。
因此,它返回时间戳(1653095853.0),这比原始的 UTC 时代秒数(1653115653)少 19800 秒。

注意:如果您的时区是负的(例如 -05:30),它会在最终时代计算中添加 19800 秒。因此,您将看到 time.gmtime() 时间戳比 UTC 时代时间戳大 19800 秒。

calendar.timegm()
它返回相应的 Unix 时间戳值,假设一个纪元为 1970-01-01。因此,在 DateTime 对象中不进行任何额外的调整。
它获取任何 DateTime 对象值,将其从纪元时间“1970-01-01”中减去并返回经过的总秒数(seconds())。

BONUS 奖励
从您的 UTC DateTime 对象获取时间戳纪元毫秒。
第一种方法:

>>> utc_time_now
datetime.datetime(2022, 5, 21, 7, 16, 34, 938547)
>>> int((calendar.timegm(utc_time_now.timetuple()) + (utc_time_now.microsecond/1e6))*1000.0)
1653117394938

第二种方法:

>>> utc_time_epoch = datetime.datetime.utcfromtimestamp(0)
>>> utc_time_epoch 
datetime.datetime(1970, 1, 1, 0, 0)
>>> utc_time_now = datetime.datetime.utcnow()
>>> utc_time_now 
datetime.datetime(2022, 5, 21, 7, 16, 34, 938547)
>>> elapsed_time = utc_time_now - utc_time_epoch
>>> elapsed_time
datetime.timedelta(19133, 26194, 938547)
>>> int(elapsed_time.total_seconds() * 1000.0)
1653117394938

祝你玩得愉快 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接