xref: /titanic_50/usr/src/lib/libast/common/tm/tminit.c (revision c869993e79c1eafbec61a56bf6cea848fe754c71)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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 
99 	if (TZ[0])
100 	{
101 		if (!environ || !*environ)
102 			environ = TE;
103 		else
104 			e = environ[0];
105 		environ[0] = TZ;
106 	}
107 	r = localtime(t);
108 	if (TZ[0])
109 	{
110 		if (environ == TE)
111 			environ = 0;
112 		else
113 			environ[0] = e;
114 	}
115 	return r;
116 }
117 
118 #endif
119 
120 /*
121  * return minutes west of GMT for local time clock
122  *
123  * isdst will point to non-zero if DST is in effect
124  * this routine also kicks in the local initialization
125  */
126 
127 static int
128 tzwest(time_t* clock, int* isdst)
129 {
130 	register struct tm*	tp;
131 	register int		n;
132 	register int		m;
133 	int			h;
134 	time_t			epoch;
135 
136 	/*
137 	 * convert to GMT assuming local time
138 	 */
139 
140 	if (!(tp = gmtime(clock)))
141 	{
142 		/*
143 		 * some systems return 0 for negative time_t
144 		 */
145 
146 		epoch = 0;
147 		clock = &epoch;
148 		tp = gmtime(clock);
149 	}
150 	n = tp->tm_yday;
151 	h = tp->tm_hour;
152 	m = tp->tm_min;
153 
154 	/*
155 	 * tmlocaltime() handles DST and GMT offset
156 	 */
157 
158 	tp = tmlocaltime(clock);
159 	if (n = tp->tm_yday - n)
160 	{
161 		if (n > 1)
162 			n = -1;
163 		else if (n < -1)
164 			n = 1;
165 	}
166 	*isdst = tp->tm_isdst;
167 	return (h - tp->tm_hour - n * 24) * 60 + m - tp->tm_min;
168 }
169 
170 /*
171  * stropt() option handler
172  */
173 
174 static int
175 tmopt(void* a, const void* p, int n, const char* v)
176 {
177 	Tm_zone_t*	zp;
178 
179 	NoP(a);
180 	if (p)
181 		switch (((Namval_t*)p)->value)
182 		{
183 		case TM_DEFAULT:
184 			tm_info.deformat = (n && (n = strlen(v)) > 0 && (n < 2 || v[n-2] != '%' || v[n-1] != '?')) ? strdup(v) : tm_info.format[TM_DEFAULT];
185 			break;
186 		case TM_type:
187 			tm_info.local->type = (n && *v) ? ((zp = tmtype(v, NiL)) ? zp->type : strdup(v)) : 0;
188 			break;
189 		default:
190 			if (n)
191 				tm_info.flags |= ((Namval_t*)p)->value;
192 			else
193 				tm_info.flags &= ~((Namval_t*)p)->value;
194 			break;
195 		}
196 	return 0;
197 }
198 
199 /*
200  * initialize the local timezone
201  */
202 
203 static void
204 tmlocal(void)
205 {
206 	register Tm_zone_t*	zp;
207 	register int		n;
208 	register char*		s;
209 	register char*		e;
210 	int			i;
211 	int			m;
212 	int			isdst;
213 	char*			t;
214 	struct tm*		tp;
215 	time_t			now;
216 	char			buf[16];
217 
218 	static Tm_zone_t	local;
219 
220 #if _lib_tzset
221 #if _tzset_environ
222 	if (s = getenv("TZ"))
223 	{
224 		sfsprintf(TZ, sizeof(TZ), "TZ=%s", s);
225 		if (!environ || !*environ)
226 			environ = TE;
227 		else
228 			e = environ[0];
229 		environ[0] = TZ;
230 	}
231 	else
232 	{
233 		TZ[0] = 0;
234 		e = 0;
235 	}
236 #endif
237 	tzset();
238 #if _tzset_environ
239 	if (environ == TE)
240 		environ = 0;
241 	else if (e)
242 		environ[0] = e;
243 #endif
244 #endif
245 #if _dat_tzname
246 	local.standard = strdup(tzname[0]);
247 	local.daylight = strdup(tzname[1]);
248 #endif
249 	tmlocale();
250 
251 	/*
252 	 * tm_info.local
253 	 */
254 
255 	tm_info.zone = tm_info.local = &local;
256 	time(&now);
257 	n = tzwest(&now, &isdst);
258 
259 	/*
260 	 * compute local DST offset by roaming
261 	 * through the last 12 months until tzwest() changes
262 	 */
263 
264 	for (i = 0; i < 12; i++)
265 	{
266 		now -= 31 * 24 * 60 * 60;
267 		if ((m = tzwest(&now, &isdst)) != n)
268 		{
269 			if (!isdst)
270 			{
271 				isdst = n;
272 				n = m;
273 				m = isdst;
274 			}
275 			m -= n;
276 			break;
277 		}
278 	}
279 	local.west = n;
280 	local.dst = m;
281 
282 	/*
283 	 * now get the time zone names
284 	 */
285 
286 #if _dat_tzname
287 	if (tzname[0])
288 	{
289 		/*
290 		 * POSIX
291 		 */
292 
293 		if (!local.standard)
294 			local.standard = strdup(tzname[0]);
295 		if (!local.daylight)
296 			local.daylight = strdup(tzname[1]);
297 	}
298 	else
299 #endif
300 	if ((s = getenv("TZNAME")) && *s && (s = strdup(s)))
301 	{
302 		/*
303 		 * BSD
304 		 */
305 
306 		local.standard = s;
307 		if (s = strchr(s, ','))
308 			*s++ = 0;
309 		else
310 			s = "";
311 		local.daylight = s;
312 	}
313 	else if ((s = getenv("TZ")) && *s && *s != ':' && (s = strdup(s)))
314 	{
315 		/*
316 		 * POSIX style but skipped by tmlocaltime()
317 		 */
318 
319 		local.standard = s;
320 		if (*++s && *++s && *++s)
321 		{
322 			*s++ = 0;
323 			tmgoff(s, &t, 0);
324 			for (s = t; isalpha(*t); t++);
325 			*t = 0;
326 		}
327 		else
328 			s = "";
329 		local.daylight = s;
330 	}
331 	else
332 	{
333 		/*
334 		 * tm_data.zone table lookup
335 		 */
336 
337 		t = 0;
338 		for (zp = tm_data.zone; zp->standard; zp++)
339 		{
340 			if (zp->type)
341 				t = zp->type;
342 			if (zp->west == n && zp->dst == m)
343 			{
344 				local.type = t;
345 				local.standard = zp->standard;
346 				if (!(s = zp->daylight))
347 				{
348 					e = (s = buf) + sizeof(buf);
349 					s = tmpoff(s, e - s, zp->standard, 0, 0);
350 					if (s < e - 1)
351 					{
352 						*s++ = ' ';
353 						tmpoff(s, e - s, tm_info.format[TM_DT], m, TM_DST);
354 					}
355 					s = strdup(buf);
356 				}
357 				local.daylight = s;
358 				break;
359 			}
360 		}
361 		if (!zp->standard)
362 		{
363 			/*
364 			 * not in the table
365 			 */
366 
367 			e = (s = buf) + sizeof(buf);
368 			s = tmpoff(s, e - s, tm_info.format[TM_UT], n, 0);
369 			local.standard = strdup(buf);
370 			if (s < e - 1)
371 			{
372 				*s++ = ' ';
373 				tmpoff(s, e - s, tm_info.format[TM_UT], m, TM_DST);
374 				local.daylight = strdup(buf);
375 			}
376 		}
377 	}
378 
379 	/*
380 	 * set the options
381 	 */
382 
383 	stropt(getenv("TM_OPTIONS"), options, sizeof(*options), tmopt, NiL);
384 
385 	/*
386 	 * the time zone type is probably related to the locale
387 	 */
388 
389 	if (!local.type)
390 	{
391 		s = local.standard;
392 		t = 0;
393 		for (zp = tm_data.zone; zp->standard; zp++)
394 		{
395 			if (zp->type)
396 				t = zp->type;
397 			if (tmword(s, NiL, zp->standard, NiL, 0))
398 			{
399 				local.type = t;
400 				break;
401 			}
402 		}
403 	}
404 
405 	/*
406 	 * tm_info.flags
407 	 */
408 
409 	if (!(tm_info.flags & TM_ADJUST))
410 	{
411 		now = (time_t)78811200;		/* Jun 30 1972 23:59:60 */
412 		tp = tmlocaltime(&now);
413 		if (tp->tm_sec != 60)
414 			tm_info.flags |= TM_ADJUST;
415 	}
416 	if (!(tm_info.flags & TM_UTC))
417 	{
418 		s = local.standard;
419 		zp = tm_data.zone;
420 		if (local.daylight)
421 			zp++;
422 		for (; !zp->type && zp->standard; zp++)
423 			if (tmword(s, NiL, zp->standard, NiL, 0))
424 			{
425 				tm_info.flags |= TM_UTC;
426 				break;
427 			}
428 	}
429 }
430 
431 /*
432  * initialize tm data
433  */
434 
435 void
436 tminit(register Tm_zone_t* zp)
437 {
438 	static uint32_t		serial = ~(uint32_t)0;
439 
440 	if (serial != ast.env_serial)
441 	{
442 		serial = ast.env_serial;
443 		if (tm_info.local)
444 		{
445 			memset(tm_info.local, 0, sizeof(*tm_info.local));
446 			tm_info.local = 0;
447 		}
448 	}
449 	if (!tm_info.local)
450 		tmlocal();
451 	if (!zp)
452 		zp = tm_info.local;
453 	tm_info.zone = zp;
454 }
455