_tzone.c (3434B)
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 julian, start, end; 29 30 static int 31 next(char *str) 32 { 33 int n, t; 34 static char *s; 35 36 if (str) 37 s = str; 38 39 switch (*s) { 40 case '0': 41 case '1': 42 case '2': 43 case '3': 44 case '4': 45 case '5': 46 case '6': 47 case '7': 48 case '8': 49 case '9': 50 n = strspn(s, "0123456789"); 51 t = NUM; 52 break; 53 case '+': 54 case '-': 55 case ':': 56 case ',': 57 n = 1; 58 t = *s; 59 break; 60 case '\0': 61 n = 0; 62 t = EOS; 63 break; 64 default: 65 n = strcspn(s, "+-0123456789"); 66 t = STR; 67 break; 68 } 69 70 if (n >= TOKENSIZ-1) 71 return -1; 72 memcpy(tokstr, s, n); 73 tokstr[n] = '\0'; 74 s += n; 75 76 return tok = t; 77 } 78 79 static int 80 accept(int c) 81 { 82 if (tok != c) 83 return 0; 84 return next(NULL); 85 } 86 87 static int 88 num(int max) 89 { 90 int n; 91 92 if (tok != NUM) 93 return -1; 94 n = atoi(tokstr); 95 if (n < 0 || n > max) 96 return -1; 97 return n; 98 } 99 100 static long 101 offset(void) 102 { 103 long off; 104 int n, sign = 1; 105 106 if (tok == EOS) 107 return -1; 108 109 switch (tok) { 110 case '-': 111 sign = -1; 112 case '+': 113 next(NULL); 114 break; 115 } 116 117 if ((n = num(24)) < 0) 118 return -1; 119 off = n * SECHOUR; 120 next(NULL); 121 if (tok == EOS) 122 goto ret; 123 124 if (!accept(':')) 125 goto ret; 126 if ((n = num(60)) < 0) 127 return -1; 128 off += n * SECMIN; 129 next(NULL); 130 131 if (!accept(':')) 132 goto ret; 133 if ((n = num(60)) < 0) 134 return -1; 135 off += n; 136 next(NULL); 137 138 ret: 139 return sign * off; 140 } 141 142 static int 143 std(void) 144 { 145 long off; 146 char *name; 147 148 if ((name = _gmtoff(tokstr)) == NULL) 149 return 0; 150 151 next(NULL); 152 if ((off = offset()) == -1) 153 return 0; 154 155 _tzname[0] = name; 156 _timezone = off; 157 158 return 1; 159 } 160 161 static int 162 dst(void) 163 { 164 long off; 165 char *name; 166 167 if ((name = _gmtoff(tokstr)) == NULL) 168 return 0; 169 _tzname[1] = name; 170 171 next(NULL); 172 if ((off = offset()) == -1) 173 _dstzone = _timezone + SECHOUR; 174 else 175 _dstzone = off; 176 177 return _daylight = 1; 178 } 179 180 static int 181 yday(void) 182 { 183 int type, n; 184 185 if (tok == STR && !strcmp(tokstr, "J")) 186 type = JULIAN; 187 else if (tok == NUM) 188 type = GREGORIAN; 189 else 190 return -1; 191 192 switch (type) { 193 case JULIAN: 194 next(NULL); 195 n = num(365); 196 next(NULL); 197 if (n == 0) 198 return -1; 199 julian = 1; 200 break; 201 case GREGORIAN: 202 n = num(365); 203 next(NULL); 204 break; 205 } 206 207 return n; 208 } 209 210 static int 211 rule(void) 212 { 213 if (!accept(',')) 214 return 0; 215 if ((start = yday()) == -1) 216 return 0; 217 if (!accept(',')) 218 return 0; 219 if ((end = yday()) == -1) 220 return 0; 221 } 222 223 void 224 _tzset(void) 225 { 226 static char *tz; 227 static char cache[80]; 228 char *s = getenv("TZ"); 229 230 if (s) { 231 if (tz && strcmp(s, tz) == 0) 232 return; 233 snprintf(cache, sizeof(cache), "%s", s); 234 tz = cache; 235 } 236 237 start = end = -1; 238 239 if (!s) 240 goto adjust; 241 242 if (next(s) == EOS) 243 goto error; 244 245 if (!std()) 246 goto error; 247 if (tok == EOS) 248 goto adjust; 249 250 if (!dst()) 251 goto error; 252 if (tok == EOS) 253 goto adjust; 254 255 if (!rule()) 256 goto error; 257 258 adjust: 259 if (!_daylight) { 260 _tzname[1] = _tzname[0]; 261 _dstzone = _timezone; 262 } 263 264 return; 265 266 error: 267 _daylight = 0; 268 _tzname[1] = _tzname[0] = "UTC"; 269 _timezone = _dstzone = 0; 270 start = end = -1; 271 julian = 0; 272 } 273 274 int 275 _isdst(struct tm *tm) 276 { 277 int yday; 278 279 if (!_daylight) 280 return 0; 281 282 yday = tm->tm_yday; 283 284 if (julian && yday+1 < 60 || FEBDAYS(tm->tm_year) < 29) 285 yday++; 286 if (yday >= start && yday <= end && tm->tm_hour >= 2) 287 return 1; 288 return 0; 289 }