Python fromtimestamp OSError

45

使用fromtimestamp构造日期时间时,如果使用小于-43200(-12小时)的负时间,我会收到

"OSError [Errno22] Invalid Argument"

我使用的是Win7 64位和Python 3.5.1。以下是产生错误的代码。

>>> import datetime
>>> import pytz
>>> datetime.datetime.fromtimestamp(-43200, pytz.utc)
datetime.datetime(1969, 12, 31, 12, 0, tzinfo=<UTC>)
>>> datetime.datetime.fromtimestamp(-43201, pytz.utc)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument

该示例使用pytz来简化时区信息,但即使没有它,错误也会发生。

这里你的两行代码很相似,第一行打印了值,第二行抛出了错误。所以我认为可能是缩进错误或其他问题。尝试使用 except OSError as e: 捕获异常,然后输出你的错误信息。 - Mohideen bin Mohammed
3
这不是语法错误。这个例子故意保持简单,以展示引发错误所需的最低限度。我最初在一个for循环中发现了这个限制。 - Jon Hartnett
1
我遇到了同样的问题,这是Python 3.6中的一个错误。详情请见:https://dev59.com/UlgQ5IYBdhLWcg3wqFsF - dashesy
7个回答

69
如果你收到此错误并且没有使用明显错误的时间戳,请检查你的单位。 fromtimestamp需要一个以秒为单位的时间戳,而通常会得到毫秒级别的时间戳(例如,当尝试解析从Moment.js生成的日历小部件中产生的时间戳时,我发现了这一点)。
以时间戳1523443804214为例——这是2018年4月11日,在我发布这篇文章前大约15分钟。根据Epoch Converter,没有问题,但请注意:“假设此时间戳是以毫秒为单位的:”
在Python中,这将返回一个OSError:
In [15]: datetime.fromtimestamp(1523443804214.0)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-15-0c8efd251031> in <module>()
----> 1 datetime.fromtimestamp(1523443804214.0)

然而,如果我们将其除以一千:

In [17]: datetime.fromtimestamp(1523443804.214)
Out[17]: datetime.datetime(2018, 4, 11, 11, 50, 4, 214000)

结果符合我们的预期。


我需要看到这个明确地告诉我。这就是我出错的确切原因。我在Python中使用JSON PowerShell日期,而它们使用毫秒。 - Matt

25

为解决此问题,请将时间戳值除以1000。

在Windows中,时间戳数字乘以1000的因子。


20
如果时间戳超出平台C localtime()gmtime()函数支持的值范围,datetime.fromtimestamp()可能会引发像你所看到的异常。
在Windows平台上,这个范围有时可能被限制在1970年至2038年。我从未在Linux系统上遇到过这个问题。
如果您在Windows上有一个负时间戳t并遇到此错误,则可以使用以下解决方法:
from datetime import datetime, timedelta
datetime.fromtimestamp(0) + timedelta(seconds=t)     # localtime
datetime.utcfromtimestamp(0) + timedelta(seconds=t)  # utc

4
当时间戳等于0时,我在Windows 10上(和Python 3.6.5)遇到了相同的问题。 - Edward Weinert
3
只需要将时间戳除以1000即可。我在Windows 10和Linux上进行了测试,两者都能正常工作并得出正确的答案。 - Hyerois
1
这是对于OP的情况和1969年日期的正确答案。 - Pythonic
1
@Primoz 在答案中添加了一种可能的解决方法。我认为正确的结果应该是 datetime.datetime(1949,1,1,0,0) 而不是 datetime.datetime(1949,1,1,1,0),对吗?也许你的机器配置在 UTC+1 时区,并且你使用了本地时间。 - wim
谢谢。是的,你说得对,它是datetime.datetime(1949, 1, 1, 0, 0)。我从控制台复制了结果,并且我的时区是UTC+1,这就是不同结果的原因。 - Primoz
显示剩余3条评论

12

@wim的回答是正确的,但其他人可能会对测试感兴趣(如果您希望调整范围,请进行调整):

import datetime
import platform
print(
    "Running on Python ver.{} on {} {}\n" \
    .format(
        platform.python_version(),
        platform.system(),
        platform.release()
        )
)
for timestamp in range(1, 100000000):
    try:
        dt = datetime.datetime.fromtimestamp(timestamp)
    except:
        pass
    else:
        break
print(
    "Smallest accepted Unix timestamp by {}: '{}' ({})" \
    .format(platform.system(), timestamp, dt)
)
我得到的是:
A:\src\X.utilities>test.py
Running on Python ver.3.6.1 on Windows 7

Smallest accepted Unix timestamp by Windows: '86400' (1970-01-02 02:00:00)

1
Running on Python ver.3.9.1 on Windows 10 Smallest accepted Unix timestamp by Windows: '1' (1970-01-01 01:00:01) - Polluks

0

虽然贡献晚了,但我认为需要强调的是这个问题是1)与特定平台有关的,以及2)可能会出现与平台无关的解决方案。

引用文档中的内容:

fromtimestamp()函数可能会引发OverflowError,如果时间戳超出了由平台C localtime()gmtime()函数支持的值范围,则会引发OSError,在1970年至2038年之间限制通常被使用。

“限制在1970年至2038年之间”基本上意味着它只能表示以秒为单位的带符号32位整数的正范围。请注意,这不是Unix时间戳的一般性限制 Unix时间戳。尽管如此,负数也可能失败,例如在以下代码中,Windows 10 上的 Python 3.10

from datetime import datetime
print(datetime.fromtimestamp(-86400)) # 1969-12-31

>>>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 22] Invalid argument

跨平台选择将会是

from datetime import datetime, timedelta
print(datetime(1970,1,1)+timedelta(seconds=-86400))

>>>
1969-12-31 00:00:00

-1

时间也可以用微秒表示,只需将时间戳值除以1000000即可。


-9
时间戳是自1970年1月1日起的秒数,始终为正值。

8
反感多余的居高临下言语。实际上,在大多数情况下,使用fromtimestamp和负数可以正常工作,但它依赖于平台。 - wim
6
使用“-1”而不是“-1”被扣除了分数。 - Andras Deak -- Слава Україні

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