0

How do you use a datetime64 passed from Python in C? I found some deprecated functions in the Numpy headers like PyArray_DatetimeToDatetimeStruct() but can't find anything relating to datetime64.

It seems like it is an array of integers along with some metadata on the backend but if so, how do you determine the units?

Scott L.
  • 265
  • 2
  • 9
  • Related: http://stackoverflow.com/q/25033711/694576 – alk Jan 13 '17 at 20:27
  • In one sense it is a standard array, with 8 bytes per item. It's the array `dtype` that handles that handles conversions. For example `x.tolist()` produces a list of Python `datetime` objects. It looks like the units information is in the `dtype.str`, `' – hpaulj Jan 13 '17 at 21:29
  • looks like most of the `numpy` `datetime` manipulation is located in `numpy/numpy/core/src/multiarray/datetime.c` – hpaulj Jan 14 '17 at 02:40

1 Answers1

2

For those wondering how to do this in the future, here is the solution I ended up with.

Pass the datetime64 as an int64 from Python:

dt = np.datetime64(datetime.utcnow())
my_C_func(dt.astype(np.int64))

The decoding in C is derived from the source of gmtime(). This code assumes that the datetime is in microseconds which you can determine by checking the dtype (thanks @hpaulj). start is the datetime64 coming from Python.

inline int
is_leapyear(uint64_t year)
{
    return (year & 0x3) == 0 && /* year % 4 == 0 */
           ((year % 100) != 0 ||
            (year % 400) == 0);
}
#define YEARSIZE(year) (is_leapyear(year) ? 366u : 365u)

...

uint64_t us_per_day = 1000ull * 1000ull * 60ull * 60ull * 24ull;
register uint64_t dayclock = start % us_per_day;
register uint64_t dayno = start / us_per_day;

uint64_t year = 1970ull;

uint64_t us = dayclock % 1000000ull;
uint64_t sec = (dayclock / 1000000ull) % 60ull;
uint64_t minute = (dayclock % 3600000000ull) / 60000000ull;
uint64_t hour = dayclock / 3600000000ull;
while (dayno >= YEARSIZE(year))
{
    dayno -= YEARSIZE(year);
    year++;
}

uint64_t day = dayno + 1;

If you don't need anything more accurate than seconds you could simply do this:

uint64_t other_start = start / 1000000ull;
struct tm* ptm;
ptm = gmtime((time_t*)&other_start);

printf("\tGMTIME VERSION:\n");
printf("\tyear: %d\n", ptm->tm_year);    
printf("\tday: %d\n", ptm->tm_yday);
printf("\thours: %d\n", ptm->tm_hour);
printf("\tminutes: %d\n", ptm->tm_min);
printf("\tsec: %d\n", ptm->tm_sec);

Of course the us portion can then be obtained separately:

uint64_t us = source % 1000000ull;
Scott L.
  • 265
  • 2
  • 9
  • Forgot extra 000 zeroes in divider, as gmtime works with seconds (as said in text). But given divider gives millis. Proper is / 1000000000ull – Alex Zaharov Jul 21 '21 at 13:11