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