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,你帮我算算是公斤还是市斤
timestamp 是 UTC 时间,你转换出来不一致说明你设置的 timezone 不对
你的意思是我是要去问这个时间戳的来源,这个是 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 了

