8 n__time_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00

src/n_time.c

Common time functions. More...

Structures

Name
struct _n_civil

Functions

Name
int _offset_ok(long offset)
unsigned _month_days(long long y, unsigned m)
int _leap(long long y)
int _digits(const char * s, size_t n, unsigned * v)
long long _days_from_civil(long long y, unsigned m, unsigned d)
int _civil_to_time(long long y, unsigned mo, unsigned d, unsigned h, unsigned mi, unsigned se, time_t * t)
int _civil_of(time_t t, long offset, _n_civil * c)
void _civil_from_days(long long z, long long * y, unsigned * m, unsigned * d)

Attributes

Name
const char *const[7] _wday_names
const char *const[12] _month_names

Detailed Description

Common time functions.

Author: Castagnier Mickael

Version: 1.0

Date: 24/03/05

Functions Documentation

function _offset_ok

static int _offset_ok(
    long offset
)

function _month_days

static unsigned _month_days(
    long long y,
    unsigned m
)

function _leap

static int _leap(
    long long y
)

function _digits

static int _digits(
    const char * s,
    size_t n,
    unsigned * v
)

function _days_from_civil

static long long _days_from_civil(
    long long y,
    unsigned m,
    unsigned d
)

function _civil_to_time

static int _civil_to_time(
    long long y,
    unsigned mo,
    unsigned d,
    unsigned h,
    unsigned mi,
    unsigned se,
    time_t * t
)

function _civil_of

static int _civil_of(
    time_t t,
    long offset,
    _n_civil * c
)

function _civil_from_days

static void _civil_from_days(
    long long z,
    long long * y,
    unsigned * m,
    unsigned * d
)

Attributes Documentation

variable _wday_names

static const char *const[7] _wday_names = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"};

variable _month_names

static const char *const[12] _month_names = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#include "nilorea/n_time.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#if defined(__windows__)
void u_sleep(__int64 usec) {
    HANDLE timer;
    LARGE_INTEGER ft;

    ft.QuadPart = -(10 * usec);  // Convert to 100 nanosecond interval, negative value indicates relative time

    timer = CreateWaitableTimer(NULL, TRUE, NULL);
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
    WaitForSingleObject(timer, INFINITE);
    CloseHandle(timer);
}
#else
void u_sleep(unsigned int usec) {
    usleep(usec);
}
#endif

void PAUSE(void) {
    char k[2] = "";

    printf("Press enter to continue...");

    int n = scanf("%c", k);
    int error = errno;
    if (n == EOF) {
        n_log(LOG_DEBUG, "Enter key was pressed !");
    } else if (error != 0) {
        n_log(LOG_ERR, "error %s when waiting for a key !", strerror(error));
    } else {
        n_log(LOG_DEBUG, "No matching characters");
    }

    printf("\n");
} /* PAUSE(...) */

int start_HiTimer(N_TIME* timer) {
    __n_assert(timer, return FALSE);
    timer->delta = 0;

#if defined(__windows__)
    if (QueryPerformanceFrequency((LARGE_INTEGER*)&timer->freq) == 0)
        return FALSE;
    if (QueryPerformanceCounter(&timer->startTime) == 0)
        return FALSE;
#else
    if (gettimeofday(&timer->startTime, 0) != 0)
        return FALSE;
#endif

    return TRUE;
} /* init_HiTimer(...) */

time_t get_usec(N_TIME* timer) {
    __n_assert(timer, return 0);
#ifdef __windows__
    QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
    timer->delta = 1000000 * (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
    timer->startTime = timer->currentTime;
#else
    gettimeofday(&timer->currentTime, 0);
    timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) * 1000000 + (timer->currentTime.tv_usec - timer->startTime.tv_usec);
    timer->startTime.tv_sec = timer->currentTime.tv_sec;
    timer->startTime.tv_usec = timer->currentTime.tv_usec;
#endif

    return timer->delta;
} /* get_usec( ... ) */

time_t get_msec(N_TIME* timer) {
    __n_assert(timer, return 0);
#ifdef __windows__
    QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
    timer->delta = 1000 * (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
    timer->startTime = timer->currentTime;
#else
    gettimeofday(&timer->currentTime, 0);
    timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) * 1000 + (timer->currentTime.tv_usec - timer->startTime.tv_usec) / 1000;
    timer->startTime.tv_sec = timer->currentTime.tv_sec;
    timer->startTime.tv_usec = timer->currentTime.tv_usec;
#endif

    return timer->delta;
} /* get_msec(...) */

time_t get_sec(N_TIME* timer) {
    __n_assert(timer, return 0);
#ifdef __windows__
    QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
    timer->delta = (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
    timer->startTime = timer->currentTime;
#else
    gettimeofday(&timer->currentTime, 0);
    timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) + (timer->currentTime.tv_usec - timer->startTime.tv_usec) / 1000000;
    timer->startTime.tv_sec = timer->currentTime.tv_sec;
    timer->startTime.tv_usec = timer->currentTime.tv_usec;
#endif
    return timer->delta;
} /* get_sec(...) */

/* Calendar arithmetic for the date formats below: days since 1970-01-01 to a
 * proleptic Gregorian date and back (H. Hinnant's algorithms), so no gmtime(),
 * timegm(), TZ or locale is involved and every platform agrees. */
static long long _days_from_civil(long long y, unsigned m, unsigned d) {
    long long era;
    unsigned yoe, doy, doe;
    y -= m <= 2;
    era = (y >= 0 ? y : y - 399) / 400;
    yoe = (unsigned)(y - era * 400);
    doy = (153 * (m + (m > 2 ? (unsigned)-3 : 9)) + 2) / 5 + d - 1;
    doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
    return era * 146097 + (long long)doe - 719468;
}

static void _civil_from_days(long long z, long long* y, unsigned* m, unsigned* d) {
    long long era;
    unsigned doe, yoe, doy, mp;
    z += 719468;
    era = (z >= 0 ? z : z - 146096) / 146097;
    doe = (unsigned)(z - era * 146097);
    yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
    doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
    mp = (5 * doy + 2) / 153;
    *d = doy - (153 * mp + 2) / 5 + 1;
    *m = mp < 10 ? mp + 3 : mp - 9;
    *y = (long long)yoe + era * 400 + (*m <= 2);
}

static const char* const _wday_names[7] = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"};
static const char* const _month_names[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                                             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

static int _leap(long long y) {
    return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
}

static unsigned _month_days(long long y, unsigned m) {
    static const unsigned md[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    return m == 2 && _leap(y) ? 29u : md[m - 1];
}

/* Broken-down fields of t + offset, years 1..9999 only. */
typedef struct {
    long long year;
    unsigned mon, mday, hour, min, sec, wday_idx;
} _n_civil;

static int _civil_of(time_t t, long offset, _n_civil* c) {
    long long s = (long long)t + offset, days, rem;
    days = s >= 0 ? s / 86400 : -((-s + 86399) / 86400);
    rem = s - days * 86400;
    _civil_from_days(days, &c->year, &c->mon, &c->mday);
    if (c->year < 1 || c->year > 9999)
        return FALSE;
    c->hour = (unsigned)(rem / 3600);
    c->min = (unsigned)(rem % 3600 / 60);
    c->sec = (unsigned)(rem % 60);
    c->wday_idx = (unsigned)(((days % 7) + 7) % 7); /* 0 = Thursday, 1970-01-01 */
    return TRUE;
}

static int _offset_ok(long offset) {
    return offset > -86400 && offset < 86400 && offset % 60 == 0;
}

int n_time_http_date(time_t t, char* out, size_t size) {
    _n_civil c;
    if (!out || size < N_TIME_HTTP_DATE_LEN || !_civil_of(t, 0, &c))
        return FALSE;
    snprintf(out, size, "%s, %02u %s %04lld %02u:%02u:%02u GMT", _wday_names[c.wday_idx], c.mday,
             _month_names[c.mon - 1], c.year, c.hour, c.min, c.sec);
    return TRUE;
}

int n_time_rfc2822(time_t t, long offset, char* out, size_t size) {
    _n_civil c;
    long a = offset < 0 ? -offset : offset;
    if (!out || size < N_TIME_RFC2822_LEN || !_offset_ok(offset) || !_civil_of(t, offset, &c))
        return FALSE;
    snprintf(out, size, "%s, %02u %s %04lld %02u:%02u:%02u %c%02ld%02ld", _wday_names[c.wday_idx], c.mday,
             _month_names[c.mon - 1], c.year, c.hour, c.min, c.sec, offset < 0 ? '-' : '+', a / 3600, a % 3600 / 60);
    return TRUE;
}

int n_time_rfc3339(time_t t, long offset, char* out, size_t size) {
    _n_civil c;
    long a = offset < 0 ? -offset : offset;
    if (!out || size < N_TIME_RFC3339_LEN || !_offset_ok(offset) || !_civil_of(t, offset, &c))
        return FALSE;
    snprintf(out, size, "%04lld-%02u-%02uT%02u:%02u:%02u%c%02ld:%02ld", c.year, c.mon, c.mday, c.hour, c.min,
             c.sec, offset < 0 ? '-' : '+', a / 3600, a % 3600 / 60);
    return TRUE;
}

/* Exactly n decimal digits at s, into *v. */
static int _digits(const char* s, size_t n, unsigned* v) {
    size_t i;
    *v = 0;
    for (i = 0; i < n; i++) {
        if (s[i] < '0' || s[i] > '9')
            return FALSE;
        *v = *v * 10 + (unsigned)(s[i] - '0');
    }
    return TRUE;
}

/* Validate a civil date-time and convert it to epoch seconds (UTC). */
static int _civil_to_time(long long y, unsigned mo, unsigned d, unsigned h, unsigned mi, unsigned se, time_t* t) {
    long long v;
    if (y < 1 || y > 9999 || mo < 1 || mo > 12 || d < 1 || d > _month_days(y, mo) || h > 23 || mi > 59 || se > 59)
        return FALSE;
    v = _days_from_civil(y, mo, d) * 86400 + (long long)h * 3600 + mi * 60 + se;
    if (sizeof(time_t) < 8 && (v > INT_MAX || v < INT_MIN))
        return FALSE;
    *t = (time_t)v;
    return TRUE;
}

int n_time_parse_http_date(const char* s, size_t len, time_t* t) {
    unsigned d, y, h, mi, se, mo = 0, k;
    time_t v;
    _n_civil c;
    /* "Sun, 06 Nov 1994 08:49:37 GMT" is exactly 29 bytes */
    if (!s || !t || len != 29 || s[3] != ',' || s[4] != ' ' || s[7] != ' ' || s[11] != ' ' || s[16] != ' ' ||
        s[19] != ':' || s[22] != ':' || s[25] != ' ' || memcmp(s + 26, "GMT", 3) != 0)
        return FALSE;
    for (k = 0; k < 12; k++)
        if (memcmp(s + 8, _month_names[k], 3) == 0)
            mo = k + 1;
    if (mo == 0 || !_digits(s + 5, 2, &d) || !_digits(s + 12, 4, &y) || !_digits(s + 17, 2, &h) ||
        !_digits(s + 20, 2, &mi) || !_digits(s + 23, 2, &se) || !_civil_to_time(y, mo, d, h, mi, se, &v))
        return FALSE;
    if (!_civil_of(v, 0, &c) || memcmp(s, _wday_names[c.wday_idx], 3) != 0)
        return FALSE;
    *t = v;
    return TRUE;
}

int n_time_parse_rfc3339(const char* s, size_t len, time_t* t) {
    unsigned y, mo, d, h, mi, se, oh = 0, om = 0;
    long offset = 0;
    time_t v;
    if (!s || !t || len < 19 || s[4] != '-' || s[7] != '-' || (s[10] != 'T' && s[10] != ' ' && s[10] != 't') ||
        s[13] != ':' || s[16] != ':')
        return FALSE;
    if (!_digits(s, 4, &y) || !_digits(s + 5, 2, &mo) || !_digits(s + 8, 2, &d) || !_digits(s + 11, 2, &h) ||
        !_digits(s + 14, 2, &mi) || !_digits(s + 17, 2, &se))
        return FALSE;
    if (len == 20 && (s[19] == 'Z' || s[19] == 'z')) {
        offset = 0;
    } else if (len == 25 && (s[19] == '+' || s[19] == '-') && s[22] == ':') {
        if (!_digits(s + 20, 2, &oh) || !_digits(s + 23, 2, &om) || oh > 23 || om > 59)
            return FALSE;
        offset = (long)(oh * 3600 + om * 60);
        if (s[19] == '-')
            offset = -offset;
    } else if (len != 19) {
        return FALSE;
    }
    if (!_civil_to_time(y, mo, d, h, mi, se, &v))
        return FALSE;
    *t = (time_t)((long long)v - offset);
    return TRUE;
}