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*
_tm_localtime(const time_t * t)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
tzwest(time_t * clock,int * isdst)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
tmopt(void * a,const void * p,int n,const char * v)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
tmlocal(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
tminit(register Tm_zone_t * zp)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