Getting type/size of `time_t` using ctypes

Question:

I’m accessing a C struct which contains some time_t fields using python ctypes module.

Given its non completely portable nature, I cannot define these fields statically as of c_int or c_long type.

How can I define them to make my code portable?

Example C struct definition:

#import <sys/types.h>
#import <time.h>

typedef struct my_struct {
    time_t timestap;
    uint16_t code;      
};

Respective python ctypes structure:

from ctypes import *

c_time = ? # What do I have to put here?

class MyStruct(Structure):
    _fields_ = [
        ('timestamp', c_time),
        ('code', c_int16),
    ]
Asked By: GaretJax

||

Answers:

Your best bet is by introspecting the system your script runs on and making a best bet on which integral type to use. Something like,

if sys.platform == 'win32':
    time_t = ctypes.c_uint64
# ...

The bottom line is that time_t is not defined in the standard. It’s up to the OS and compiler. So your definition of time_t in your Python script depends on the DLL/so you are interacting with.

Answered By: Santa

Starting with Python 3.12 (still in development at the time of writing this, planned for release in October 2023), ctypes.c_time_t can be used for this, in your example:

# This requires Python 3.12, which adds ctypes.c_time_t

from ctypes import *

class MyStruct(Structure):
    _fields_ = [
        ('timestamp', c_time_t),
        ('code', c_int16),
    ]

I had a use case for this last year and the patch was accepted.

If you don’t have Python 3.12 yet, something like this could do the trick, although it’s not covering all cases (from the PR/issue above):

import platform
import ctypes

if platform.system() == 'Windows':
    # Assume MSVC(?) - what about mingw/clang?
    time_t = ctypes.c_int64
elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_int64):
    # 64-bit platform of any kind - assume 64-bit time_t(?)
    time_t = ctypes.c_int64
else:
    # assume some kind of 32-bit platform(?)
    time_t = ctypes.c_int32

# ... use time_t here ...

This should be fine for Windows (MSVC >= 2005), macOS (x86_64, aarch64) and Linux (arm32, aarch64, x86, x86_64), but might need tweaking for the BSDs on some CPUs.

Once Python 3.12 is released and ctypes.c_time_t is a thing, you can just use that.

For example, this is how gPodder’s libgpod_ctypes.py tries to use ctypes.c_time_t, but falls back to some simple heuristics on older systems:

# ctypes.c_time_t will be available in Python 3.12 onwards
# See also: https://github.com/python/cpython/pull/92870
if hasattr(ctypes, 'c_time_t'):
    time_t = ctypes.c_time_t
else:
    # See also: https://github.com/python/cpython/issues/92869
    if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_int64):
        time_t = ctypes.c_int64
    else:
        # On 32-bit systems, time_t is historically 32-bit, but due to Y2K38
        # there have been efforts to establish 64-bit time_t on 32-bit Linux:
        # https://linux.slashdot.org/story/20/02/15/0247201/linux-is-ready-for-the-end-of-time
        # https://www.gnu.org/software/libc/manual/html_node/64_002dbit-time-symbol-handling.html
        time_t = ctypes.c_int32
Answered By: Thomas Perl
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.