1#include <stdio.h>2#include <stdlib.h>3#include <string.h>4#include <time.h>56#include "../../libc.h"78#define TOKENSIZ 10910enum {11 EOS,12 NUM,13 STR,14};1516enum {17 GREGORIAN,18 JULIAN,19};2021int _daylight;22char *_tzname[2];23long _timezone, _dstzone;2425static int tok;26static char tokstr[TOKENSIZ];2728static int sjulian, start;29static int ejulian, end;30static long shour, ehour;3132static int33next(char *str)34{35 int n, t;36 static char *s;3738 if (str)39 s = str;4041 switch (*s) {42 case '0':43 case '1':44 case '2':45 case '3':46 case '4':47 case '5':48 case '6':49 case '7':50 case '8':51 case '9':52 n = strspn(s, "0123456789");53 t = NUM;54 break;55 case '/':56 case '+':57 case '-':58 case ':':59 case ',':60 n = 1;61 t = *s;62 break;63 case '\0':64 n = 0;65 t = EOS;66 break;67 default:68 n = strcspn(s, "+-0123456789,/");69 t = STR;70 break;71 }7273 if (n >= TOKENSIZ-1)74 return -1;75 memcpy(tokstr, s, n);76 tokstr[n] = '\0';77 s += n;7879 return tok = t;80}8182static int83accept(int c)84{85 if (tok != c)86 return 0;87 return next(NULL);88}8990static int91num(int max)92{93 int n;9495 if (tok != NUM)96 return -1;97 n = atoi(tokstr);98 if (n < 0 || n > max)99 return -1;100 return n;101}102103static long104offset(void)105{106 long off;107 int n, sign = 1;108109 if (tok == EOS)110 return -1;111112 switch (tok) {113 case '-':114 sign = -1;115 case '+':116 next(NULL);117 break;118 }119120 if ((n = num(24)) < 0)121 return -1;122 off = n * SECHOUR;123 next(NULL);124 if (tok == EOS)125 goto ret;126127 if (!accept(':'))128 goto ret;129 if ((n = num(60)) < 0)130 return -1;131 off += n * SECMIN;132 next(NULL);133134 if (!accept(':'))135 goto ret;136 if ((n = num(60)) < 0)137 return -1;138 off += n;139 next(NULL);140141 ret:142 return sign * off;143}144145static int146std(void)147{148 long off;149 char *name;150151 if ((name = _gmtoff(tokstr)) == NULL)152 return 0;153154 next(NULL);155 if ((off = offset()) == -1)156 return 0;157158 _tzname[0] = name;159 _timezone = off;160161 return 1;162}163164static int165dst(void)166{167 long off;168 char *name;169170 if ((name = _gmtoff(tokstr)) == NULL)171 return 0;172 _tzname[1] = name;173174 next(NULL);175 if ((off = offset()) == -1)176 _dstzone = _timezone - SECHOUR;177 else178 _dstzone = off;179180 return _daylight = 1;181}182183static int184yday(int *julian, long *hour)185{186 long off;187 int type, n;188189 if (tok == STR && !strcmp(tokstr, "J"))190 type = JULIAN;191 else if (tok == NUM)192 type = GREGORIAN;193 else194 return -1;195196 switch (type) {197 case JULIAN:198 *julian = 1;199 next(NULL);200 n = num(365);201 next(NULL);202 if (n == 0)203 return -1;204 n--;205 break;206 case GREGORIAN:207 *julian = 0;208 n = num(365);209 next(NULL);210 break;211 }212 if (accept('/')) {213 if ((off = offset()) < 0)214 return -1;215 *hour = off;216 }217 return n;218}219220static int221rule(void)222{223 shour = ehour = 2 * SECHOUR;224225 if (!accept(','))226 return 0;227 if ((start = yday(&sjulian, &shour)) == -1)228 return 0;229 if (!accept(','))230 return 0;231 if ((end = yday(&ejulian, &ehour)) == -1)232 return 0;233}234235void236_tzset(void)237{238 static char *tz;239 static char cache[80];240 char *s = getenv("TZ");241242 if (s) {243 if (tz && strcmp(s, tz) == 0)244 return;245 snprintf(cache, sizeof(cache), "%s", s);246 tz = cache;247 }248249 if (!s || next(s) == EOS)250 goto error;251252 start = end = -1;253254 if (!std())255 goto error;256 if (tok == EOS)257 goto adjust;258259 if (!dst())260 goto error;261 if (tok == EOS)262 goto adjust;263264 if (!rule())265 goto error;266 if (tok != EOS)267 goto error;268269adjust:270 if (!_daylight) {271 _tzname[1] = _tzname[0];272 _dstzone = _timezone;273 }274275 return;276277error:278 _daylight = 0;279 _tzname[1] = _tzname[0] = "UTC";280 _timezone = _dstzone = 0;281}282283int284_isdst(struct tm *tm)285{286 long off;287 int day, bis, yday;288289 if (!_daylight)290 return 0;291292 bis = FEBDAYS(tm->tm_year) == 29;293 yday = tm->tm_yday;294 off = tm->tm_hour * SECHOUR;295 off += tm->tm_min * SECMIN;296 off += tm->tm_sec;297298 day = start;299 if (sjulian && bis && day >= 59)300 day++;301 if (yday < day || yday == day && off < shour)302 return 0;303304 day = end;305 if (ejulian && bis && day >= 59)306 day++;307 if (yday > day || yday == day && off > ehour)308 return 0;309310 return 1;311}