Python中如何判断一个时间戳是否由UTC时间转换而来
新手一枚,以前一直以为时间戳是不带时区的,可今天发现 datetime.now().timestamp()和 datetime.utcnow().timestamp()转出来的时间戳不一样。 那么问题来了,拿到一个外部传进来的时间戳时,我应该用 utcfromtimestamp()呢,还是 fromtimestamp()去转成 datetime 形式
Python中如何判断一个时间戳是否由UTC时间转换而来
你应该先搞清时间戳的概念
要判断一个时间戳是否由UTC时间转换而来,最直接的方法是检查它是否具有时区信息。在Python中,我们可以使用datetime模块来处理时间戳和时区。
首先,我们需要了解Python中的两种时间戳表示:
- naive datetime:没有时区信息的时间戳
- aware datetime:带有时区信息的时间戳
如果一个时间戳是由UTC时间转换而来,它应该具有时区信息,并且时区应该是UTC。
下面是一个完整的代码示例:
from datetime import datetime, timezone
import pytz
def is_utc_timestamp(timestamp):
"""
判断时间戳是否由UTC时间转换而来
参数:
timestamp: datetime对象
返回:
bool: 如果是UTC时间戳返回True,否则返回False
"""
# 检查是否为aware datetime(带时区信息)
if timestamp.tzinfo is None:
return False
# 检查时区是否为UTC
# 方法1:直接比较时区
if timestamp.tzinfo == timezone.utc:
return True
# 方法2:使用pytz的UTC时区比较
try:
import pytz
if timestamp.tzinfo == pytz.UTC:
return True
except ImportError:
pass
# 方法3:检查时区偏移量是否为0
if timestamp.utcoffset().total_seconds() == 0:
return True
return False
# 测试示例
if __name__ == "__main__":
# 示例1:UTC时间戳
utc_time = datetime.now(timezone.utc)
print(f"UTC时间戳: {utc_time}")
print(f"是否为UTC: {is_utc_timestamp(utc_time)}")
# 示例2:本地时间戳(无时区信息)
local_time = datetime.now()
print(f"\n本地时间戳: {local_time}")
print(f"是否为UTC: {is_utc_timestamp(local_time)}")
# 示例3:其他时区的时间戳
try:
import pytz
ny_time = datetime.now(pytz.timezone('America/New_York'))
print(f"\n纽约时间戳: {ny_time}")
print(f"是否为UTC: {is_utc_timestamp(ny_time)}")
except ImportError:
print("\n需要安装pytz库来测试其他时区")
# 示例4:从字符串解析的UTC时间戳
utc_str = "2024-01-01T12:00:00+00:00"
parsed_time = datetime.fromisoformat(utc_str)
print(f"\n解析的时间戳: {parsed_time}")
print(f"是否为UTC: {is_utc_timestamp(parsed_time)}")
代码解释:
-
核心逻辑:首先检查时间戳是否有时区信息(
tzinfo不为None),然后检查时区是否为UTC。 -
三种检查方法:
- 直接与
timezone.utc比较 - 与
pytz.UTC比较(需要安装pytz库) - 检查UTC偏移量是否为0
- 直接与
-
关键点:
- 使用
datetime.now(timezone.utc)创建UTC时间戳 tzinfo属性为None表示没有时区信息utcoffset()方法返回时区偏移量
- 使用
-
注意事项:
- 如果时间戳没有时区信息,肯定不是UTC时间戳
- 即使有时区信息,也需要确认是否是UTC时区
- 使用
fromisoformat()可以正确解析带时区的ISO格式字符串
总结:检查tzinfo属性和时区偏移量就能确定是否UTC时间戳。
Python 表示这还真不知道
看具体情况。理论上,timestamp 是没有时区概念的,所有的 timestamp 都是从 UTC 开始算。datetime.datetime.fromtimestamp 会将 timestamp 转换出来的时间转换到本地时区,但是不添加 tzinfo,所以属于 tz-naive 的本地时区时间;而 utcfromtimestamp 转换出来的 datetime 不进行时区转换,是 tz-naive 的 UTC 时间
我的意思是如果我拿到一个时间戳,我想要一个 datetime 形式的 utc 时间。如果这个时间戳是从 datetime.now 转过来的,我用 utcfromtimestamp()就能拿到正确的值。但如果这个时间戳是从 datetime.utcnow 转过来的,我再用 utcfromtimestamp()就不对了,这种时候用 fromtimestamp()就行了。但只有时间戳应该怎么判断该用 utcfromtimestamp()还是 fromtimestamp()呢
我说一个物品的重量是 25,你帮我算算是公斤还是市斤
这个问题问得我…
你的意思是我是要去问这个时间戳的来源,这个是 now.timestamp()转出来的还是 utcnow.timestamp()转出来的是吗。。。
上面的 NoAnyLove 哥不是说了嘛,timestamp 本身都是从 UTC 算的
你用 utcfromtimestamp 还是 fromtimestamp 对结果没有差别,只是一个返回的是 utc 0 时区的 datetime object,另一个是 local 时区的 datetime object,两个对象指代的都是同一个时间点啊,有啥问题?
我的意思是如果我拿到一个时间戳,我想要一个 datetime 形式的 utc 时间。如果这个时间戳是从 datetime.now 转过来的,我用 utcfromtimestamp()就能拿到正确的值。但如果这个时间戳是从 datetime.utcnow 转过来的,我再用 utcfromtimestamp()就不对了,这种时候用 fromtimestamp()就行了。但只有时间戳应该怎么判断该用 utcfromtimestamp()还是 fromtimestamp()呢
因为我发现 datetime.now().timestamp()和 datetime.utcnow().timestamp()转出来的时间戳不一样。。。。
先去搞清楚 timezone-naive, timezone-aware
实际上只有 utcnow 的 timestamp 转换出来的才是标准时间戳,直接用 now 的 timestamp 并不是标准 unix 时间戳
取时间戳为啥不直接用 time.time()? datetime.timestamp 本身是用来做转换的,不是取时间戳;你用哪个时区转换就得用哪个时区反转
所以应该问一下那个外部时间戳的来源,问他时间戳用的是不是 utc 时间,不是的话还要问所用时区。不问的话无解。
我刚刚试了一下 time.time()也不是标准 unix 时间戳,有别的取标准时间戳的方法吗
按照 python 官方文档,time.time()的确是标准时间戳,不过你需要 int 转换一下
还有,java/javascript 中的时间戳是以毫秒为单位的,需要 int(time.time()*1000)
万分感谢!我整理一下你看对吗:如果确定传过来的是标准 unix 时间戳,我用 datetime.fromtimestamp()就能拿到一个 datetime 形式的 utc 时间。但如果对面不是标准的时间戳,我就要用 datetime.utcfromtimestamp()才能拿到一个 datetime 形式的 utc 时间。
timestamp()是 py3 才有的吧
你要拿真正的时间戳,首先要保证 datetime 对象是有 tzinfo 的,而你用 datetime.now()或者 datetime.utcnow()生成的都是 naive 的对象,不带 tzinfo,所以你再用 timestamp()获得的自然也可能是不正确的时间戳了
啊。。。为什么我 time.time()出来的时间戳是和 datetime.now().timestamp()一样的,
#16
用 time.gmtime(0)查一下你系统的 epoch,看看是不是标准 utc,可能是你系统配置或者时钟有问题
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0) 查出来是这个,应该对的吧
如果它是一个 ISO8601 格式的时间戳,那么它已经携带了时区信息,或表示它是一个 UTC 时间(即以 UTC+0 处理)
如果已知它是整数且是 Unix 时间戳,那么就隐含了是 UTC 时间的信息
如果什么都没有,就不好说了
。。。首先时间戳是没有时区的
那么为什么 datetime.now().timestamp()和 datetime.utcnow().timestamp()转出来的时间戳不一样,
因为 datetime.now()和 datetime.utcnow()不一样
咦,我 python 里的 datetime 对象怎么没有 timestamp 方法。
用个 arrow 或 https://github.com/sdispater/pendulum (推荐后者)
importPython 3.6.4 (default, Mar 9 2018, 23:15:03)
Type ‘copyright’, ‘credits’ or ‘license’ for more information
IPython 6.2.1 – An enhanced Interactive Python. Type ‘?’ for help.
In [1]: import arrow
In [2]: arrow.now().timestamp, arrow.utcnow().timestamp
Out[2]: (1523935279, 1523935279)
又翻了一下文档,time.time()用的是本地时区,标准的时间戳还是应该用 datetime.utcnow().timestamp()
你那个还是按照 GMT+8 时区算的时间戳
好的,谢谢,辛苦了。我还以为是我电脑有问题呢,看来取标准时间戳只能用 datetime.utcnow().timestamp()了
#28 时间戳没有时区概念
#23 看代码,datetime.timestamp 方法,实际上计算的是你给的那个 datetime 相对于 utc 时间的秒数,但里面默认了使用本地时区
看这里
>>> datetime.utcnow()
datetime.datetime(2018, 4, 17, 3, 32, 23, 714844)
>>> datetime.utcfromtimestamp(datetime.utcnow().timestamp())
datetime.datetime(2018, 4, 16, 19, 32, 41, 180266)
看到了吗?用这个 utcnow 时间戳是转换不回来正确的 utc 时间的,因为提前了 8 个小时,所以,time.time()才是正确的,用这个网站验证你的时间戳
https://www.unixtimestamp.com/
看你们讨论让我想起几周前我跟我们同事讨论时间戳的问题,也是特别纠结。
当时还说,得叫霍金来帮忙…
每隔几天霍金去世了……
看了 NoAnyLove 的回复,去查了下。时间戳没有时区概念,datetime.datetime.fromtimestamp()和 utcfromtimestamp()区别只是前者将时间转到了当前主机的本地时间。所以用哪个方法转化时间戳都一样……之前我也没理解时间戳的概念啊。但是不问外部传来时间戳的时区,就不知道它原来的本地时间。这个我应该还是没说错。
时间戳本身的确没有时区的概念,但是按照 unix 标准时间戳的定义是用 utc 时区进行计算的
楼主没搞清设计这个接口时的权责划分的问题。
你拿到 datetime 时,你不需要考虑它的来源是 datetime.now()还是 datetime.utcnow(),这是调用你的接口的人应该考虑的问题。你只要按照固定的方法(比如 fromtimestamp())来处理就好了。
如果你真的这么在意 datetime 的时区,那么改一下接口的设计,加上 tzinfo,让调用方把 datetime 和 tzinfo 一并传给你。
推荐个库 arrow 专解决这些时间转换的破问题
#34 你可以按我给的那个网站验证一下,那个时间戳是正确的
突然想到,既然时间戳没有时区概念,那么转化时间戳的 timestamp()是不应该传入带时区的时间。也就是 datetime.now().timestamp()是错误的用法?
不不不,datetime.utcnow().timestamp()只要用 fromtimestamp()就能换回正确的 utc 时间了。
datetime.now().timestamp()才要用 utcfromtimestamp()才能得到 utc 时间
d1 = datetime.now()
d2 = datetime.utcnow()
t1 = d1.timestamp()
t2 = d2.timestamp()
t3 = time.time()
nd1 = datetime.utcfromtimestamp(t1)
nd2 = datetime.fromtimestamp(t2)
nd3 = datetime.utcfromtimestamp(t3)
这里 nd1,nd2,nd3 拿到的都是相同的 utc 时间
另外,你的意思是 time.time()才是 unix 标准时间戳?,那 datetime.utcnow().timestamp()是啥
看我之前发的,还有人不信。
/t/346227
最后的结论是啥,就是用第三方模块吗。。
用 unix 时间为 0 输出就知道了
找了几个在线的时间戳网站,好像还真是这样:
datetime.now().timstamp()和 time.time()出来的就是标准 unix 时间戳。
那么问题来了,datetime.utcnow().timstamp()出来的这个是什么鬼。
#40 看函数定义啊
Help on built-in function fromtimestamp:
fromtimestamp(…) method of builtins.type instance
timestamp[, tz] -> tz’s local time from POSIX timestamp.
fromtimestamp 本来得到应该是你的本地时间,现在得到的却是 utc 时间了
#44 之前说过了,是把 utc 时间当做本地时间转换的时间戳,可能是 bug 或者是 feature (滑稽
嗯啊,也就是说,datetime.now().timstamp()和 time.time()出来的就是标准 unix 时间戳。
对于外部传来的标准 unix 时间戳,用 fromtimestamp()拿到的就是本地时间的 datetime ;用 utcfromtimestamp()拿到的就是 utc 时间的 datetime。这样总结没问题吧。
对的,实际上因为 Python2 里面不存在 datetime.timestamp 方法,一般都是使用 time.time,就不会出现今天你说的这个 utcnow.timestamp 问题
真是万分感谢,帮忙解决了这个疑惑,不然以后很容易采坑。
总结在 47 楼了,应该是没错了。
另外谢谢所有参与讨论和提供第三方库解决方法的朋友们
搞清楚时间戳,时区,utc,cst,等等时间格式规则,问题迎刃而解。都有专门的 rfc 文档解释,当然,那个很晦涩,直接 google。一大把的教程。。。
只是用第三方库来处理问题,三个月不用。又得踩一次坑
In [52]: datetime.datetime.fromtimestamp?
Docstring: timestamp[, tz] -> tz’s local time from POSIX timestamp.
Type: builtin_function_or_method
In [53]: tz_cn = pytz.timezone(‘Asia/Shanghai’)
In [54]: tz_utc = pytz.timezone(‘utc’)
In [55]: datetime.datetime.fromtimestamp(0, tz_utc)
Out[55]: datetime.datetime(1970, 1, 1, 0, 0, tzinfo=<UTC>)
In [56]: datetime.datetime.fromtimestamp(0, tz_cn)
Out[56]: datetime.datetime(1970, 1, 1, 8, 0, tzinfo=<DstTzInfo ‘Asia/Shanghai’ CST+8:00:00 STD>)
In [57]: datetime.datetime.fromtimestamp(0)
Out[57]: datetime.datetime(1970, 1, 1, 8, 0)
两个字:约定
最好的方法是所有时间戳都带上时区,再约定下不带时区的情况下默认用哪个时区,一般最好是 UTC
timestamp 是时区相关的,你只要在同一时区下解决就 ok 了


