scc

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

commit 4dc7e1ae7b8c34bdb9847e902a6e2701a9bd43c3
parent 1c2ad3584e9473c7327a908b2435fdcdbe535777
Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
Date:   Sat, 18 Apr 2026 20:01:12 +0200

libc/time: Rework the TZ parser

Many bugs were found and it made it more similar to the POSIX
specification.

Diffstat:
Msrc/libc/arch/posix/_tzone.c | 89++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/libc/libc.h | 29++++++++++++++++-------------
Msrc/libc/time/gentz | 53+++++++++++++++++++++++++++--------------------------
Msrc/libc/time/localtime.c | 2+-
4 files changed, 94 insertions(+), 79 deletions(-)

diff --git a/src/libc/arch/posix/_tzone.c b/src/libc/arch/posix/_tzone.c @@ -1,3 +1,4 @@ +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -21,11 +22,10 @@ int _daylight; char *_tzname[2]; long _timezone, _dstzone; -static char st[TOKENSIZ], ds[TOKENSIZ], tokstr[TOKENSIZ]; static int tok; +static char tokstr[TOKENSIZ]; -static int start, end; -static int julian; +static int julian, start, end; static int next(char *str) @@ -89,8 +89,6 @@ num(int max) { int n; - if (tok == EOS) - return 0; if (tok != NUM) return -1; n = atoi(tokstr); @@ -102,21 +100,18 @@ num(int max) static long offset(void) { - int sign = 1; - int n; long off; + int n, sign = 1; if (tok == EOS) return -1; switch (tok) { - case '+': - sign = -1; case '-': + sign = -1; + case '+': next(NULL); break; - default: - return -1; } if ((n = num(24)) < 0) @@ -127,16 +122,14 @@ offset(void) goto ret; if (!accept(':')) - return -1; + goto ret; if ((n = num(60)) < 0) return -1; off += n * SECMIN; next(NULL); - if (tok == EOS) - goto ret; if (!accept(':')) - return -1; + goto ret; if ((n = num(60)) < 0) return -1; off += n; @@ -150,14 +143,16 @@ static int std(void) { long off; + char *name; - if (tok != STR) + if ((name = _gmtoff(tokstr)) == NULL) return 0; - strcpy(st, tokstr); - next(NULL); + next(NULL); if ((off = offset()) == -1) return 0; + + _tzname[0] = name; _timezone = off; return 1; @@ -167,12 +162,13 @@ static int dst(void) { long off; + char *name; - if (tok != STR) + if ((name = _gmtoff(tokstr)) == NULL) return 0; - strcpy(ds, tokstr); - next(NULL); + _tzname[1] = name; + next(NULL); if ((off = offset()) == -1) _dstzone = _timezone + SECHOUR; else @@ -214,8 +210,6 @@ yday(void) static int rule(void) { - if (tok == EOS) - return 0; if (!accept(',')) return 0; if ((start = yday()) == -1) @@ -230,37 +224,51 @@ void _tzset(void) { static char *tz; + static char cache[80]; char *s = getenv("TZ"); - if (s && tz && strcmp(s, tz) == 0) - return; - tz = s; - - _daylight = 0; - _tzname[1] = _tzname[0] = "UTC"; - _timezone = _dstzone = -1; + if (s) { + if (tz && strcmp(s, tz) == 0) + return; + snprintf(cache, sizeof(cache), "%s", s); + tz = cache; + } start = end = -1; - julian = 0; - if (!tz) + if (!s) goto adjust; - next(tz); + + if (next(s) == EOS) + goto error; if (!std()) + goto error; + if (tok == EOS) goto adjust; + if (!dst()) + goto error; + if (tok == EOS) goto adjust; + if (!rule()) - goto adjust; + goto error; adjust: - if (!_daylight) + if (!_daylight) { _tzname[1] = _tzname[0]; - if (_timezone == -1) - _timezone = _gmtoff(_tzname[0]); - if (_dstzone == -1) - _dstzone = _gmtoff(_tzname[1]); + _dstzone = _timezone; + } + + return; + +error: + _daylight = 0; + _tzname[1] = _tzname[0] = "UTC"; + _timezone = _dstzone = 0; + start = end = -1; + julian = 0; } int @@ -268,6 +276,9 @@ _isdst(struct tm *tm) { int yday; + if (!_daylight) + return 0; + yday = tm->tm_yday; if (julian && yday+1 < 60 || FEBDAYS(tm->tm_year) < 29) diff --git a/src/libc/libc.h b/src/libc/libc.h @@ -12,15 +12,6 @@ FILE *_fpopen(const char * restrict, const char *restrict, #endif #ifdef _TIME_H -enum { - SUN, - MON, - TUE, - WED, - THU, - FRI, - SAT -}; #define JAN 0 #define FEB 1 @@ -33,18 +24,30 @@ enum { #define SECHOUR (60 * SECMIN) /* 3600 */ #define SECDAY (24 * SECHOUR) /* 86400 */ +enum { + SUN, + MON, + TUE, + WED, + THU, + FRI, + SAT +}; + +struct tzone; + /* _tzone.c variables */ -extern time_t _tzstdoff, _tzdstoff; -extern time_t _tzstart, _tzend; +extern int _daylight; extern char *_tzname[2]; -extern int _tzjulian; +extern long _timezone, _dstzone; extern int _daysmon[12]; -long _gmtoff(char *); void _tzset(void); +int _isdst(struct tm *); int _daysyear(int); int _newyear(int); +char *_gmtoff(char *); #endif extern unsigned _exitn; diff --git a/src/libc/time/gentz b/src/libc/time/gentz @@ -1,39 +1,40 @@ #!/bin/sh -cat <<'EOF' +cat <<EOF +#include <stdlib.h> #include <string.h> #include <time.h> #include "../libc.h" -struct tzone { - char *name; - long gmtoff; -}; - -static struct tzone tzones[]; - -long -_gmtoff(char *tz) +` +awk '{ + zones[$1] = 1 + if (length($1) > max) + max = length($1) + nr++ +} +END { + printf("#define MAX %d\n", max+1); + printf("#define NR %d\n", nr); + print("static char tzones[NR][MAX] = {"); + for (z in zones) + printf("\t\\"%s\\",\n", z) | "sort" + close("sort") + print "};\n" +}' "$@"` + +static int +cmp(const void *ps1, const void *ps2) { - struct tzone *t; + const char *s1 = ps1, *s2 = ps2; - for (t = tzones; t->name; t++) { - if (!strcmp(t->name, tz)) - return t->gmtoff; - } - return 0; + return strcmp(s1, s2); } -EOF - -awk ' -BEGIN {print "static struct tzone tzones[] = {" } +char * +_gmtoff(char *tz) { - split($2,a,":") - min = a[1] * 60 - min = min + a[2] - sec = min * 60 - printf "\t{\"%s\", %d},\n", $1, sec + return bsearch(tz, tzones, NR, MAX, cmp); } -END { print "\t{0,\t0}\n};\n" }' "$@" +EOF diff --git a/src/libc/time/localtime.c b/src/libc/time/localtime.c @@ -27,7 +27,7 @@ localtime(const time_t *timep) name = _tzname[0]; } - t += off; + t -= off; tm = gmtime(&t); tm->tm_zone = name; tm->tm_isdst = dst;