_tzone.c (3902B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <time.h> 5 6 #include "../../libc.h" 7 8 #define TOKENSIZ 10 9 10 enum { 11 EOS, 12 NUM, 13 STR, 14 }; 15 16 enum { 17 GREGORIAN, 18 JULIAN, 19 }; 20 21 int _daylight; 22 char *_tzname[2]; 23 long _timezone, _dstzone; 24 25 static int tok; 26 static char tokstr[TOKENSIZ]; 27 28 static int sjulian, start; 29 static int ejulian, end; 30 static long shour, ehour; 31 32 static int 33 next(char *str) 34 { 35 int n, t; 36 static char *s; 37 38 if (str) 39 s = str; 40 41 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 } 72 73 if (n >= TOKENSIZ-1) 74 return -1; 75 memcpy(tokstr, s, n); 76 tokstr[n] = '\0'; 77 s += n; 78 79 return tok = t; 80 } 81 82 static int 83 accept(int c) 84 { 85 if (tok != c) 86 return 0; 87 return next(NULL); 88 } 89 90 static int 91 num(int max) 92 { 93 int n; 94 95 if (tok != NUM) 96 return -1; 97 n = atoi(tokstr); 98 if (n < 0 || n > max) 99 return -1; 100 return n; 101 } 102 103 static long 104 offset(void) 105 { 106 long off; 107 int n, sign = 1; 108 109 if (tok == EOS) 110 return -1; 111 112 switch (tok) { 113 case '-': 114 sign = -1; 115 case '+': 116 next(NULL); 117 break; 118 } 119 120 if ((n = num(24)) < 0) 121 return -1; 122 off = n * SECHOUR; 123 next(NULL); 124 if (tok == EOS) 125 goto ret; 126 127 if (!accept(':')) 128 goto ret; 129 if ((n = num(60)) < 0) 130 return -1; 131 off += n * SECMIN; 132 next(NULL); 133 134 if (!accept(':')) 135 goto ret; 136 if ((n = num(60)) < 0) 137 return -1; 138 off += n; 139 next(NULL); 140 141 ret: 142 return sign * off; 143 } 144 145 static int 146 std(void) 147 { 148 long off; 149 char *name; 150 151 if ((name = _gmtoff(tokstr)) == NULL) 152 return 0; 153 154 next(NULL); 155 if ((off = offset()) == -1) 156 return 0; 157 158 _tzname[0] = name; 159 _timezone = off; 160 161 return 1; 162 } 163 164 static int 165 dst(void) 166 { 167 long off; 168 char *name; 169 170 if ((name = _gmtoff(tokstr)) == NULL) 171 return 0; 172 _tzname[1] = name; 173 174 next(NULL); 175 if ((off = offset()) == -1) 176 _dstzone = _timezone - SECHOUR; 177 else 178 _dstzone = off; 179 180 return _daylight = 1; 181 } 182 183 static int 184 yday(int *julian, long *hour) 185 { 186 long off; 187 int type, n; 188 189 if (tok == STR && !strcmp(tokstr, "J")) 190 type = JULIAN; 191 else if (tok == NUM) 192 type = GREGORIAN; 193 else 194 return -1; 195 196 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 } 219 220 static int 221 rule(void) 222 { 223 shour = ehour = 2 * SECHOUR; 224 225 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 } 234 235 void 236 _tzset(void) 237 { 238 static char *tz; 239 static char cache[80]; 240 char *s = getenv("TZ"); 241 242 if (s) { 243 if (tz && strcmp(s, tz) == 0) 244 return; 245 snprintf(cache, sizeof(cache), "%s", s); 246 tz = cache; 247 } 248 249 if (!s || next(s) == EOS) 250 goto error; 251 252 start = end = -1; 253 254 if (!std()) 255 goto error; 256 if (tok == EOS) 257 goto adjust; 258 259 if (!dst()) 260 goto error; 261 if (tok == EOS) 262 goto adjust; 263 264 if (!rule()) 265 goto error; 266 if (tok != EOS) 267 goto error; 268 269 adjust: 270 if (!_daylight) { 271 _tzname[1] = _tzname[0]; 272 _dstzone = _timezone; 273 } 274 275 return; 276 277 error: 278 _daylight = 0; 279 _tzname[1] = _tzname[0] = "UTC"; 280 _timezone = _dstzone = 0; 281 } 282 283 int 284 _isdst(struct tm *tm) 285 { 286 long off; 287 int day, bis, yday; 288 289 if (!_daylight) 290 return 0; 291 292 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; 297 298 day = start; 299 if (sjulian && bis && day >= 59) 300 day++; 301 if (yday < day || yday == day && off < shour) 302 return 0; 303 304 day = end; 305 if (ejulian && bis && day >= 59) 306 day++; 307 if (yday > day || yday == day && off > ehour) 308 return 0; 309 310 return 1; 311 }