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