]> git.proxmox.com Git - mirror_edk2.git/blame - StdLib/LibC/Time/ZoneProc.c
Add Socket Libraries.
[mirror_edk2.git] / StdLib / LibC / Time / ZoneProc.c
CommitLineData
2aa62f2b 1/** @file\r
2 Time Zone processing.\r
3\r
4 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials are licensed and made available under\r
6 the terms and conditions of the BSD License that accompanies this distribution.\r
7 The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php.\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13 Portions derived from the NIH time zone package file, localtime.c,\r
14 which contains the following notice:\r
15\r
16 This file is in the public domain, so clarified as of\r
17 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).\r
18\r
19 NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp\r
20**/\r
21#include <LibConfig.h>\r
22#include <sys/EfiSysCall.h>\r
23\r
24#include <ctype.h>\r
25#include <fcntl.h>\r
26#include <stdio.h>\r
27#include <stdlib.h>\r
28#include <string.h>\r
29#include <time.h>\r
30#include "tzfile.h"\r
31#include "TimeVals.h"\r
32\r
33#ifndef WILDABBR\r
34/*\r
35** Someone might make incorrect use of a time zone abbreviation:\r
36** 1. They might reference tzname[0] before calling tzset (explicitly\r
37** or implicitly).\r
38** 2. They might reference tzname[1] before calling tzset (explicitly\r
39** or implicitly).\r
40** 3. They might reference tzname[1] after setting to a time zone\r
41** in which Daylight Saving Time is never observed.\r
42** 4. They might reference tzname[0] after setting to a time zone\r
43** in which Standard Time is never observed.\r
44** 5. They might reference tm.TM_ZONE after calling offtime.\r
45** What's best to do in the above cases is open to debate;\r
46** for now, we just set things up so that in any of the five cases\r
47** WILDABBR is used. Another possibility: initialize tzname[0] to the\r
48** string "tzname[0] used before set", and similarly for the other cases.\r
49** And another: initialize tzname[0] to "ERA", with an explanation in the\r
50** manual page of what this "time zone abbreviation" means (doing this so\r
51** that tzname[0] has the "normal" length of three characters).\r
52*/\r
53#define WILDABBR " "\r
54#endif /* !defined WILDABBR */\r
55\r
56const char wildabbr[9] = "WILDABBR";\r
57const char gmt[4] = "GMT";\r
58\r
59struct state * lclptr = NULL;\r
60struct state * gmtptr = NULL;\r
61\r
62#ifndef TZ_STRLEN_MAX\r
63#define TZ_STRLEN_MAX 255\r
64#endif /* !defined TZ_STRLEN_MAX */\r
65\r
66static char lcl_TZname[TZ_STRLEN_MAX + 1];\r
67static int lcl_is_set = 0;\r
68//static int gmt_is_set = 0;\r
69\r
70char * tzname[2] = {\r
71 (char *)__UNCONST(wildabbr),\r
72 (char *)__UNCONST(wildabbr)\r
73};\r
74\r
75long int timezone = 0;\r
76int daylight = 0;\r
77\r
78#ifndef NO_ZONEINFO_FILES\r
79/** Get first 4 characters of codep as a 32-bit integer.\r
80\r
81 The first character of codep becomes the MSB of the resultant integer.\r
82**/\r
83static INT32\r
84detzcode(const char * const codep)\r
85{\r
86 register INT32 result;\r
87\r
88 /*\r
89 ** The first character must be sign extended on systems with >32bit\r
90 ** longs. This was solved differently in the master tzcode sources\r
91 ** (the fix first appeared in tzcode95c.tar.gz). But I believe\r
92 ** that this implementation is superior.\r
93 */\r
94#define SIGN_EXTEND_CHAR(x) ((signed char) x)\r
95\r
96 result = (SIGN_EXTEND_CHAR(codep[0]) << 24) \\r
97 | (codep[1] & 0xff) << 16 \\r
98 | (codep[2] & 0xff) << 8\r
99 | (codep[3] & 0xff);\r
100 return result;\r
101}\r
102#endif /* NO_ZONEINFO_FILES */\r
103\r
104static void\r
105settzname (void)\r
106{\r
107 register struct state * const sp = lclptr;\r
108 register int i;\r
109\r
110 tzname[0] = (char *)__UNCONST(wildabbr);\r
111 tzname[1] = (char *)__UNCONST(wildabbr);\r
112 daylight = 0;\r
113 timezone = 0;\r
114 if (sp == NULL) {\r
115 tzname[0] = tzname[1] = (char *)__UNCONST(gmt);\r
116 return;\r
117 }\r
118 for (i = 0; i < sp->typecnt; ++i) {\r
119 register const struct ttinfo * const ttisp = &sp->ttis[i];\r
120\r
121 tzname[ttisp->tt_isdst] =\r
122 &sp->chars[ttisp->tt_abbrind];\r
123 if (ttisp->tt_isdst)\r
124 daylight = 1;\r
125 if (i == 0 || !ttisp->tt_isdst)\r
126 timezone = -(ttisp->tt_gmtoff);\r
127 }\r
128 /*\r
129 ** And to get the latest zone names into tzname. . .\r
130 */\r
131 for (i = 0; i < sp->timecnt; ++i) {\r
132 register const struct ttinfo * const ttisp =\r
133 &sp->ttis[ sp->types[i] ];\r
134\r
135 tzname[ttisp->tt_isdst] =\r
136 &sp->chars[ttisp->tt_abbrind];\r
137 }\r
138}\r
139\r
140/*\r
141** Given a pointer into a time zone string, scan until a character that is not\r
142** a valid character in a zone name is found. Return a pointer to that\r
143** character.\r
144*/\r
145static const char *\r
146getzname(register const char *strp)\r
147{\r
148 register char c;\r
149\r
150 while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&\r
151 c != '+')\r
152 ++strp;\r
153 return strp;\r
154}\r
155\r
156/*\r
157** Given a pointer into a time zone string, extract a number from that string.\r
158** Check that the number is within a specified range; if it is not, return\r
159** NULL.\r
160** Otherwise, return a pointer to the first character not part of the number.\r
161*/\r
162static const char *\r
163getnum(\r
164 register const char *strp,\r
165 int * const nump,\r
166 const int min,\r
167 const int max\r
168 )\r
169{\r
170 register char c;\r
171 register int num;\r
172\r
173 if (strp == NULL || !is_digit(c = *strp))\r
174 return NULL;\r
175 num = 0;\r
176 do {\r
177 num = num * 10 + (c - '0');\r
178 if (num > max)\r
179 return NULL; /* illegal value */\r
180 c = *++strp;\r
181 } while (is_digit(c));\r
182 if (num < min)\r
183 return NULL; /* illegal value */\r
184 *nump = num;\r
185 return strp;\r
186}\r
187\r
188/*\r
189** Given a pointer into a time zone string, extract a number of seconds,\r
190** in hh[:mm[:ss]] form, from the string.\r
191** If any error occurs, return NULL.\r
192** Otherwise, return a pointer to the first character not part of the number\r
193** of seconds.\r
194*/\r
195static const char *\r
196getsecs(\r
197 register const char *strp,\r
198 LONG32 * const secsp\r
199 )\r
200{\r
201 int num;\r
202\r
203 /*\r
204 ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like\r
205 ** "M10.4.6/26", which does not conform to Posix,\r
206 ** but which specifies the equivalent of\r
207 ** ``02:00 on the first Sunday on or after 23 Oct''.\r
208 */\r
209 strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);\r
210 if (strp == NULL)\r
211 return NULL;\r
212 *secsp = (long)(num * SECSPERHOUR);\r
213 if (*strp == ':') {\r
214 ++strp;\r
215 strp = getnum(strp, &num, 0, MINSPERHOUR - 1);\r
216 if (strp == NULL)\r
217 return NULL;\r
218 *secsp += num * SECSPERMIN;\r
219 if (*strp == ':') {\r
220 ++strp;\r
221 /* `SECSPERMIN' allows for leap seconds. */\r
222 strp = getnum(strp, &num, 0, SECSPERMIN);\r
223 if (strp == NULL)\r
224 return NULL;\r
225 *secsp += num;\r
226 }\r
227 }\r
228 return strp;\r
229}\r
230\r
231/*\r
232** Given a pointer into a time zone string, extract an offset, in\r
233** [+-]hh[:mm[:ss]] form, from the string.\r
234** If any error occurs, return NULL.\r
235** Otherwise, return a pointer to the first character not part of the time.\r
236*/\r
237static const char *\r
238getoffset(\r
239 register const char *strp,\r
240 LONG32 * const offsetp\r
241 )\r
242{\r
243 register int neg = 0;\r
244\r
245 if (*strp == '-') {\r
246 neg = 1;\r
247 ++strp;\r
248 } else if (*strp == '+')\r
249 ++strp;\r
250 strp = getsecs(strp, offsetp);\r
251 if (strp == NULL)\r
252 return NULL; /* illegal time */\r
253 if (neg)\r
254 *offsetp = -*offsetp;\r
255 return strp;\r
256}\r
257\r
258/*\r
259** Given a pointer into a time zone string, extract a rule in the form\r
260** date[/time]. See POSIX section 8 for the format of "date" and "time".\r
261** If a valid rule is not found, return NULL.\r
262** Otherwise, return a pointer to the first character not part of the rule.\r
263*/\r
264static const char *\r
265getrule(\r
266 const char *strp,\r
267 register struct rule * const rulep\r
268 )\r
269{\r
270 if (*strp == 'J') {\r
271 /*\r
272 ** Julian day.\r
273 */\r
274 rulep->r_type = JULIAN_DAY;\r
275 ++strp;\r
276 strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);\r
277 } else if (*strp == 'M') {\r
278 /*\r
279 ** Month, week, day.\r
280 */\r
281 rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r
282 ++strp;\r
283 strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);\r
284 if (strp == NULL)\r
285 return NULL;\r
286 if (*strp++ != '.')\r
287 return NULL;\r
288 strp = getnum(strp, &rulep->r_week, 1, 5);\r
289 if (strp == NULL)\r
290 return NULL;\r
291 if (*strp++ != '.')\r
292 return NULL;\r
293 strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);\r
294 } else if (is_digit(*strp)) {\r
295 /*\r
296 ** Day of year.\r
297 */\r
298 rulep->r_type = DAY_OF_YEAR;\r
299 strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);\r
300 } else return NULL; /* invalid format */\r
301 if (strp == NULL)\r
302 return NULL;\r
303 if (*strp == '/') {\r
304 /*\r
305 ** Time specified.\r
306 */\r
307 ++strp;\r
308 strp = getsecs(strp, &rulep->r_time);\r
309 } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */\r
310 return strp;\r
311}\r
312\r
313static int\r
314tzload(register const char *name, register struct state * const sp)\r
315{\r
316#ifndef NO_ZONEINFO_FILES\r
317 register const char * p;\r
318 register int i;\r
319 register int fid;\r
320\r
321 if (name == NULL && (name = TZDEFAULT) == NULL)\r
322 return -1;\r
323\r
324 {\r
325 register int doaccess;\r
326 /*\r
327 ** Section 4.9.1 of the C standard says that\r
328 ** "FILENAME_MAX expands to an integral constant expression\r
329 ** that is the size needed for an array of char large enough\r
330 ** to hold the longest file name string that the implementation\r
331 ** guarantees can be opened."\r
332 */\r
333 char fullname[FILENAME_MAX + 1];\r
334\r
335 if (name[0] == ':')\r
336 ++name;\r
337 doaccess = name[0] == '/';\r
338 if (!doaccess) {\r
339 if ((p = TZDIR) == NULL)\r
340 return -1;\r
341 if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)\r
342 return -1;\r
343 (void) strcpy(fullname, p); /* XXX strcpy is safe */\r
344 (void) strcat(fullname, "/"); /* XXX strcat is safe */\r
345 (void) strcat(fullname, name); /* XXX strcat is safe */\r
346 /*\r
347 ** Set doaccess if '.' (as in "../") shows up in name.\r
348 */\r
349 if (strchr(name, '.') != NULL)\r
350 doaccess = TRUE;\r
351 name = fullname;\r
352 }\r
353 if (doaccess && access(name, R_OK) != 0)\r
354 return -1;\r
355 /*\r
356 * XXX potential security problem here if user of a set-id\r
357 * program has set TZ (which is passed in as name) here,\r
358 * and uses a race condition trick to defeat the access(2)\r
359 * above.\r
360 */\r
361 if ((fid = open(name, OPEN_MODE)) == -1)\r
362 return -1;\r
363 }\r
364 {\r
365 struct tzhead * tzhp;\r
366 union {\r
367 struct tzhead tzhead;\r
368 char buf[sizeof *sp + sizeof *tzhp];\r
369 } u;\r
370 int ttisstdcnt;\r
371 int ttisgmtcnt;\r
372\r
373 i = read(fid, u.buf, sizeof u.buf);\r
374 if (close(fid) != 0)\r
375 return -1;\r
376 ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);\r
377 ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);\r
378 sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);\r
379 sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);\r
380 sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);\r
381 sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);\r
382 p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;\r
383 if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||\r
384 sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||\r
385 sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||\r
386 sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||\r
387 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||\r
388 (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))\r
389 return -1;\r
390 if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */\r
391 sp->timecnt + /* types */\r
392 sp->typecnt * (4 + 2) + /* ttinfos */\r
393 sp->charcnt + /* chars */\r
394 sp->leapcnt * (4 + 4) + /* lsinfos */\r
395 ttisstdcnt + /* ttisstds */\r
396 ttisgmtcnt) /* ttisgmts */\r
397 return -1;\r
398 for (i = 0; i < sp->timecnt; ++i) {\r
399 sp->ats[i] = detzcode(p);\r
400 p += 4;\r
401 }\r
402 for (i = 0; i < sp->timecnt; ++i) {\r
403 sp->types[i] = (unsigned char) *p++;\r
404 if (sp->types[i] >= sp->typecnt)\r
405 return -1;\r
406 }\r
407 for (i = 0; i < sp->typecnt; ++i) {\r
408 register struct ttinfo * ttisp;\r
409\r
410 ttisp = &sp->ttis[i];\r
411 ttisp->tt_gmtoff = detzcode(p);\r
412 p += 4;\r
413 ttisp->tt_isdst = (unsigned char) *p++;\r
414 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)\r
415 return -1;\r
416 ttisp->tt_abbrind = (unsigned char) *p++;\r
417 if (ttisp->tt_abbrind < 0 ||\r
418 ttisp->tt_abbrind > sp->charcnt)\r
419 return -1;\r
420 }\r
421 for (i = 0; i < sp->charcnt; ++i)\r
422 sp->chars[i] = *p++;\r
423 sp->chars[i] = '\0'; /* ensure '\0' at end */\r
424 for (i = 0; i < sp->leapcnt; ++i) {\r
425 register struct lsinfo * lsisp;\r
426\r
427 lsisp = &sp->lsis[i];\r
428 lsisp->ls_trans = detzcode(p);\r
429 p += 4;\r
430 lsisp->ls_corr = detzcode(p);\r
431 p += 4;\r
432 }\r
433 for (i = 0; i < sp->typecnt; ++i) {\r
434 register struct ttinfo * ttisp;\r
435\r
436 ttisp = &sp->ttis[i];\r
437 if (ttisstdcnt == 0)\r
438 ttisp->tt_ttisstd = FALSE;\r
439 else {\r
440 ttisp->tt_ttisstd = *p++;\r
441 if (ttisp->tt_ttisstd != TRUE &&\r
442 ttisp->tt_ttisstd != FALSE)\r
443 return -1;\r
444 }\r
445 }\r
446 for (i = 0; i < sp->typecnt; ++i) {\r
447 register struct ttinfo * ttisp;\r
448\r
449 ttisp = &sp->ttis[i];\r
450 if (ttisgmtcnt == 0)\r
451 ttisp->tt_ttisgmt = FALSE;\r
452 else {\r
453 ttisp->tt_ttisgmt = *p++;\r
454 if (ttisp->tt_ttisgmt != TRUE &&\r
455 ttisp->tt_ttisgmt != FALSE)\r
456 return -1;\r
457 }\r
458 }\r
459 }\r
460 return 0;\r
461#else /* ! NO_ZONEINFO_FILES */\r
462 return -1;\r
463#endif\r
464}\r
465\r
466/*\r
467** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r
468** year, a rule, and the offset from UTC at the time that rule takes effect,\r
469** calculate the Epoch-relative time that rule takes effect.\r
470*/\r
471static\r
472time_t\r
473transtime(\r
474 const time_t janfirst,\r
475 const int year,\r
476 const struct rule * const rulep,\r
477 const LONG32 offset\r
478 )\r
479{\r
480 register int leapyear;\r
481 register time_t value;\r
482 register int i;\r
483 int d, m1, yy0, yy1, yy2, dow;\r
484\r
485 INITIALIZE(value);\r
486 leapyear = isleap(year);\r
487 switch (rulep->r_type) {\r
488\r
489 case JULIAN_DAY:\r
490 /*\r
491 ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r
492 ** years.\r
493 ** In non-leap years, or if the day number is 59 or less, just\r
494 ** add SECSPERDAY times the day number-1 to the time of\r
495 ** January 1, midnight, to get the day.\r
496 */\r
497 value = janfirst + (rulep->r_day - 1) * SECSPERDAY;\r
498 if (leapyear && rulep->r_day >= 60)\r
499 value += SECSPERDAY;\r
500 break;\r
501\r
502 case DAY_OF_YEAR:\r
503 /*\r
504 ** n - day of year.\r
505 ** Just add SECSPERDAY times the day number to the time of\r
506 ** January 1, midnight, to get the day.\r
507 */\r
508 value = janfirst + rulep->r_day * SECSPERDAY;\r
509 break;\r
510\r
511 case MONTH_NTH_DAY_OF_WEEK:\r
512 /*\r
513 ** Mm.n.d - nth "dth day" of month m.\r
514 */\r
515 value = janfirst;\r
516 for (i = 0; i < rulep->r_mon - 1; ++i)\r
517 value += mon_lengths[leapyear][i] * SECSPERDAY;\r
518\r
519 /*\r
520 ** Use Zeller's Congruence to get day-of-week of first day of\r
521 ** month.\r
522 */\r
523 m1 = (rulep->r_mon + 9) % 12 + 1;\r
524 yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;\r
525 yy1 = yy0 / 100;\r
526 yy2 = yy0 % 100;\r
527 dow = ((26 * m1 - 2) / 10 +\r
528 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;\r
529 if (dow < 0)\r
530 dow += DAYSPERWEEK;\r
531\r
532 /*\r
533 ** "dow" is the day-of-week of the first day of the month. Get\r
534 ** the day-of-month (zero-origin) of the first "dow" day of the\r
535 ** month.\r
536 */\r
537 d = rulep->r_day - dow;\r
538 if (d < 0)\r
539 d += DAYSPERWEEK;\r
540 for (i = 1; i < rulep->r_week; ++i) {\r
541 if (d + DAYSPERWEEK >=\r
542 mon_lengths[leapyear][rulep->r_mon - 1])\r
543 break;\r
544 d += DAYSPERWEEK;\r
545 }\r
546\r
547 /*\r
548 ** "d" is the day-of-month (zero-origin) of the day we want.\r
549 */\r
550 value += d * SECSPERDAY;\r
551 break;\r
552 }\r
553\r
554 /*\r
555 ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r
556 ** question. To get the Epoch-relative time of the specified local\r
557 ** time on that day, add the transition time and the current offset\r
558 ** from UTC.\r
559 */\r
560 return value + rulep->r_time + offset;\r
561}\r
562\r
563/*\r
564** Given a POSIX section 8-style TZ string, fill in the rule tables as\r
565** appropriate.\r
566*/\r
567static int\r
568tzparse(\r
569 const char * name,\r
570 struct state * const sp,\r
571 const int lastditch\r
572 )\r
573{\r
574 const char *stdname;\r
575 const char *dstname;\r
576 size_t stdlen;\r
577 size_t dstlen;\r
578 LONG32 stdoffset;\r
579 LONG32 dstoffset;\r
580 time_t *atp;\r
581 unsigned char *typep;\r
582 char *cp;\r
583 int load_result;\r
584\r
585 dstname = NULL;\r
586 stdname = name;\r
587 if (lastditch) {\r
588 stdlen = strlen(name); /* length of standard zone name */\r
589 name += stdlen;\r
590 if (stdlen >= sizeof sp->chars)\r
591 stdlen = (sizeof sp->chars) - 1;\r
592 stdoffset = 0;\r
593 } else {\r
594 name = getzname(name);\r
595 stdlen = name - stdname;\r
596 if (stdlen < 3)\r
597 return -1;\r
598 if (*name == '\0')\r
599 return -1;\r
600 name = getoffset(name, &stdoffset);\r
601 if (name == NULL)\r
602 return -1;\r
603 }\r
604 load_result = tzload(TZDEFRULES, sp);\r
605 if (load_result != 0)\r
606 sp->leapcnt = 0; /* so, we're off a little */\r
607 if (*name != '\0') {\r
608 dstname = name;\r
609 name = getzname(name);\r
610 dstlen = name - dstname; /* length of DST zone name */\r
611 if (dstlen < 3)\r
612 return -1;\r
613 if (*name != '\0' && *name != ',' && *name != ';') {\r
614 name = getoffset(name, &dstoffset);\r
615 if (name == NULL)\r
616 return -1;\r
617 } else dstoffset = stdoffset - SECSPERHOUR;\r
618 if (*name == '\0' && load_result != 0)\r
619 name = TZDEFRULESTRING;\r
620 if (*name == ',' || *name == ';') {\r
621 struct rule start;\r
622 struct rule end;\r
623 register int year;\r
624 register time_t janfirst;\r
625 time_t starttime;\r
626 time_t endtime;\r
627\r
628 ++name;\r
629 if ((name = getrule(name, &start)) == NULL)\r
630 return -1;\r
631 if (*name++ != ',')\r
632 return -1;\r
633 if ((name = getrule(name, &end)) == NULL)\r
634 return -1;\r
635 if (*name != '\0')\r
636 return -1;\r
637 sp->typecnt = 2; /* standard time and DST */\r
638 /*\r
639 ** Two transitions per year, from EPOCH_YEAR to 2037.\r
640 */\r
641 sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);\r
642 if (sp->timecnt > TZ_MAX_TIMES)\r
643 return -1;\r
644 sp->ttis[0].tt_gmtoff = -dstoffset;\r
645 sp->ttis[0].tt_isdst = 1;\r
646 sp->ttis[0].tt_abbrind = (int)stdlen + 1;\r
647 sp->ttis[1].tt_gmtoff = -stdoffset;\r
648 sp->ttis[1].tt_isdst = 0;\r
649 sp->ttis[1].tt_abbrind = 0;\r
650 atp = sp->ats;\r
651 typep = sp->types;\r
652 janfirst = 0;\r
653 for (year = EPOCH_YEAR; year <= 2037; ++year) {\r
654 starttime = transtime(janfirst, year, &start,\r
655 stdoffset);\r
656 endtime = transtime(janfirst, year, &end,\r
657 dstoffset);\r
658 if (starttime > endtime) {\r
659 *atp++ = endtime;\r
660 *typep++ = 1; /* DST ends */\r
661 *atp++ = starttime;\r
662 *typep++ = 0; /* DST begins */\r
663 } else {\r
664 *atp++ = starttime;\r
665 *typep++ = 0; /* DST begins */\r
666 *atp++ = endtime;\r
667 *typep++ = 1; /* DST ends */\r
668 }\r
669 janfirst += year_lengths[isleap(year)] *\r
670 SECSPERDAY;\r
671 }\r
672 } else {\r
673 register LONG32 theirstdoffset;\r
674 register LONG32 theiroffset;\r
675 register int i;\r
676 register int j;\r
677\r
678 if (*name != '\0')\r
679 return -1;\r
680 /*\r
681 ** Initial values of theirstdoffset\r
682 */\r
683 theirstdoffset = 0;\r
684 for (i = 0; i < sp->timecnt; ++i) {\r
685 j = sp->types[i];\r
686 if (!sp->ttis[j].tt_isdst) {\r
687 theirstdoffset =\r
688 -sp->ttis[j].tt_gmtoff;\r
689 break;\r
690 }\r
691 }\r
692 /*\r
693 ** Initially we're assumed to be in standard time.\r
694 */\r
695 theiroffset = theirstdoffset;\r
696 /*\r
697 ** Now juggle transition times and types\r
698 ** tracking offsets as you do.\r
699 */\r
700 for (i = 0; i < sp->timecnt; ++i) {\r
701 j = sp->types[i];\r
702 sp->types[i] = (unsigned char)sp->ttis[j].tt_isdst;\r
703 if (sp->ttis[j].tt_ttisgmt) {\r
704 /* No adjustment to transition time */\r
705 } else {\r
706 /*\r
707 ** If summer time is in effect, and the\r
708 ** transition time was not specified as\r
709 ** standard time, add the summer time\r
710 ** offset to the transition time;\r
711 ** otherwise, add the standard time\r
712 ** offset to the transition time.\r
713 */\r
714 /*\r
715 ** Transitions from DST to DDST\r
716 ** will effectively disappear since\r
717 ** POSIX provides for only one DST\r
718 ** offset.\r
719 */\r
720 sp->ats[i] += stdoffset -\r
721 theirstdoffset;\r
722 }\r
723 theiroffset = -sp->ttis[j].tt_gmtoff;\r
724 if (!sp->ttis[j].tt_isdst)\r
725 theirstdoffset = theiroffset;\r
726 }\r
727 /*\r
728 ** Finally, fill in ttis.\r
729 ** ttisstd and ttisgmt need not be handled.\r
730 */\r
731 sp->ttis[0].tt_gmtoff = -stdoffset;\r
732 sp->ttis[0].tt_isdst = FALSE;\r
733 sp->ttis[0].tt_abbrind = 0;\r
734 sp->ttis[1].tt_gmtoff = -dstoffset;\r
735 sp->ttis[1].tt_isdst = TRUE;\r
736 sp->ttis[1].tt_abbrind = (int)stdlen + 1;\r
737 sp->typecnt = 2;\r
738 }\r
739 } else {\r
740 dstlen = 0;\r
741 sp->typecnt = 1; /* only standard time */\r
742 sp->timecnt = 0;\r
743 sp->ttis[0].tt_gmtoff = -stdoffset;\r
744 sp->ttis[0].tt_isdst = 0;\r
745 sp->ttis[0].tt_abbrind = 0;\r
746 }\r
747 sp->charcnt = (int)stdlen + 1;\r
748 if (dstlen != 0)\r
749 sp->charcnt += (int)dstlen + 1;\r
750 if ((size_t) sp->charcnt > sizeof sp->chars)\r
751 return -1;\r
752 cp = sp->chars;\r
753 (void) strncpy(cp, stdname, stdlen);\r
754 cp += stdlen;\r
755 *cp++ = '\0';\r
756 if (dstlen != 0) {\r
757 (void) strncpy(cp, dstname, dstlen);\r
758 *(cp + dstlen) = '\0';\r
759 }\r
760 return 0;\r
761}\r
762\r
763void\r
2aa62f2b 764gmtload(struct state * const sp)\r
765{\r
766 if (tzload(gmt, sp) != 0)\r
767 (void) tzparse(gmt, sp, TRUE);\r
768}\r
769\r
770static void\r
771tzsetwall(void)\r
772{\r
773 if (lcl_is_set < 0)\r
774 return;\r
775 lcl_is_set = -1;\r
776\r
777 if (lclptr == NULL) {\r
778 lclptr = (struct state *) malloc(sizeof *lclptr);\r
779 if (lclptr == NULL) {\r
780 settzname(); /* all we can do */\r
781 return;\r
782 }\r
783 }\r
784 if (tzload((char *) NULL, lclptr) != 0)\r
785 gmtload(lclptr);\r
786 settzname();\r
787}\r
788\r
789void\r
2aa62f2b 790tzset(void)\r
791{\r
792 register const char * name;\r
793\r
794 name = getenv("TZ");\r
795 if (name == NULL) {\r
796 tzsetwall();\r
797 return;\r
798 }\r
799\r
800 if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)\r
801 return;\r
802 lcl_is_set = strlen(name) < sizeof lcl_TZname;\r
803 if (lcl_is_set)\r
804 (void)strncpyX(lcl_TZname, name, sizeof(lcl_TZname));\r
805\r
806 if (lclptr == NULL) {\r
807 lclptr = (struct state *) malloc(sizeof *lclptr);\r
808 if (lclptr == NULL) {\r
809 settzname(); /* all we can do */\r
810 return;\r
811 }\r
812 }\r
813 if (*name == '\0') {\r
814 /*\r
815 ** User wants it fast rather than right.\r
816 */\r
817 lclptr->leapcnt = 0; /* so, we're off a little */\r
818 lclptr->timecnt = 0;\r
819 lclptr->typecnt = 0;\r
820 lclptr->ttis[0].tt_isdst = 0;\r
821 lclptr->ttis[0].tt_gmtoff = 0;\r
822 lclptr->ttis[0].tt_abbrind = 0;\r
823 (void)strncpyX(lclptr->chars, gmt, sizeof(lclptr->chars));\r
824 } else if (tzload(name, lclptr) != 0)\r
825 if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)\r
826 (void) gmtload(lclptr);\r
827 settzname();\r
828}\r