Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_time.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
26
27#include "nilorea/n_time.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34#if defined(__windows__)
39void u_sleep(__int64 usec) {
40 HANDLE timer;
41 LARGE_INTEGER ft;
42
43 ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative value indicates relative time
44
45 timer = CreateWaitableTimer(NULL, TRUE, NULL);
46 SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
47 WaitForSingleObject(timer, INFINITE);
48 CloseHandle(timer);
49}
50#else
55void u_sleep(unsigned int usec) {
56 usleep(usec);
57}
58#endif
59
63void PAUSE(void) {
64 char k[2] = "";
65
66 printf("Press enter to continue...");
67
68 int n = scanf("%c", k);
69 int error = errno;
70 if (n == EOF) {
71 n_log(LOG_DEBUG, "Enter key was pressed !");
72 } else if (error != 0) {
73 n_log(LOG_ERR, "error %s when waiting for a key !", strerror(error));
74 } else {
75 n_log(LOG_DEBUG, "No matching characters");
76 }
77
78 printf("\n");
79} /* PAUSE(...) */
80
86int start_HiTimer(N_TIME* timer) {
87 __n_assert(timer, return FALSE);
88 timer->delta = 0;
89
90#if defined(__windows__)
91 if (QueryPerformanceFrequency((LARGE_INTEGER*)&timer->freq) == 0)
92 return FALSE;
93 if (QueryPerformanceCounter(&timer->startTime) == 0)
94 return FALSE;
95#else
96 if (gettimeofday(&timer->startTime, 0) != 0)
97 return FALSE;
98#endif
99
100 return TRUE;
101} /* init_HiTimer(...) */
102
108time_t get_usec(N_TIME* timer) {
109 __n_assert(timer, return 0);
110#ifdef __windows__
111 QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
112 timer->delta = 1000000 * (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
113 timer->startTime = timer->currentTime;
114#else
115 gettimeofday(&timer->currentTime, 0);
116 timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) * 1000000 + (timer->currentTime.tv_usec - timer->startTime.tv_usec);
117 timer->startTime.tv_sec = timer->currentTime.tv_sec;
118 timer->startTime.tv_usec = timer->currentTime.tv_usec;
119#endif
120
121 return timer->delta;
122} /* get_usec( ... ) */
123
129time_t get_msec(N_TIME* timer) {
130 __n_assert(timer, return 0);
131#ifdef __windows__
132 QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
133 timer->delta = 1000 * (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
134 timer->startTime = timer->currentTime;
135#else
136 gettimeofday(&timer->currentTime, 0);
137 timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) * 1000 + (timer->currentTime.tv_usec - timer->startTime.tv_usec) / 1000;
138 timer->startTime.tv_sec = timer->currentTime.tv_sec;
139 timer->startTime.tv_usec = timer->currentTime.tv_usec;
140#endif
141
142 return timer->delta;
143} /* get_msec(...) */
144
150time_t get_sec(N_TIME* timer) {
151 __n_assert(timer, return 0);
152#ifdef __windows__
153 QueryPerformanceCounter((LARGE_INTEGER*)&timer->currentTime);
154 timer->delta = (timer->currentTime.QuadPart - timer->startTime.QuadPart) / timer->freq.QuadPart;
155 timer->startTime = timer->currentTime;
156#else
157 gettimeofday(&timer->currentTime, 0);
158 timer->delta = (timer->currentTime.tv_sec - timer->startTime.tv_sec) + (timer->currentTime.tv_usec - timer->startTime.tv_usec) / 1000000;
159 timer->startTime.tv_sec = timer->currentTime.tv_sec;
160 timer->startTime.tv_usec = timer->currentTime.tv_usec;
161#endif
162 return timer->delta;
163} /* get_sec(...) */
164
165/* Calendar arithmetic for the date formats below: days since 1970-01-01 to a
166 * proleptic Gregorian date and back (H. Hinnant's algorithms), so no gmtime(),
167 * timegm(), TZ or locale is involved and every platform agrees. */
168static long long _days_from_civil(long long y, unsigned m, unsigned d) {
169 long long era;
170 unsigned yoe, doy, doe;
171 y -= m <= 2;
172 era = (y >= 0 ? y : y - 399) / 400;
173 yoe = (unsigned)(y - era * 400);
174 doy = (153 * (m + (m > 2 ? (unsigned)-3 : 9)) + 2) / 5 + d - 1;
175 doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
176 return era * 146097 + (long long)doe - 719468;
177}
178
179static void _civil_from_days(long long z, long long* y, unsigned* m, unsigned* d) {
180 long long era;
181 unsigned doe, yoe, doy, mp;
182 z += 719468;
183 era = (z >= 0 ? z : z - 146096) / 146097;
184 doe = (unsigned)(z - era * 146097);
185 yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
186 doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
187 mp = (5 * doy + 2) / 153;
188 *d = doy - (153 * mp + 2) / 5 + 1;
189 *m = mp < 10 ? mp + 3 : mp - 9;
190 *y = (long long)yoe + era * 400 + (*m <= 2);
191}
192
193static const char* const _wday_names[7] = {"Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed"};
194static const char* const _month_names[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
195 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
196
197static int _leap(long long y) {
198 return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
199}
200
201static unsigned _month_days(long long y, unsigned m) {
202 static const unsigned md[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
203 return m == 2 && _leap(y) ? 29u : md[m - 1];
204}
205
206/* Broken-down fields of t + offset, years 1..9999 only. */
207typedef struct {
208 long long year;
209 unsigned mon, mday, hour, min, sec, wday_idx;
210} _n_civil;
211
212static int _civil_of(time_t t, long offset, _n_civil* c) {
213 long long s = (long long)t + offset, days, rem;
214 days = s >= 0 ? s / 86400 : -((-s + 86399) / 86400);
215 rem = s - days * 86400;
216 _civil_from_days(days, &c->year, &c->mon, &c->mday);
217 if (c->year < 1 || c->year > 9999)
218 return FALSE;
219 c->hour = (unsigned)(rem / 3600);
220 c->min = (unsigned)(rem % 3600 / 60);
221 c->sec = (unsigned)(rem % 60);
222 c->wday_idx = (unsigned)(((days % 7) + 7) % 7); /* 0 = Thursday, 1970-01-01 */
223 return TRUE;
224}
225
226static int _offset_ok(long offset) {
227 return offset > -86400 && offset < 86400 && offset % 60 == 0;
228}
229
237int n_time_http_date(time_t t, char* out, size_t size) {
238 _n_civil c;
239 if (!out || size < N_TIME_HTTP_DATE_LEN || !_civil_of(t, 0, &c))
240 return FALSE;
241 snprintf(out, size, "%s, %02u %s %04lld %02u:%02u:%02u GMT", _wday_names[c.wday_idx], c.mday,
242 _month_names[c.mon - 1], c.year, c.hour, c.min, c.sec);
243 return TRUE;
244}
245
254int n_time_rfc2822(time_t t, long offset, char* out, size_t size) {
255 _n_civil c;
256 long a = offset < 0 ? -offset : offset;
257 if (!out || size < N_TIME_RFC2822_LEN || !_offset_ok(offset) || !_civil_of(t, offset, &c))
258 return FALSE;
259 snprintf(out, size, "%s, %02u %s %04lld %02u:%02u:%02u %c%02ld%02ld", _wday_names[c.wday_idx], c.mday,
260 _month_names[c.mon - 1], c.year, c.hour, c.min, c.sec, offset < 0 ? '-' : '+', a / 3600, a % 3600 / 60);
261 return TRUE;
262}
263
272int n_time_rfc3339(time_t t, long offset, char* out, size_t size) {
273 _n_civil c;
274 long a = offset < 0 ? -offset : offset;
275 if (!out || size < N_TIME_RFC3339_LEN || !_offset_ok(offset) || !_civil_of(t, offset, &c))
276 return FALSE;
277 snprintf(out, size, "%04lld-%02u-%02uT%02u:%02u:%02u%c%02ld:%02ld", c.year, c.mon, c.mday, c.hour, c.min,
278 c.sec, offset < 0 ? '-' : '+', a / 3600, a % 3600 / 60);
279 return TRUE;
280}
281
282/* Exactly n decimal digits at s, into *v. */
283static int _digits(const char* s, size_t n, unsigned* v) {
284 size_t i;
285 *v = 0;
286 for (i = 0; i < n; i++) {
287 if (s[i] < '0' || s[i] > '9')
288 return FALSE;
289 *v = *v * 10 + (unsigned)(s[i] - '0');
290 }
291 return TRUE;
292}
293
294/* Validate a civil date-time and convert it to epoch seconds (UTC). */
295static int _civil_to_time(long long y, unsigned mo, unsigned d, unsigned h, unsigned mi, unsigned se, time_t* t) {
296 long long v;
297 if (y < 1 || y > 9999 || mo < 1 || mo > 12 || d < 1 || d > _month_days(y, mo) || h > 23 || mi > 59 || se > 59)
298 return FALSE;
299 v = _days_from_civil(y, mo, d) * 86400 + (long long)h * 3600 + mi * 60 + se;
300 if (sizeof(time_t) < 8 && (v > INT_MAX || v < INT_MIN))
301 return FALSE;
302 *t = (time_t)v;
303 return TRUE;
304}
305
313int n_time_parse_http_date(const char* s, size_t len, time_t* t) {
314 unsigned d, y, h, mi, se, mo = 0, k;
315 time_t v;
316 _n_civil c;
317 /* "Sun, 06 Nov 1994 08:49:37 GMT" is exactly 29 bytes */
318 if (!s || !t || len != 29 || s[3] != ',' || s[4] != ' ' || s[7] != ' ' || s[11] != ' ' || s[16] != ' ' ||
319 s[19] != ':' || s[22] != ':' || s[25] != ' ' || memcmp(s + 26, "GMT", 3) != 0)
320 return FALSE;
321 for (k = 0; k < 12; k++)
322 if (memcmp(s + 8, _month_names[k], 3) == 0)
323 mo = k + 1;
324 if (mo == 0 || !_digits(s + 5, 2, &d) || !_digits(s + 12, 4, &y) || !_digits(s + 17, 2, &h) ||
325 !_digits(s + 20, 2, &mi) || !_digits(s + 23, 2, &se) || !_civil_to_time(y, mo, d, h, mi, se, &v))
326 return FALSE;
327 if (!_civil_of(v, 0, &c) || memcmp(s, _wday_names[c.wday_idx], 3) != 0)
328 return FALSE;
329 *t = v;
330 return TRUE;
331}
332
340int n_time_parse_rfc3339(const char* s, size_t len, time_t* t) {
341 unsigned y, mo, d, h, mi, se, oh = 0, om = 0;
342 long offset = 0;
343 time_t v;
344 if (!s || !t || len < 19 || s[4] != '-' || s[7] != '-' || (s[10] != 'T' && s[10] != ' ' && s[10] != 't') ||
345 s[13] != ':' || s[16] != ':')
346 return FALSE;
347 if (!_digits(s, 4, &y) || !_digits(s + 5, 2, &mo) || !_digits(s + 8, 2, &d) || !_digits(s + 11, 2, &h) ||
348 !_digits(s + 14, 2, &mi) || !_digits(s + 17, 2, &se))
349 return FALSE;
350 if (len == 20 && (s[19] == 'Z' || s[19] == 'z')) {
351 offset = 0;
352 } else if (len == 25 && (s[19] == '+' || s[19] == '-') && s[22] == ':') {
353 if (!_digits(s + 20, 2, &oh) || !_digits(s + 23, 2, &om) || oh > 23 || om > 59)
354 return FALSE;
355 offset = (long)(oh * 3600 + om * 60);
356 if (s[19] == '-')
357 offset = -offset;
358 } else if (len != 19) {
359 return FALSE;
360 }
361 if (!_civil_to_time(y, mo, d, h, mi, se, &v))
362 return FALSE;
363 *t = (time_t)((long long)v - offset);
364 return TRUE;
365}
static int h(void *ctx, const N_ROUTE_MATCH *m)
a handler that does nothing (the test only routes)
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
struct timeval startTime
start time
Definition n_time.h:61
time_t delta
time since last poll
Definition n_time.h:51
struct timeval currentTime
current time
Definition n_time.h:63
int n_time_parse_http_date(const char *s, size_t len, time_t *t)
parse an IMF-fixdate HTTP date strictly
Definition n_time.c:313
int n_time_rfc2822(time_t t, long offset, char *out, size_t size)
format t as an RFC 2822 date at offset seconds east of UTC
Definition n_time.c:254
int n_time_parse_rfc3339(const char *s, size_t len, time_t *t)
parse "YYYY-MM-DD[T| ]HH:MM:SS[Z|+hh:mm|-hh:mm]" (UTC without a zone)
Definition n_time.c:340
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
Definition n_time.c:55
int start_HiTimer(N_TIME *timer)
Initialize or restart from zero any N_TIME HiTimer.
Definition n_time.c:86
#define N_TIME_RFC2822_LEN
buffer size for n_time_rfc2822(): "Thu, 01 Jan 1970 00:00:00 +0000" + NUL
Definition n_time.h:92
#define N_TIME_HTTP_DATE_LEN
buffer size for n_time_http_date(): "Thu, 01 Jan 1970 00:00:00 GMT" + NUL
Definition n_time.h:90
time_t get_usec(N_TIME *timer)
Poll any N_TIME HiTimer, returning usec, and moving currentTime to startTime.
Definition n_time.c:108
time_t get_msec(N_TIME *timer)
Poll any N_TIME HiTimer, returning msec, and moving currentTime to startTime.
Definition n_time.c:129
time_t get_sec(N_TIME *timer)
Poll any N_TIME HiTimer, returning sec, and moving currentTime to startTime.
Definition n_time.c:150
int n_time_http_date(time_t t, char *out, size_t size)
format t as an HTTP date (RFC 9110 IMF-fixdate, always GMT)
Definition n_time.c:237
int n_time_rfc3339(time_t t, long offset, char *out, size_t size)
format t as RFC 3339 at offset seconds east of UTC ("+00:00" for UTC)
Definition n_time.c:272
void PAUSE(void)
make a pause in a terminal
Definition n_time.c:63
#define N_TIME_RFC3339_LEN
buffer size for n_time_rfc3339(): "1970-01-01T00:00:00+00:00" + NUL
Definition n_time.h:94
Timing Structure.
Definition n_time.h:49
static int _offset_ok(long offset)
Definition n_time.c:226
long long year
Definition n_time.c:208
static int _civil_of(time_t t, long offset, _n_civil *c)
Definition n_time.c:212
unsigned mon
Definition n_time.c:209
static const char *const _wday_names[7]
Definition n_time.c:193
static long long _days_from_civil(long long y, unsigned m, unsigned d)
Definition n_time.c:168
unsigned mday
Definition n_time.c:209
static int _civil_to_time(long long y, unsigned mo, unsigned d, unsigned h, unsigned mi, unsigned se, time_t *t)
Definition n_time.c:295
unsigned min
Definition n_time.c:209
unsigned hour
Definition n_time.c:209
static int _digits(const char *s, size_t n, unsigned *v)
Definition n_time.c:283
static unsigned _month_days(long long y, unsigned m)
Definition n_time.c:201
static void _civil_from_days(long long z, long long *y, unsigned *m, unsigned *d)
Definition n_time.c:179
unsigned sec
Definition n_time.c:209
static int _leap(long long y)
Definition n_time.c:197
unsigned wday_idx
Definition n_time.c:209
static const char *const _month_names[12]
Definition n_time.c:194
Timing utilities.