xref: /titanic_44/usr/src/lib/libast/common/tm/tminit.c (revision 8339b41da2395f0525c46ceedfeb01961893ec44)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 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 
55 static 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 
76 extern Tm_info_t	_tm_info_;
77 
78 #undef	extern
79 
80 Tm_info_t		_tm_info_ = { 0 };
81 
82 __EXTERN__(Tm_info_t, _tm_info_);
83 
84 __EXTERN__(Tm_info_t*, _tm_infop_);
85 
86 Tm_info_t*		_tm_infop_ = &_tm_info_;
87 
88 #if _tzset_environ
89 
90 static char	TZ[256];
91 static char*	TE[2];
92 
93 struct 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 
128 static int
129 tzwest(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 
175 static int
176 tmopt(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 
204 static void
205 tmlocal(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 
440 void
441 tminit(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