为任何架构获得尽可能高的 gmtime

Get the highest possible gmtime for any architecture

我遇到了一个问题,开始于 here。我发现了原因并且没有尝试解决其他问题。

我需要的是将 CherryPy 设置为在不同平台上可用的最长可能会话时间。为此 CherryPy 使用 time.gmtime()。在我的 Windows 64 位上,我可以毫无问题地将会话超时设置为未来 100 年,但这在 armhf 平台上不起作用。 armhf 允许我将会话设置为 22 年。

不是 我正在寻找一种根据体系结构动态设置超时的方法。

在 armhf 上,我尝试使用 time.gmtime(sys.maxsize),它返回了 2038 年的日期。time.gmtime(sys.maxsize+1) Returns OverflowError: timestamp out of range for platform time_t 错误。所以我想这是可能的最晚日期。

问题是在我的 Windows 64 位机器上做同样的事情(其中 sys.maxsize = 9223372036854775807time.gmtime(sys.maxsize) returns OSError: [Errno 22] Invalid argument。有没有办法在 architecture/platform 中执行此操作?

编辑: 这个问题不仅是由我在 CherryPy 中的代码引起的,其中会话的超时值对于某些 platforms/architectures(主要是 arm)来说太高了,但是在其中一些(Arm7)上,它也是由 CherryPy 内部引起的。

给定 python 版本的 time.gmtime 的最高可能值由编译此 python 的 time_t 类型的最高可能值决定。任何架构的最小理智 time_t python 是 运行 是 32 位有符号整数(Python 文档说最小 sys.maxint 是 2**31-1) .这意味着答案是 time.gmtime(2 ** 31 - 1) == time.gmtime(2147483647) == time.struct_time(tm_year=2038, tm_mon= 1、tm_mday=19,tm_hour=3,tm_min=14,tm_sec=7,tm_wday=1,tm_yday=19。 ..). 另见 https://en.wikipedia.org/wiki/Year_2038_problem

time.gmtime() 接受浮点数,因此其输入受限于 sys.float_info.maxint in the range of C long (or long long if available).

要找到 "the highest date possible" 我们可以使用二进制搜索,如 @BlackJack's answer:

#!/usr/bin/env python
import ctypes
import sys
import time

MAX_TIME = max(int(sys.float_info.max),
               2**(8*ctypes.sizeof(getattr(ctypes, 'c_longlong', ctypes.c_long))))
BOUNDARY = 0.5
assert False < BOUNDARY < True # necessary for the binary search to work

class GmtimeOverflowTable:
    def __getitem__(self, timestamp):
        assert timestamp >= 0
        try:
            time.gmtime(timestamp)
        except (OSError, OverflowError, ValueError): # ValueError for Python <3.3
            return True # overflow
        return False

def find_max_gmtime_timestamp():
    overflow = GmtimeOverflowTable()
    assert overflow[float('+inf')] and not overflow[0]
    if overflow[MAX_TIME]:
        ts = binary_search(overflow, BOUNDARY, 0, MAX_TIME)
        assert overflow[ts] and not overflow[ts - 1]
        return ts - 1
    raise OverflowError("Max gmtime timestamp is larger than " + str(MAX_TIME))

print(find_max_gmtime_timestamp())

其中 binary_search() 是一个自定义函数,用于接受超出 bisect.bisect() 范围的输入:

def binary_search(haystack, needle, lo, hi): # avoid bisect() range limitation
    while lo < hi:
        mid = (lo + hi) // 2
        if haystack[mid] > needle:
            hi = mid
        elif haystack[mid] < needle:
            lo = mid + 1
        else:
            return mid
    return hi

我机器上的结果:

| Python version       | max gmtime timestamp |
|----------------------+----------------------|
| Python 2.7.9         |    67768036191676795 |
| Python 3.4.3         |    67768036191676799 |
| Pypy  (Python 2.7.9) |    67768036191676795 |
| Pypy3 (Python 3.2)   |    67768036191676795 |
| Jython 2.7.0         |     9223372036854777 |

67768036191676799 Python 3 max gmtime() 时间戳对应max 32-bit int year:

>>> import time; time.gmtime(67768036191676799)                                    
time.struct_time(tm_year=2147485547, tm_mon=12, tm_mday=31, tm_hour=23, tm_min=59, tm_sec=59, tm_wday=2, tm_yday=365, tm_isdst=0)
>>> 2147485547-1900
2147483647
>>> 2**31-1
2147483647

In general, Python time.gmtime() delegates to the platform C gmtime() function:

Most of the functions defined in this module call platform C library functions with the same name. It may sometimes be helpful to consult the platform documentation, because the semantics of these functions varies among platforms.

The corresponding function signature in C11:

struct tm *gmtime(const time_t *timer);

time_t limits are implementation-defined in C:

The range and precision of times representable in clock_t and time_t are implementation-defined.

time_t is required to be a real type on c11:

real types
    integer types
        char
        sίgned integer types
            standard sίgned integer types
                signed char, short int, int, long int, long long int
            extended sίgned integer types
        unsίgned integer types
            standard unsίgned integer types
                _Bool, unsigned char, unsigned short int, unsigned int,
                unsigned long int, unsigned long long int
            extended unsίgned integer types
        enumeration  types
    real floating types
        float, double, long double

即原则上 time_t 可以是扩展整数类型或例如 long double。

time_t is an integer type on POSIX

max time_t 可能大于 sys.maxsize 例如,time_t 在 32 位系统上可能是 64 位类型。

另请参阅:

  • Maximum values for time_t (struct timespec)
  • What is the biggest useful value of time_t?

可以在不知道 time_t 限制的情况下找到最大 gmtime() 时间戳:

def find_max_gmtime_timestamp():
    ts = 1
    overflow = GmtimeOverflowTable()
    assert overflow[float('+inf')] and not overflow[ts]
    while not overflow[ts]:
        ts *= 2
    ts = binary_search(overflow, BOUNDARY, ts//2, ts)
    max_ts = ts - 1
    assert overflow[max_ts+1] and not overflow[max_ts]
    return max_ts

结果是一样的

如果 TZ=right/UTC 那么结果是 67768036191676825 对应于相同的最大时间 2147485547-12-31 23:59:59 UTCright/UTC 时间戳较大,因为它包括闰秒(26 截至 2015-07-01)。