Python中如何判断一个时间戳是否由UTC时间转换而来

新手一枚,以前一直以为时间戳是不带时区的,可今天发现 datetime.now().timestamp()和 datetime.utcnow().timestamp()转出来的时间戳不一样。 那么问题来了,拿到一个外部传进来的时间戳时,我应该用 utcfromtimestamp()呢,还是 fromtimestamp()去转成 datetime 形式


Python中如何判断一个时间戳是否由UTC时间转换而来
54 回复

你应该先搞清时间戳的概念


要判断一个时间戳是否由UTC时间转换而来,最直接的方法是检查它是否具有时区信息。在Python中,我们可以使用datetime模块来处理时间戳和时区。

首先,我们需要了解Python中的两种时间戳表示:

  1. naive datetime:没有时区信息的时间戳
  2. 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)}")

代码解释:

  1. 核心逻辑:首先检查时间戳是否有时区信息(tzinfo不为None),然后检查时区是否为UTC。

  2. 三种检查方法

    • 直接与timezone.utc比较
    • pytz.UTC比较(需要安装pytz库)
    • 检查UTC偏移量是否为0
  3. 关键点

    • 使用datetime.now(timezone.utc)创建UTC时间戳
    • tzinfo属性为None表示没有时区信息
    • utcoffset()方法返回时区偏移量
  4. 注意事项

    • 如果时间戳没有时区信息,肯定不是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 时间戳,有别的取标准时间戳的方法吗

万分感谢!我也是今天才知道 datetime.now().timestamp()取出来的不是标准时间戳。

按照 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 你可以按我给的那个网站验证一下,那个时间戳是正确的

真的觉得时间是一个值得讨论 N 小时的问题,,我再清清脑子,,

突然想到,既然时间戳没有时区概念,那么转化时间戳的 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 了

回到顶部