scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

commit d9de09f797e53933d3e77323311ec3a89f0b965b
parent e5dca185c3eb30748b6a54b2d7e0981d1714a814
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date:   Thu, 30 Apr 2026 11:53:54 +0200

tests/libc: Add 0078-time

Co-authored-by: Hiltjo Posthuma <hiltjo@codemadness.org>

Diffstat:
Mtests/libc/execute/.gitignore | 1+
Atests/libc/execute/0078-time.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/libc/execute/libc-tests.lst | 1+
3 files changed, 163 insertions(+), 0 deletions(-)

diff --git a/tests/libc/execute/.gitignore b/tests/libc/execute/.gitignore @@ -76,3 +76,4 @@ test.log 0075-strftime 0076-localeconv 0077-setlocale +0078-time diff --git a/tests/libc/execute/0078-time.c b/tests/libc/execute/0078-time.c @@ -0,0 +1,161 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +/* +output: +testing +50592 dates compared, all OK +done +end: +*/ + + +#ifndef __unix__ +#define putenv(x) (x) +/* + * Ok, this is by definition undefined behaviour because + * we are using the name of a library function. The reallity + * is that if you use a static linker then this would + * overload the libc getenv and it will work as expected, + * but if you use a dynamic linker then the internal + * references will be tied to the internal getenv function + * and this would not work. For our main use case that is + * testing scc libc this is good enough, and with the + * fallback to putenv and unsetenv this will work in + * the systems we are interested on + */ +char * +getenv(const char *name) +{ + if (strcmp(name, "TZ") != 0) + return NULL; + return "UTC"; +} +#endif + +static long long +datetounix(long long year, int mon, int day, int hour, int min, int sec) +{ + /* seconds in a month in a regular (non-leap) year */ + static const long secs_through_month[] = { + 0, 31 * 86400, 59 * 86400, 90 * 86400, + 120 * 86400, 151 * 86400, 181 * 86400, 212 * 86400, + 243 * 86400, 273 * 86400, 304 * 86400, 334 * 86400 }; + int is_leap = 0, cycles, centuries = 0, leaps = 0, rem; + long long t; + + /* optimization: handle common range year 1902 up to and including 2038 */ + if (year - 2ULL < 136) { + /* amount of leap days relative to 1970: every 4 years */ + leaps = (year - 68) >> 2; + if (!((year - 68) & 3)) { + leaps--; + is_leap = 1; + } else { + is_leap = 0; + } + t = 31536000 * (year - 70) + (86400 * leaps); /* 365 * 86400 = 31536000 */ + } else { + /* general leap year calculation: + leap years occur mostly every 4 years but every 100 years + a leap year is skipped unless the year is divisible by 400 */ + cycles = (year - 100) / 400; + rem = (year - 100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + is_leap = 1; + } else { + if (rem >= 300) { + centuries = 3; + rem -= 300; + } else if (rem >= 200) { + centuries = 2; + rem -= 200; + } else if (rem >= 100) { + centuries = 1; + rem -= 100; + } + if (rem) { + leaps = rem / 4U; + rem %= 4U; + is_leap = !rem; + } + } + leaps += (97 * cycles) + (24 * centuries) - is_leap; + + /* adjust 8 leap days from 1970 up to and including 2000: + ((30 * 365) + 8) * 86400 = 946771200 */ + t = ((year - 100) * 31536000LL) + (leaps * 86400LL) + 946771200LL; + } + t += secs_through_month[mon]; + if (is_leap && mon >= 2) + t += 86400; + t += 86400LL * (day - 1); + t += 3600LL * hour; + t += 60LL * min; + t += sec; + + return t; +} + +void +testtm(struct tm *tm) +{ + time_t t1, t2; /* assume time_t is signed 64-bit */ + long long ll; + + t1 = mktime(tm); + if (t1 == (time_t)-1) { + printf("timegm failed for %d, %d, %d, %d, %d, %d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + } + + ll = datetounix(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + t2 = (time_t)ll; + + if (t1 != t2) { + printf("result: %lld != %lld\n", (long long)t1, (long long)t2); + printf("for: %d, %d, %d, %d, %d, %d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + exit(1); + } +} + +int +main(void) +{ + struct tm tm; + int y, mon, d; + unsigned counter = 0; + + puts("testing"); + putenv("TZ=UTC"); + + for (y = 1902; y < 2038; y++) { + for (mon = 0; mon < 12; mon++) { + for (d = 1; d <= 31; d++) { + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = y - 1900; + tm.tm_mon = mon - 1; + tm.tm_mday = d; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + + testtm(&tm); + counter++; + } + } + } + printf("%u dates compared, all OK\n", counter); + puts("done"); + + return 0; +} diff --git a/tests/libc/execute/libc-tests.lst b/tests/libc/execute/libc-tests.lst @@ -74,3 +74,4 @@ 0075-strftime 0076-localeconv 0077-setlocale +0078-time