14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1985-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * Phong Vo <kpv@research.att.com> * 204887Schin * * 214887Schin ***********************************************************************/ 224887Schin #pragma prototyped 234887Schin /* 244887Schin * Glenn Fowler 254887Schin * AT&T Research 264887Schin * 274887Schin * time conversion support 284887Schin */ 294887Schin 304887Schin #include <tm.h> 314887Schin #include <ctype.h> 324887Schin #include <namval.h> 334887Schin 344887Schin #include "FEATURE/tmlib" 354887Schin 364887Schin #ifndef tzname 374887Schin # if defined(__DYNAMIC__) 384887Schin # define tzname __DYNAMIC__(tzname) 394887Schin # else 404887Schin # if !_dat_tzname 414887Schin # if _dat__tzname 424887Schin # undef _dat_tzname 434887Schin # define _dat_tzname 1 444887Schin # define tzname _tzname 454887Schin # endif 464887Schin # endif 474887Schin # endif 484887Schin # if _dat_tzname 494887Schin extern char* tzname[]; 504887Schin # endif 514887Schin #endif 524887Schin 534887Schin #define TM_type (-1) 544887Schin 554887Schin static const Namval_t options[] = 564887Schin { 574887Schin "adjust", TM_ADJUST, 584887Schin "format", TM_DEFAULT, 594887Schin "leap", TM_LEAP, 604887Schin "subsecond", TM_SUBSECOND, 614887Schin "type", TM_type, 624887Schin "utc", TM_UTC, 634887Schin 0, 0 644887Schin }; 654887Schin 664887Schin /* 674887Schin * 2007-03-19 move tm_info from _tm_info_ to (*_tm_infop_) 684887Schin * to allow future Tm_info_t growth 694887Schin * by 2009 _tm_info_ can be static 704887Schin */ 714887Schin 724887Schin #if _BLD_ast && defined(__EXPORT__) 734887Schin #define extern extern __EXPORT__ 744887Schin #endif 754887Schin 764887Schin extern Tm_info_t _tm_info_; 774887Schin 784887Schin #undef extern 794887Schin 804887Schin Tm_info_t _tm_info_ = { 0 }; 814887Schin 824887Schin __EXTERN__(Tm_info_t, _tm_info_); 834887Schin 844887Schin __EXTERN__(Tm_info_t*, _tm_infop_); 854887Schin 864887Schin Tm_info_t* _tm_infop_ = &_tm_info_; 874887Schin 884887Schin #if _tzset_environ 894887Schin 904887Schin static char TZ[256]; 914887Schin static char* TE[2]; 924887Schin 934887Schin struct tm* 944887Schin _tm_localtime(const time_t* t) 954887Schin { 964887Schin struct tm* r; 974887Schin char* e; 984887Schin 994887Schin if (TZ[0]) 1004887Schin { 1014887Schin if (!environ || !*environ) 1024887Schin environ = TE; 1034887Schin else 1044887Schin e = environ[0]; 1054887Schin environ[0] = TZ; 1064887Schin } 1074887Schin r = localtime(t); 1084887Schin if (TZ[0]) 1094887Schin { 1104887Schin if (environ == TE) 1114887Schin environ = 0; 1124887Schin else 1134887Schin environ[0] = e; 1144887Schin } 1154887Schin return r; 1164887Schin } 1174887Schin 1184887Schin #endif 1194887Schin 1204887Schin /* 1214887Schin * return minutes west of GMT for local time clock 1224887Schin * 1234887Schin * isdst will point to non-zero if DST is in effect 1244887Schin * this routine also kicks in the local initialization 1254887Schin */ 1264887Schin 1274887Schin static int 1284887Schin tzwest(time_t* clock, int* isdst) 1294887Schin { 1304887Schin register struct tm* tp; 1314887Schin register int n; 1324887Schin register int m; 1334887Schin int h; 1344887Schin time_t epoch; 1354887Schin 1364887Schin /* 1374887Schin * convert to GMT assuming local time 1384887Schin */ 1394887Schin 1404887Schin if (!(tp = gmtime(clock))) 1414887Schin { 1424887Schin /* 1434887Schin * some systems return 0 for negative time_t 1444887Schin */ 1454887Schin 1464887Schin epoch = 0; 1474887Schin clock = &epoch; 1484887Schin tp = gmtime(clock); 1494887Schin } 1504887Schin n = tp->tm_yday; 1514887Schin h = tp->tm_hour; 1524887Schin m = tp->tm_min; 1534887Schin 1544887Schin /* 1554887Schin * tmlocaltime() handles DST and GMT offset 1564887Schin */ 1574887Schin 1584887Schin tp = tmlocaltime(clock); 1594887Schin if (n = tp->tm_yday - n) 1604887Schin { 1614887Schin if (n > 1) 1624887Schin n = -1; 1634887Schin else if (n < -1) 1644887Schin n = 1; 1654887Schin } 1664887Schin *isdst = tp->tm_isdst; 1674887Schin return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min; 1684887Schin } 1694887Schin 1704887Schin /* 1714887Schin * stropt() option handler 1724887Schin */ 1734887Schin 1744887Schin static int 1754887Schin tmopt(void* a, const void* p, int n, const char* v) 1764887Schin { 1774887Schin Tm_zone_t* zp; 1784887Schin 1794887Schin NoP(a); 1804887Schin if (p) 1814887Schin switch (((Namval_t*)p)->value) 1824887Schin { 1834887Schin case TM_DEFAULT: 1844887Schin tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT]; 1854887Schin break; 1864887Schin case TM_type: 1874887Schin tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0; 1884887Schin break; 1894887Schin default: 1904887Schin if (n) 1914887Schin tm_info.flags |= ((Namval_t*)p)->value; 1924887Schin else 1934887Schin tm_info.flags &= ~((Namval_t*)p)->value; 1944887Schin break; 1954887Schin } 1964887Schin return 0; 1974887Schin } 1984887Schin 1994887Schin /* 2004887Schin * initialize the local timezone 2014887Schin */ 2024887Schin 2034887Schin static void 2044887Schin tmlocal(void) 2054887Schin { 2064887Schin register Tm_zone_t* zp; 2074887Schin register int n; 2084887Schin register char* s; 2094887Schin register char* e; 2104887Schin int i; 2114887Schin int m; 2124887Schin int isdst; 2134887Schin char* t; 2144887Schin struct tm* tp; 2154887Schin time_t now; 2164887Schin char buf[16]; 2174887Schin 2184887Schin static Tm_zone_t local; 2194887Schin 2204887Schin #if _lib_tzset 2214887Schin #if _tzset_environ 2224887Schin if (s = getenv("TZ")) 2234887Schin { 2244887Schin sfsprintf(TZ, sizeof(TZ), "TZ=%s", s); 2254887Schin if (!environ || !*environ) 2264887Schin environ = TE; 2274887Schin else 2284887Schin e = environ[0]; 2294887Schin environ[0] = TZ; 2304887Schin } 2314887Schin else 2324887Schin { 2334887Schin TZ[0] = 0; 2344887Schin e = 0; 2354887Schin } 2364887Schin #endif 2374887Schin tzset(); 2384887Schin #if _tzset_environ 2394887Schin if (environ == TE) 2404887Schin environ = 0; 2414887Schin else if (e) 2424887Schin environ[0] = e; 2434887Schin #endif 2444887Schin #endif 2454887Schin #if _dat_tzname 2464887Schin local.standard = strdup(tzname[0]); 2474887Schin local.daylight = strdup(tzname[1]); 2484887Schin #endif 2494887Schin tmlocale(); 2504887Schin 2514887Schin /* 2524887Schin * tm_info.local 2534887Schin */ 2544887Schin 2554887Schin tm_info.zone = tm_info.local = &local; 2564887Schin time(&now); 2574887Schin n = tzwest(&now, &isdst); 2584887Schin 2594887Schin /* 2604887Schin * compute local DST offset by roaming 2614887Schin * through the last 12 months until tzwest() changes 2624887Schin */ 2634887Schin 2644887Schin for (i = 0; i < 12; i++) 2654887Schin { 2664887Schin now -= 31 * 24 * 60 * 60; 2674887Schin if ((m = tzwest(&now, &isdst)) != n) 2684887Schin { 2694887Schin if (!isdst) 2704887Schin { 2714887Schin isdst = n; 2724887Schin n = m; 2734887Schin m = isdst; 2744887Schin } 2754887Schin m -= n; 2764887Schin break; 2774887Schin } 2784887Schin } 2794887Schin local.west = n; 2804887Schin local.dst = m; 2814887Schin 2824887Schin /* 2834887Schin * now get the time zone names 2844887Schin */ 2854887Schin 2864887Schin #if _dat_tzname 2874887Schin if (tzname[0]) 2884887Schin { 2894887Schin /* 2904887Schin * POSIX 2914887Schin */ 2924887Schin 2934887Schin if (!local.standard) 2944887Schin local.standard = strdup(tzname[0]); 2954887Schin if (!local.daylight) 2964887Schin local.daylight = strdup(tzname[1]); 2974887Schin } 2984887Schin else 2994887Schin #endif 3004887Schin if ((s = getenv("TZNAME")) && *s && (s = strdup(s))) 3014887Schin { 3024887Schin /* 3034887Schin * BSD 3044887Schin */ 3054887Schin 3064887Schin local.standard = s; 3074887Schin if (s = strchr(s, ',')) 3084887Schin *s++ = 0; 3094887Schin else 3104887Schin s = ""; 3114887Schin local.daylight = s; 3124887Schin } 3134887Schin else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s))) 3144887Schin { 3154887Schin /* 3164887Schin * POSIX style but skipped by tmlocaltime() 3174887Schin */ 3184887Schin 3194887Schin local.standard = s; 3204887Schin if (*++s && *++s && *++s) 3214887Schin { 3224887Schin *s++ = 0; 3234887Schin tmgoff(s, &t, 0); 3244887Schin for (s = t; isalpha(*t); t++); 3254887Schin *t = 0; 3264887Schin } 3274887Schin else 3284887Schin s = ""; 3294887Schin local.daylight = s; 3304887Schin } 3314887Schin else 3324887Schin { 3334887Schin /* 3344887Schin * tm_data.zone table lookup 3354887Schin */ 3364887Schin 3374887Schin t = 0; 3384887Schin for (zp = tm_data.zone; zp->standard; zp++) 3394887Schin { 3404887Schin if (zp->type) 3414887Schin t = zp->type; 3424887Schin if (zp->west == n && zp->dst == m) 3434887Schin { 3444887Schin local.type = t; 3454887Schin local.standard = zp->standard; 3464887Schin if (!(s = zp->daylight)) 3474887Schin { 3484887Schin e = (s = buf) + sizeof(buf); 3494887Schin s = tmpoff(s, e - s, zp->standard, 0, 0); 3504887Schin if (s < e - 1) 3514887Schin { 3524887Schin *s++ = ' '; 3534887Schin tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST); 3544887Schin } 3554887Schin s = strdup(buf); 3564887Schin } 3574887Schin local.daylight = s; 3584887Schin break; 3594887Schin } 3604887Schin } 3614887Schin if (!zp->standard) 3624887Schin { 3634887Schin /* 3644887Schin * not in the table 3654887Schin */ 3664887Schin 3674887Schin e = (s = buf) + sizeof(buf); 3684887Schin s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0); 3694887Schin local.standard = strdup(buf); 3704887Schin if (s < e - 1) 3714887Schin { 3724887Schin *s++ = ' '; 3734887Schin tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST); 3744887Schin local.daylight = strdup(buf); 3754887Schin } 3764887Schin } 3774887Schin } 3784887Schin 3794887Schin /* 3804887Schin * set the options 3814887Schin */ 3824887Schin 3834887Schin stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL); 3844887Schin 3854887Schin /* 3864887Schin * the time zone type is probably related to the locale 3874887Schin */ 3884887Schin 3894887Schin if (!local.type) 3904887Schin { 3914887Schin s = local.standard; 3924887Schin t = 0; 3934887Schin for (zp = tm_data.zone; zp->standard; zp++) 3944887Schin { 3954887Schin if (zp->type) 3964887Schin t = zp->type; 3974887Schin if (tmword(s, NiL, zp->standard, NiL, 0)) 3984887Schin { 3994887Schin local.type = t; 4004887Schin break; 4014887Schin } 4024887Schin } 4034887Schin } 4044887Schin 4054887Schin /* 4064887Schin * tm_info.flags 4074887Schin */ 4084887Schin 4094887Schin if (!(tm_info.flags & TM_ADJUST)) 4104887Schin { 4114887Schin now = (time_t)78811200; /* Jun 30 1972 23:59:60 */ 4124887Schin tp = tmlocaltime(&now); 4134887Schin if (tp->tm_sec != 60) 4144887Schin tm_info.flags |= TM_ADJUST; 4154887Schin } 4164887Schin if (!(tm_info.flags & TM_UTC)) 4174887Schin { 4184887Schin s = local.standard; 4194887Schin zp = tm_data.zone; 4204887Schin if (local.daylight) 4214887Schin zp++; 4224887Schin for (; !zp->type && zp->standard; zp++) 4234887Schin if (tmword(s, NiL, zp->standard, NiL, 0)) 4244887Schin { 4254887Schin tm_info.flags |= TM_UTC; 4264887Schin break; 4274887Schin } 4284887Schin } 4294887Schin } 4304887Schin 4314887Schin /* 4324887Schin * initialize tm data 4334887Schin */ 4344887Schin 4354887Schin void 4364887Schin tminit(register Tm_zone_t* zp) 4374887Schin { 4384887Schin static uint32_t serial = ~(uint32_t)0; 4394887Schin 4404887Schin if (serial != ast.env_serial) 4414887Schin { 4424887Schin serial = ast.env_serial; 4434887Schin if (tm_info.local) 4444887Schin { 4454887Schin memset(tm_info.local, 0, sizeof(*tm_info.local)); 4464887Schin tm_info.local = 0; 4474887Schin } 4484887Schin } 4494887Schin if (!tm_info.local) 4504887Schin tmlocal(); 4514887Schin if (!zp) 4524887Schin zp = tm_info.local; 4534887Schin tm_info.zone = zp; 4544887Schin } 455