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