3

I am having a problem which started here. I found out why and am not trying to solve something else.

What I need is to set CherryPy to the longest possible session time that is available on different platforms. For this CherryPy uses time.gmtime(). On my Windows 64 Bit I have no problems setting the session timeout 100 years in the future but this does not work on a armhf platform. armhf allows me to set the session to be alive for 22 years.

Not I am looking for a way to set the timeout dynamically depending on the architecture.

On armhf I tryed using time.gmtime(sys.maxsize) which returned me the date in the year 2038. time.gmtime(sys.maxsize+1) Returns a OverflowError: timestamp out of range for platform time_t error. So I guess that this is the highest date possible.

Problem is that doing the same on my Windows machine with 64 bit (where sys.maxsize = 9223372036854775807) time.gmtime(sys.maxsize) returns OSError: [Errno 22] Invalid argument. Is there a way to do this across any architecture/platform?

Edit: This issue is not only caused by my code in CherryPy where the timeout value for a session was too high for certain platforms/architectures (mainly arm) but on some of them (Arm7) it is caused by CherryPy internaly too.

ap0
  • 1,083
  • 1
  • 12
  • 37
  • Do you really need to set the session time for that far in the future? – Cyphase Aug 21 '15 at 08:42
  • 1
    Are you searching for _one static_ value which can be used safely on all platforms — past, present, future — or do you want to determine the highest value _dynamically_ on the platform your code runs on? I think there is some confusion because I'm quite sure the „not“ in the very first sentence should have been a „now” instead and in the third paragraph I also read the „Not” as „Now” because the „Not” is in an odd place regarding english grammer (I think) and „Now“ would make sense there. To me at least. – BlackJack Aug 22 '15 at 08:36

2 Answers2

4

time.gmtime() accepts a float and therefore its input is limited by sys.float_info.max or an int in the range of C long (or long long if available).

To find "the highest date possible" we could use a binary search like in @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())

where binary_search() is a custom function that is used to accept input outside of bisect.bisect() range:

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

Results on my machine:

| 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() timestamp corresponds to 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

i.e., in principle time_t may be an extended integer type or e.g., a long double.

time_t is an integer type on POSIX

max time_t may be larger than sys.maxsize e.g., time_t may be a 64-bit type on 32-bit system.

See also:


It is possible to find the max gmtime() timestamp without knowing time_t limit:

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

The result is the same.

If TZ=right/UTC then the result is 67768036191676825 that corresponds to the same max time 2147485547-12-31 23:59:59 UTC. right/UTC timestamp is larger because it includes leap seconds (26 as of 2015-07-01).

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

Highest possible value for time.gmtime for given python version is determined by highest possible value of time_t type this python is compiled with. Minimal sane time_t for any architecture python is running on is 32 bit signed integer (Python documentation says that minimal sys.maxint is 2**31-1). That means that answer is 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...). See also https://en.wikipedia.org/wiki/Year_2038_problem

Konstantin Svintsov
  • 1,607
  • 10
  • 25
  • That is the answer for the _minimal_ ”sane” time value but the question was about the maximum. On a 64 bit system this might be higher than the maximum signed 32 bit value. – BlackJack Aug 21 '15 at 14:16
  • Highest possible gmtime for _any_ architecture - is a minimal highest gmtime over all existing architectures. – Konstantin Svintsov Aug 22 '15 at 02:58