1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * time conversion support 28 */ 29 30 #include <tm.h> 31 #include <ctype.h> 32 #include <namval.h> 33 34 #include "FEATURE/tmlib" 35 36 #ifndef tzname 37 # if defined(__DYNAMIC__) 38 # define tzname __DYNAMIC__(tzname) 39 # else 40 # if !_dat_tzname 41 # if _dat__tzname 42 # undef _dat_tzname 43 # define _dat_tzname 1 44 # define tzname _tzname 45 # endif 46 # endif 47 # endif 48 # if _dat_tzname 49 extern char* tzname[]; 50 # endif 51 #endif 52 53 #define TM_type (-1) 54 55 static const Namval_t options[] = 56 { 57 "adjust", TM_ADJUST, 58 "format", TM_DEFAULT, 59 "leap", TM_LEAP, 60 "subsecond", TM_SUBSECOND, 61 "type", TM_type, 62 "utc", TM_UTC, 63 0, 0 64 }; 65 66 /* 67 * 2007-03-19 move tm_info from _tm_info_ to (*_tm_infop_) 68 * to allow future Tm_info_t growth 69 * by 2009 _tm_info_ can be static 70 */ 71 72 #if _BLD_ast && defined(__EXPORT__) 73 #define extern extern __EXPORT__ 74 #endif 75 76 extern Tm_info_t _tm_info_; 77 78 #undef extern 79 80 Tm_info_t _tm_info_ = { 0 }; 81 82 __EXTERN__(Tm_info_t, _tm_info_); 83 84 __EXTERN__(Tm_info_t*, _tm_infop_); 85 86 Tm_info_t* _tm_infop_ = &_tm_info_; 87 88 #if _tzset_environ 89 90 static char TZ[256]; 91 static char* TE[2]; 92 93 struct tm* 94 _tm_localtime(const time_t* t) 95 { 96 struct tm* r; 97 char* e; 98 char** v = environ; 99 100 if (TZ[0]) 101 { 102 if (!environ || !*environ) 103 environ = TE; 104 else 105 e = environ[0]; 106 environ[0] = TZ; 107 } 108 r = localtime(t); 109 if (TZ[0]) 110 { 111 if (environ != v) 112 environ = v; 113 else 114 environ[0] = e; 115 } 116 return r; 117 } 118 119 #endif 120 121 /* 122 * return minutes west of GMT for local time clock 123 * 124 * isdst will point to non-zero if DST is in effect 125 * this routine also kicks in the local initialization 126 */ 127 128 static int 129 tzwest(time_t* clock, int* isdst) 130 { 131 register struct tm* tp; 132 register int n; 133 register int m; 134 int h; 135 time_t epoch; 136 137 /* 138 * convert to GMT assuming local time 139 */ 140 141 if (!(tp = gmtime(clock))) 142 { 143 /* 144 * some systems return 0 for negative time_t 145 */ 146 147 epoch = 0; 148 clock = &epoch; 149 tp = gmtime(clock); 150 } 151 n = tp->tm_yday; 152 h = tp->tm_hour; 153 m = tp->tm_min; 154 155 /* 156 * tmlocaltime() handles DST and GMT offset 157 */ 158 159 tp = tmlocaltime(clock); 160 if (n = tp->tm_yday - n) 161 { 162 if (n > 1) 163 n = -1; 164 else if (n < -1) 165 n = 1; 166 } 167 *isdst = tp->tm_isdst; 168 return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min; 169 } 170 171 /* 172 * stropt() option handler 173 */ 174 175 static int 176 tmopt(void* a, const void* p, int n, const char* v) 177 { 178 Tm_zone_t* zp; 179 180 NoP(a); 181 if (p) 182 switch (((Namval_t*)p)->value) 183 { 184 case TM_DEFAULT: 185 tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT]; 186 break; 187 case TM_type: 188 tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0; 189 break; 190 default: 191 if (n) 192 tm_info.flags |= ((Namval_t*)p)->value; 193 else 194 tm_info.flags &= ~((Namval_t*)p)->value; 195 break; 196 } 197 return 0; 198 } 199 200 /* 201 * initialize the local timezone 202 */ 203 204 static void 205 tmlocal(void) 206 { 207 register Tm_zone_t* zp; 208 register int n; 209 register char* s; 210 register char* e; 211 int i; 212 int m; 213 int isdst; 214 char* t; 215 struct tm* tp; 216 time_t now; 217 char buf[16]; 218 219 static Tm_zone_t local; 220 221 #if _tzset_environ 222 { 223 char** v = environ; 224 225 if (s = getenv("TZ")) 226 { 227 sfsprintf(TZ, sizeof(TZ), "TZ=%s", s); 228 if (!environ || !*environ) 229 environ = TE; 230 else 231 e = environ[0]; 232 environ[0] = TZ; 233 } 234 else 235 { 236 TZ[0] = 0; 237 e = 0; 238 } 239 #endif 240 #if _lib_tzset 241 tzset(); 242 #endif 243 #if _tzset_environ 244 if (environ != v) 245 environ = v; 246 else if (e) 247 environ[0] = e; 248 } 249 #endif 250 #if _dat_tzname 251 local.standard = strdup(tzname[0]); 252 local.daylight = strdup(tzname[1]); 253 #endif 254 tmlocale(); 255 256 /* 257 * tm_info.local 258 */ 259 260 tm_info.zone = tm_info.local = &local; 261 time(&now); 262 n = tzwest(&now, &isdst); 263 264 /* 265 * compute local DST offset by roaming 266 * through the last 12 months until tzwest() changes 267 */ 268 269 for (i = 0; i < 12; i++) 270 { 271 now -= 31 * 24 * 60 * 60; 272 if ((m = tzwest(&now, &isdst)) != n) 273 { 274 if (!isdst) 275 { 276 isdst = n; 277 n = m; 278 m = isdst; 279 } 280 m -= n; 281 break; 282 } 283 } 284 local.west = n; 285 local.dst = m; 286 287 /* 288 * now get the time zone names 289 */ 290 291 #if _dat_tzname 292 if (tzname[0]) 293 { 294 /* 295 * POSIX 296 */ 297 298 if (!local.standard) 299 local.standard = strdup(tzname[0]); 300 if (!local.daylight) 301 local.daylight = strdup(tzname[1]); 302 } 303 else 304 #endif 305 if ((s = getenv("TZNAME")) && *s && (s = strdup(s))) 306 { 307 /* 308 * BSD 309 */ 310 311 local.standard = s; 312 if (s = strchr(s, ',')) 313 *s++ = 0; 314 else 315 s = ""; 316 local.daylight = s; 317 } 318 else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s))) 319 { 320 /* 321 * POSIX style but skipped by tmlocaltime() 322 */ 323 324 local.standard = s; 325 if (*++s && *++s && *++s) 326 { 327 *s++ = 0; 328 tmgoff(s, &t, 0); 329 for (s = t; isalpha(*t); t++); 330 *t = 0; 331 } 332 else 333 s = ""; 334 local.daylight = s; 335 } 336 else 337 { 338 /* 339 * tm_data.zone table lookup 340 */ 341 342 t = 0; 343 for (zp = tm_data.zone; zp->standard; zp++) 344 { 345 if (zp->type) 346 t = zp->type; 347 if (zp->west == n && zp->dst == m) 348 { 349 local.type = t; 350 local.standard = zp->standard; 351 if (!(s = zp->daylight)) 352 { 353 e = (s = buf) + sizeof(buf); 354 s = tmpoff(s, e - s, zp->standard, 0, 0); 355 if (s < e - 1) 356 { 357 *s++ = ' '; 358 tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST); 359 } 360 s = strdup(buf); 361 } 362 local.daylight = s; 363 break; 364 } 365 } 366 if (!zp->standard) 367 { 368 /* 369 * not in the table 370 */ 371 372 e = (s = buf) + sizeof(buf); 373 s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0); 374 local.standard = strdup(buf); 375 if (s < e - 1) 376 { 377 *s++ = ' '; 378 tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST); 379 local.daylight = strdup(buf); 380 } 381 } 382 } 383 384 /* 385 * set the options 386 */ 387 388 stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL); 389 390 /* 391 * the time zone type is probably related to the locale 392 */ 393 394 if (!local.type) 395 { 396 s = local.standard; 397 t = 0; 398 for (zp = tm_data.zone; zp->standard; zp++) 399 { 400 if (zp->type) 401 t = zp->type; 402 if (tmword(s, NiL, zp->standard, NiL, 0)) 403 { 404 local.type = t; 405 break; 406 } 407 } 408 } 409 410 /* 411 * tm_info.flags 412 */ 413 414 if (!(tm_info.flags & TM_ADJUST)) 415 { 416 now = (time_t)78811200; /* Jun 30 1972 23:59:60 */ 417 tp = tmlocaltime(&now); 418 if (tp->tm_sec != 60) 419 tm_info.flags |= TM_ADJUST; 420 } 421 if (!(tm_info.flags & TM_UTC)) 422 { 423 s = local.standard; 424 zp = tm_data.zone; 425 if (local.daylight) 426 zp++; 427 for (; !zp->type && zp->standard; zp++) 428 if (tmword(s, NiL, zp->standard, NiL, 0)) 429 { 430 tm_info.flags |= TM_UTC; 431 break; 432 } 433 } 434 } 435 436 /* 437 * initialize tm data 438 */ 439 440 void 441 tminit(register Tm_zone_t* zp) 442 { 443 static uint32_t serial = ~(uint32_t)0; 444 445 if (serial != ast.env_serial) 446 { 447 serial = ast.env_serial; 448 if (tm_info.local) 449 { 450 memset(tm_info.local, 0, sizeof(*tm_info.local)); 451 tm_info.local = 0; 452 } 453 } 454 if (!tm_info.local) 455 tmlocal(); 456 if (!zp) 457 zp = tm_info.local; 458 tm_info.zone = zp; 459 } 460