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