pythoncpython-3.x

How to add/subtract time in Python C API?


There are many tutorials and examples out there on how to perform date-arithmetic from Python. But I cannot find anything for the Python C API...

My C function, callable from Python, operates on two dates: start and finish. If start is not explicitly specified, it must be understood as one day before the finish.

How do I do this?

    /* Attempt converting the finish-date, if not a date already */
    if (!PyDate_Check(finishO)) {

        o = PyDate_FromTimestamp(finishO);
        if (o == NULL)
            return PyErr_Format(PyExc_ValueError,
                "%R is not a valid date", finishO);
        finishO = o;
    }

    /* If start-date is not specified, assume one day prior the finish date */
    if (startO == NULL) {
        PyObject    *oneDay = PyDelta_FromDSU(1, 0, 0);
        /* Derive startO from finishO by applying the one-day delta somehow */
    }

Solution

  • Once you have the timedelta object returned from PyDelta_FromDSU, use the PyNumber API to compute the offset.

    Functional Windows example below. I didn't write a whole Python C module, just the demonstration function called from ctypes:

    test.c - compiled with MSVC: cl /LD /W4 /Ic:\python313\include test.c -link /libpath:c:\python313\libs

    #include <Python.h>
    #include <datetime.h>  // PyDateTime not included in Python.h
    
    __declspec(dllexport)
    PyObject* func(PyObject* start, PyObject* finish) {
        PyDateTime_IMPORT;  // required initialization
        if(start == Py_None) {
            PyObject *oneDay = PyDelta_FromDSU(1, 0, 0);
            start = PyNumber_Subtract(finish, oneDay);
            Py_XDECREF(oneDay);
        }
        return start;
    }
    

    test.py

    import datetime as dt
    import ctypes as ct
    
    dll = ct.PyDLL('./test')
    func = dll.func
    func.argtypes = ct.py_object, ct.py_object
    func.restype = ct.py_object
    
    start = dt.datetime(2025, 8, 14)
    finish = dt.datetime(2025, 8, 14, 1)
    
    print(f'start  = {start}')
    print(f'finish = {finish}')
    print(func(start, finish))  # returns start
    print(func(None, finish))   # returns 1 day before finish
    

    Output:

    start  = 2025-08-14 00:00:00
    finish = 2025-08-14 01:00:00
    2025-08-14 00:00:00
    2025-08-13 01:00:00