14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.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*
_tm_localtime(const time_t * t)944887Schin _tm_localtime(const time_t* t)
954887Schin {
964887Schin struct tm* r;
974887Schin char* e;
98*12068SRoger.Faulkner@Oracle.COM char** v = environ;
994887Schin
1004887Schin if (TZ[0])
1014887Schin {
1024887Schin if (!environ || !*environ)
1034887Schin environ = TE;
1044887Schin else
1054887Schin e = environ[0];
1064887Schin environ[0] = TZ;
1074887Schin }
1084887Schin r = localtime(t);
1094887Schin if (TZ[0])
1104887Schin {
111*12068SRoger.Faulkner@Oracle.COM if (environ != v)
112*12068SRoger.Faulkner@Oracle.COM environ = v;
1134887Schin else
1144887Schin environ[0] = e;
1154887Schin }
1164887Schin return r;
1174887Schin }
1184887Schin
1194887Schin #endif
1204887Schin
1214887Schin /*
1224887Schin * return minutes west of GMT for local time clock
1234887Schin *
1244887Schin * isdst will point to non-zero if DST is in effect
1254887Schin * this routine also kicks in the local initialization
1264887Schin */
1274887Schin
1284887Schin static int
tzwest(time_t * clock,int * isdst)1294887Schin tzwest(time_t* clock, int* isdst)
1304887Schin {
1314887Schin register struct tm* tp;
1324887Schin register int n;
1334887Schin register int m;
1344887Schin int h;
1354887Schin time_t epoch;
1364887Schin
1374887Schin /*
1384887Schin * convert to GMT assuming local time
1394887Schin */
1404887Schin
1414887Schin if (!(tp = gmtime(clock)))
1424887Schin {
1434887Schin /*
1444887Schin * some systems return 0 for negative time_t
1454887Schin */
1464887Schin
1474887Schin epoch = 0;
1484887Schin clock = &epoch;
1494887Schin tp = gmtime(clock);
1504887Schin }
1514887Schin n = tp->tm_yday;
1524887Schin h = tp->tm_hour;
1534887Schin m = tp->tm_min;
1544887Schin
1554887Schin /*
1564887Schin * tmlocaltime() handles DST and GMT offset
1574887Schin */
1584887Schin
1594887Schin tp = tmlocaltime(clock);
1604887Schin if (n = tp->tm_yday - n)
1614887Schin {
1624887Schin if (n > 1)
1634887Schin n = -1;
1644887Schin else if (n < -1)
1654887Schin n = 1;
1664887Schin }
1674887Schin *isdst = tp->tm_isdst;
1684887Schin return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min;
1694887Schin }
1704887Schin
1714887Schin /*
1724887Schin * stropt() option handler
1734887Schin */
1744887Schin
1754887Schin static int
tmopt(void * a,const void * p,int n,const char * v)1764887Schin tmopt(void* a, const void* p, int n, const char* v)
1774887Schin {
1784887Schin Tm_zone_t* zp;
1794887Schin
1804887Schin NoP(a);
1814887Schin if (p)
1824887Schin switch (((Namval_t*)p)->value)
1834887Schin {
1844887Schin case TM_DEFAULT:
1854887Schin tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT];
1864887Schin break;
1874887Schin case TM_type:
1884887Schin tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0;
1894887Schin break;
1904887Schin default:
1914887Schin if (n)
1924887Schin tm_info.flags |= ((Namval_t*)p)->value;
1934887Schin else
1944887Schin tm_info.flags &= ~((Namval_t*)p)->value;
1954887Schin break;
1964887Schin }
1974887Schin return 0;
1984887Schin }
1994887Schin
2004887Schin /*
2014887Schin * initialize the local timezone
2024887Schin */
2034887Schin
2044887Schin static void
tmlocal(void)2054887Schin tmlocal(void)
2064887Schin {
2074887Schin register Tm_zone_t* zp;
2084887Schin register int n;
2094887Schin register char* s;
2104887Schin register char* e;
2114887Schin int i;
2124887Schin int m;
2134887Schin int isdst;
2144887Schin char* t;
2154887Schin struct tm* tp;
2164887Schin time_t now;
2174887Schin char buf[16];
2184887Schin
2194887Schin static Tm_zone_t local;
2204887Schin
2214887Schin #if _tzset_environ
2224887Schin {
223*12068SRoger.Faulkner@Oracle.COM char** v = environ;
224*12068SRoger.Faulkner@Oracle.COM
225*12068SRoger.Faulkner@Oracle.COM if (s = getenv("TZ"))
226*12068SRoger.Faulkner@Oracle.COM {
227*12068SRoger.Faulkner@Oracle.COM sfsprintf(TZ, sizeof(TZ), "TZ=%s", s);
228*12068SRoger.Faulkner@Oracle.COM if (!environ || !*environ)
229*12068SRoger.Faulkner@Oracle.COM environ = TE;
230*12068SRoger.Faulkner@Oracle.COM else
231*12068SRoger.Faulkner@Oracle.COM e = environ[0];
232*12068SRoger.Faulkner@Oracle.COM environ[0] = TZ;
233*12068SRoger.Faulkner@Oracle.COM }
2344887Schin else
235*12068SRoger.Faulkner@Oracle.COM {
236*12068SRoger.Faulkner@Oracle.COM TZ[0] = 0;
237*12068SRoger.Faulkner@Oracle.COM e = 0;
238*12068SRoger.Faulkner@Oracle.COM }
239*12068SRoger.Faulkner@Oracle.COM #endif
240*12068SRoger.Faulkner@Oracle.COM #if _lib_tzset
241*12068SRoger.Faulkner@Oracle.COM tzset();
242*12068SRoger.Faulkner@Oracle.COM #endif
243*12068SRoger.Faulkner@Oracle.COM #if _tzset_environ
244*12068SRoger.Faulkner@Oracle.COM if (environ != v)
245*12068SRoger.Faulkner@Oracle.COM environ = v;
246*12068SRoger.Faulkner@Oracle.COM else if (e)
247*12068SRoger.Faulkner@Oracle.COM environ[0] = e;
2484887Schin }
2494887Schin #endif
2504887Schin #if _dat_tzname
2514887Schin local.standard = strdup(tzname[0]);
2524887Schin local.daylight = strdup(tzname[1]);
2534887Schin #endif
2544887Schin tmlocale();
2554887Schin
2564887Schin /*
2574887Schin * tm_info.local
2584887Schin */
2594887Schin
2604887Schin tm_info.zone = tm_info.local = &local;
2614887Schin time(&now);
2624887Schin n = tzwest(&now, &isdst);
2634887Schin
2644887Schin /*
2654887Schin * compute local DST offset by roaming
2664887Schin * through the last 12 months until tzwest() changes
2674887Schin */
2684887Schin
2694887Schin for (i = 0; i < 12; i++)
2704887Schin {
2714887Schin now -= 31 * 24 * 60 * 60;
2724887Schin if ((m = tzwest(&now, &isdst)) != n)
2734887Schin {
2744887Schin if (!isdst)
2754887Schin {
2764887Schin isdst = n;
2774887Schin n = m;
2784887Schin m = isdst;
2794887Schin }
2804887Schin m -= n;
2814887Schin break;
2824887Schin }
2834887Schin }
2844887Schin local.west = n;
2854887Schin local.dst = m;
2864887Schin
2874887Schin /*
2884887Schin * now get the time zone names
2894887Schin */
2904887Schin
2914887Schin #if _dat_tzname
2924887Schin if (tzname[0])
2934887Schin {
2944887Schin /*
2954887Schin * POSIX
2964887Schin */
2974887Schin
2984887Schin if (!local.standard)
2994887Schin local.standard = strdup(tzname[0]);
3004887Schin if (!local.daylight)
3014887Schin local.daylight = strdup(tzname[1]);
3024887Schin }
3034887Schin else
3044887Schin #endif
3054887Schin if ((s = getenv("TZNAME")) && *s && (s = strdup(s)))
3064887Schin {
3074887Schin /*
3084887Schin * BSD
3094887Schin */
3104887Schin
3114887Schin local.standard = s;
3124887Schin if (s = strchr(s, ','))
3134887Schin *s++ = 0;
3144887Schin else
3154887Schin s = "";
3164887Schin local.daylight = s;
3174887Schin }
3184887Schin else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s)))
3194887Schin {
3204887Schin /*
3214887Schin * POSIX style but skipped by tmlocaltime()
3224887Schin */
3234887Schin
3244887Schin local.standard = s;
3254887Schin if (*++s && *++s && *++s)
3264887Schin {
3274887Schin *s++ = 0;
3284887Schin tmgoff(s, &t, 0);
3294887Schin for (s = t; isalpha(*t); t++);
3304887Schin *t = 0;
3314887Schin }
3324887Schin else
3334887Schin s = "";
3344887Schin local.daylight = s;
3354887Schin }
3364887Schin else
3374887Schin {
3384887Schin /*
3394887Schin * tm_data.zone table lookup
3404887Schin */
3414887Schin
3424887Schin t = 0;
3434887Schin for (zp = tm_data.zone; zp->standard; zp++)
3444887Schin {
3454887Schin if (zp->type)
3464887Schin t = zp->type;
3474887Schin if (zp->west == n && zp->dst == m)
3484887Schin {
3494887Schin local.type = t;
3504887Schin local.standard = zp->standard;
3514887Schin if (!(s = zp->daylight))
3524887Schin {
3534887Schin e = (s = buf) + sizeof(buf);
3544887Schin s = tmpoff(s, e - s, zp->standard, 0, 0);
3554887Schin if (s < e - 1)
3564887Schin {
3574887Schin *s++ = ' ';
3584887Schin tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST);
3594887Schin }
3604887Schin s = strdup(buf);
3614887Schin }
3624887Schin local.daylight = s;
3634887Schin break;
3644887Schin }
3654887Schin }
3664887Schin if (!zp->standard)
3674887Schin {
3684887Schin /*
3694887Schin * not in the table
3704887Schin */
3714887Schin
3724887Schin e = (s = buf) + sizeof(buf);
3734887Schin s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0);
3744887Schin local.standard = strdup(buf);
3754887Schin if (s < e - 1)
3764887Schin {
3774887Schin *s++ = ' ';
3784887Schin tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST);
3794887Schin local.daylight = strdup(buf);
3804887Schin }
3814887Schin }
3824887Schin }
3834887Schin
3844887Schin /*
3854887Schin * set the options
3864887Schin */
3874887Schin
3884887Schin stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL);
3894887Schin
3904887Schin /*
3914887Schin * the time zone type is probably related to the locale
3924887Schin */
3934887Schin
3944887Schin if (!local.type)
3954887Schin {
3964887Schin s = local.standard;
3974887Schin t = 0;
3984887Schin for (zp = tm_data.zone; zp->standard; zp++)
3994887Schin {
4004887Schin if (zp->type)
4014887Schin t = zp->type;
4024887Schin if (tmword(s, NiL, zp->standard, NiL, 0))
4034887Schin {
4044887Schin local.type = t;
4054887Schin break;
4064887Schin }
4074887Schin }
4084887Schin }
4094887Schin
4104887Schin /*
4114887Schin * tm_info.flags
4124887Schin */
4134887Schin
4144887Schin if (!(tm_info.flags & TM_ADJUST))
4154887Schin {
4164887Schin now = (time_t)78811200; /* Jun 30 1972 23:59:60 */
4174887Schin tp = tmlocaltime(&now);
4184887Schin if (tp->tm_sec != 60)
4194887Schin tm_info.flags |= TM_ADJUST;
4204887Schin }
4214887Schin if (!(tm_info.flags & TM_UTC))
4224887Schin {
4234887Schin s = local.standard;
4244887Schin zp = tm_data.zone;
4254887Schin if (local.daylight)
4264887Schin zp++;
4274887Schin for (; !zp->type && zp->standard; zp++)
4284887Schin if (tmword(s, NiL, zp->standard, NiL, 0))
4294887Schin {
4304887Schin tm_info.flags |= TM_UTC;
4314887Schin break;
4324887Schin }
4334887Schin }
4344887Schin }
4354887Schin
4364887Schin /*
4374887Schin * initialize tm data
4384887Schin */
4394887Schin
4404887Schin void
tminit(register Tm_zone_t * zp)4414887Schin tminit(register Tm_zone_t* zp)
4424887Schin {
4434887Schin static uint32_t serial = ~(uint32_t)0;
4444887Schin
4454887Schin if (serial != ast.env_serial)
4464887Schin {
4474887Schin serial = ast.env_serial;
4484887Schin if (tm_info.local)
4494887Schin {
4504887Schin memset(tm_info.local, 0, sizeof(*tm_info.local));
4514887Schin tm_info.local = 0;
4524887Schin }
4534887Schin }
4544887Schin if (!tm_info.local)
4554887Schin tmlocal();
4564887Schin if (!zp)
4574887Schin zp = tm_info.local;
4584887Schin tm_info.zone = zp;
4594887Schin }
460