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