1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 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 99 if (TZ[0]) 100 { 101 if (!environ || !*environ) 102 environ = TE; 103 else 104 e = environ[0]; 105 environ[0] = TZ; 106 } 107 r = localtime(t); 108 if (TZ[0]) 109 { 110 if (environ == TE) 111 environ = 0; 112 else 113 environ[0] = e; 114 } 115 return r; 116 } 117 118 #endif 119 120 /* 121 * return minutes west of GMT for local time clock 122 * 123 * isdst will point to non-zero if DST is in effect 124 * this routine also kicks in the local initialization 125 */ 126 127 static int 128 tzwest(time_t* clock, int* isdst) 129 { 130 register struct tm* tp; 131 register int n; 132 register int m; 133 int h; 134 time_t epoch; 135 136 /* 137 * convert to GMT assuming local time 138 */ 139 140 if (!(tp = gmtime(clock))) 141 { 142 /* 143 * some systems return 0 for negative time_t 144 */ 145 146 epoch = 0; 147 clock = &epoch; 148 tp = gmtime(clock); 149 } 150 n = tp->tm_yday; 151 h = tp->tm_hour; 152 m = tp->tm_min; 153 154 /* 155 * tmlocaltime() handles DST and GMT offset 156 */ 157 158 tp = tmlocaltime(clock); 159 if (n = tp->tm_yday - n) 160 { 161 if (n > 1) 162 n = -1; 163 else if (n < -1) 164 n = 1; 165 } 166 *isdst = tp->tm_isdst; 167 return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min; 168 } 169 170 /* 171 * stropt() option handler 172 */ 173 174 static int 175 tmopt(void* a, const void* p, int n, const char* v) 176 { 177 Tm_zone_t* zp; 178 179 NoP(a); 180 if (p) 181 switch (((Namval_t*)p)->value) 182 { 183 case TM_DEFAULT: 184 tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT]; 185 break; 186 case TM_type: 187 tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0; 188 break; 189 default: 190 if (n) 191 tm_info.flags |= ((Namval_t*)p)->value; 192 else 193 tm_info.flags &= ~((Namval_t*)p)->value; 194 break; 195 } 196 return 0; 197 } 198 199 /* 200 * initialize the local timezone 201 */ 202 203 static void 204 tmlocal(void) 205 { 206 register Tm_zone_t* zp; 207 register int n; 208 register char* s; 209 register char* e; 210 int i; 211 int m; 212 int isdst; 213 char* t; 214 struct tm* tp; 215 time_t now; 216 char buf[16]; 217 218 static Tm_zone_t local; 219 220 #if _lib_tzset 221 #if _tzset_environ 222 if (s = getenv("TZ")) 223 { 224 sfsprintf(TZ, sizeof(TZ), "TZ=%s", s); 225 if (!environ || !*environ) 226 environ = TE; 227 else 228 e = environ[0]; 229 environ[0] = TZ; 230 } 231 else 232 { 233 TZ[0] = 0; 234 e = 0; 235 } 236 #endif 237 tzset(); 238 #if _tzset_environ 239 if (environ == TE) 240 environ = 0; 241 else if (e) 242 environ[0] = e; 243 #endif 244 #endif 245 #if _dat_tzname 246 local.standard = strdup(tzname[0]); 247 local.daylight = strdup(tzname[1]); 248 #endif 249 tmlocale(); 250 251 /* 252 * tm_info.local 253 */ 254 255 tm_info.zone = tm_info.local = &local; 256 time(&now); 257 n = tzwest(&now, &isdst); 258 259 /* 260 * compute local DST offset by roaming 261 * through the last 12 months until tzwest() changes 262 */ 263 264 for (i = 0; i < 12; i++) 265 { 266 now -= 31 * 24 * 60 * 60; 267 if ((m = tzwest(&now, &isdst)) != n) 268 { 269 if (!isdst) 270 { 271 isdst = n; 272 n = m; 273 m = isdst; 274 } 275 m -= n; 276 break; 277 } 278 } 279 local.west = n; 280 local.dst = m; 281 282 /* 283 * now get the time zone names 284 */ 285 286 #if _dat_tzname 287 if (tzname[0]) 288 { 289 /* 290 * POSIX 291 */ 292 293 if (!local.standard) 294 local.standard = strdup(tzname[0]); 295 if (!local.daylight) 296 local.daylight = strdup(tzname[1]); 297 } 298 else 299 #endif 300 if ((s = getenv("TZNAME")) && *s && (s = strdup(s))) 301 { 302 /* 303 * BSD 304 */ 305 306 local.standard = s; 307 if (s = strchr(s, ',')) 308 *s++ = 0; 309 else 310 s = ""; 311 local.daylight = s; 312 } 313 else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s))) 314 { 315 /* 316 * POSIX style but skipped by tmlocaltime() 317 */ 318 319 local.standard = s; 320 if (*++s && *++s && *++s) 321 { 322 *s++ = 0; 323 tmgoff(s, &t, 0); 324 for (s = t; isalpha(*t); t++); 325 *t = 0; 326 } 327 else 328 s = ""; 329 local.daylight = s; 330 } 331 else 332 { 333 /* 334 * tm_data.zone table lookup 335 */ 336 337 t = 0; 338 for (zp = tm_data.zone; zp->standard; zp++) 339 { 340 if (zp->type) 341 t = zp->type; 342 if (zp->west == n && zp->dst == m) 343 { 344 local.type = t; 345 local.standard = zp->standard; 346 if (!(s = zp->daylight)) 347 { 348 e = (s = buf) + sizeof(buf); 349 s = tmpoff(s, e - s, zp->standard, 0, 0); 350 if (s < e - 1) 351 { 352 *s++ = ' '; 353 tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST); 354 } 355 s = strdup(buf); 356 } 357 local.daylight = s; 358 break; 359 } 360 } 361 if (!zp->standard) 362 { 363 /* 364 * not in the table 365 */ 366 367 e = (s = buf) + sizeof(buf); 368 s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0); 369 local.standard = strdup(buf); 370 if (s < e - 1) 371 { 372 *s++ = ' '; 373 tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST); 374 local.daylight = strdup(buf); 375 } 376 } 377 } 378 379 /* 380 * set the options 381 */ 382 383 stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL); 384 385 /* 386 * the time zone type is probably related to the locale 387 */ 388 389 if (!local.type) 390 { 391 s = local.standard; 392 t = 0; 393 for (zp = tm_data.zone; zp->standard; zp++) 394 { 395 if (zp->type) 396 t = zp->type; 397 if (tmword(s, NiL, zp->standard, NiL, 0)) 398 { 399 local.type = t; 400 break; 401 } 402 } 403 } 404 405 /* 406 * tm_info.flags 407 */ 408 409 if (!(tm_info.flags & TM_ADJUST)) 410 { 411 now = (time_t)78811200; /* Jun 30 1972 23:59:60 */ 412 tp = tmlocaltime(&now); 413 if (tp->tm_sec != 60) 414 tm_info.flags |= TM_ADJUST; 415 } 416 if (!(tm_info.flags & TM_UTC)) 417 { 418 s = local.standard; 419 zp = tm_data.zone; 420 if (local.daylight) 421 zp++; 422 for (; !zp->type && zp->standard; zp++) 423 if (tmword(s, NiL, zp->standard, NiL, 0)) 424 { 425 tm_info.flags |= TM_UTC; 426 break; 427 } 428 } 429 } 430 431 /* 432 * initialize tm data 433 */ 434 435 void 436 tminit(register Tm_zone_t* zp) 437 { 438 static uint32_t serial = ~(uint32_t)0; 439 440 if (serial != ast.env_serial) 441 { 442 serial = ast.env_serial; 443 if (tm_info.local) 444 { 445 memset(tm_info.local, 0, sizeof(*tm_info.local)); 446 tm_info.local = 0; 447 } 448 } 449 if (!tm_info.local) 450 tmlocal(); 451 if (!zp) 452 zp = tm_info.local; 453 tm_info.zone = zp; 454 } 455