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