]> granicus.if.org Git - postgresql/blob - src/timezone/localtime.c
Add Olson's public domain timezone library to src/timezone.
[postgresql] / src / timezone / localtime.c
1 /*\r
2 ** This file is in the public domain, so clarified as of\r
3 ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).\r
4 */\r
5 \r
6 #ifndef lint\r
7 #ifndef NOID\r
8 static char     elsieid[] = "@(#)localtime.c    7.78";\r
9 #endif /* !defined NOID */\r
10 #endif /* !defined lint */\r
11 \r
12 /*\r
13 ** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).\r
14 ** POSIX-style TZ environment variable handling from Guy Harris\r
15 ** (guy@auspex.com).\r
16 */\r
17 \r
18 /*LINTLIBRARY*/\r
19 \r
20 #include "private.h"\r
21 #include "tzfile.h"\r
22 #include "fcntl.h"\r
23 \r
24 /*\r
25 ** SunOS 4.1.1 headers lack O_BINARY.\r
26 */\r
27 \r
28 #ifdef O_BINARY\r
29 #define OPEN_MODE       (O_RDONLY | O_BINARY)\r
30 #endif /* defined O_BINARY */\r
31 #ifndef O_BINARY\r
32 #define OPEN_MODE       O_RDONLY\r
33 #endif /* !defined O_BINARY */\r
34 \r
35 #ifndef WILDABBR\r
36 /*\r
37 ** Someone might make incorrect use of a time zone abbreviation:\r
38 **      1.      They might reference tzname[0] before calling tzset (explicitly\r
39 **              or implicitly).\r
40 **      2.      They might reference tzname[1] before calling tzset (explicitly\r
41 **              or implicitly).\r
42 **      3.      They might reference tzname[1] after setting to a time zone\r
43 **              in which Daylight Saving Time is never observed.\r
44 **      4.      They might reference tzname[0] after setting to a time zone\r
45 **              in which Standard Time is never observed.\r
46 **      5.      They might reference tm.TM_ZONE after calling offtime.\r
47 ** What's best to do in the above cases is open to debate;\r
48 ** for now, we just set things up so that in any of the five cases\r
49 ** WILDABBR is used.  Another possibility:  initialize tzname[0] to the\r
50 ** string "tzname[0] used before set", and similarly for the other cases.\r
51 ** And another:  initialize tzname[0] to "ERA", with an explanation in the\r
52 ** manual page of what this "time zone abbreviation" means (doing this so\r
53 ** that tzname[0] has the "normal" length of three characters).\r
54 */\r
55 #define WILDABBR        "   "\r
56 #endif /* !defined WILDABBR */\r
57 \r
58 static char             wildabbr[] = "WILDABBR";\r
59 \r
60 static const char       gmt[] = "GMT";\r
61 \r
62 /*\r
63 ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.\r
64 ** We default to US rules as of 1999-08-17.\r
65 ** POSIX 1003.1 section 8.1.1 says that the default DST rules are\r
66 ** implementation dependent; for historical reasons, US rules are a\r
67 ** common default.\r
68 */\r
69 #ifndef TZDEFRULESTRING\r
70 #define TZDEFRULESTRING ",M4.1.0,M10.5.0"\r
71 #endif /* !defined TZDEFDST */\r
72 \r
73 struct ttinfo {                         /* time type information */\r
74         long            tt_gmtoff;      /* UTC offset in seconds */\r
75         int             tt_isdst;       /* used to set tm_isdst */\r
76         int             tt_abbrind;     /* abbreviation list index */\r
77         int             tt_ttisstd;     /* TRUE if transition is std time */\r
78         int             tt_ttisgmt;     /* TRUE if transition is UTC */\r
79 };\r
80 \r
81 struct lsinfo {                         /* leap second information */\r
82         time_t          ls_trans;       /* transition time */\r
83         long            ls_corr;        /* correction to apply */\r
84 };\r
85 \r
86 #define BIGGEST(a, b)   (((a) > (b)) ? (a) : (b))\r
87 \r
88 #ifdef TZNAME_MAX\r
89 #define MY_TZNAME_MAX   TZNAME_MAX\r
90 #endif /* defined TZNAME_MAX */\r
91 #ifndef TZNAME_MAX\r
92 #define MY_TZNAME_MAX   255\r
93 #endif /* !defined TZNAME_MAX */\r
94 \r
95 struct state {\r
96         int             leapcnt;\r
97         int             timecnt;\r
98         int             typecnt;\r
99         int             charcnt;\r
100         time_t          ats[TZ_MAX_TIMES];\r
101         unsigned char   types[TZ_MAX_TIMES];\r
102         struct ttinfo   ttis[TZ_MAX_TYPES];\r
103         char            chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),\r
104                                 (2 * (MY_TZNAME_MAX + 1)))];\r
105         struct lsinfo   lsis[TZ_MAX_LEAPS];\r
106 };\r
107 \r
108 struct rule {\r
109         int             r_type;         /* type of rule--see below */\r
110         int             r_day;          /* day number of rule */\r
111         int             r_week;         /* week number of rule */\r
112         int             r_mon;          /* month number of rule */\r
113         long            r_time;         /* transition time of rule */\r
114 };\r
115 \r
116 #define JULIAN_DAY              0       /* Jn - Julian day */\r
117 #define DAY_OF_YEAR             1       /* n - day of year */\r
118 #define MONTH_NTH_DAY_OF_WEEK   2       /* Mm.n.d - month, week, day of week */\r
119 \r
120 /*\r
121 ** Prototypes for static functions.\r
122 */\r
123 \r
124 static long             detzcode P((const char * codep));\r
125 static const char *     getzname P((const char * strp));\r
126 static const char *     getnum P((const char * strp, int * nump, int min,\r
127                                 int max));\r
128 static const char *     getsecs P((const char * strp, long * secsp));\r
129 static const char *     getoffset P((const char * strp, long * offsetp));\r
130 static const char *     getrule P((const char * strp, struct rule * rulep));\r
131 static void             gmtload P((struct state * sp));\r
132 static void             gmtsub P((const time_t * timep, long offset,\r
133                                 struct tm * tmp));\r
134 static void             localsub P((const time_t * timep, long offset,\r
135                                 struct tm * tmp));\r
136 static int              increment_overflow P((int * number, int delta));\r
137 static int              normalize_overflow P((int * tensptr, int * unitsptr,\r
138                                 int base));\r
139 static void             settzname P((void));\r
140 static time_t           time1 P((struct tm * tmp,\r
141                                 void(*funcp) P((const time_t *,\r
142                                 long, struct tm *)),\r
143                                 long offset));\r
144 static time_t           time2 P((struct tm *tmp,\r
145                                 void(*funcp) P((const time_t *,\r
146                                 long, struct tm*)),\r
147                                 long offset, int * okayp));\r
148 static time_t           time2sub P((struct tm *tmp,\r
149                                 void(*funcp) P((const time_t *,\r
150                                 long, struct tm*)),\r
151                                 long offset, int * okayp, int do_norm_secs));\r
152 static void             timesub P((const time_t * timep, long offset,\r
153                                 const struct state * sp, struct tm * tmp));\r
154 static int              tmcomp P((const struct tm * atmp,\r
155                                 const struct tm * btmp));\r
156 static time_t           transtime P((time_t janfirst, int year,\r
157                                 const struct rule * rulep, long offset));\r
158 static int              tzload P((const char * name, struct state * sp));\r
159 static int              tzparse P((const char * name, struct state * sp,\r
160                                 int lastditch));\r
161 \r
162 #ifdef ALL_STATE\r
163 static struct state *   lclptr;\r
164 static struct state *   gmtptr;\r
165 #endif /* defined ALL_STATE */\r
166 \r
167 #ifndef ALL_STATE\r
168 static struct state     lclmem;\r
169 static struct state     gmtmem;\r
170 #define lclptr          (&lclmem)\r
171 #define gmtptr          (&gmtmem)\r
172 #endif /* State Farm */\r
173 \r
174 #ifndef TZ_STRLEN_MAX\r
175 #define TZ_STRLEN_MAX 255\r
176 #endif /* !defined TZ_STRLEN_MAX */\r
177 \r
178 static char             lcl_TZname[TZ_STRLEN_MAX + 1];\r
179 static int              lcl_is_set;\r
180 static int              gmt_is_set;\r
181 \r
182 char *                  tzname[2] = {\r
183         wildabbr,\r
184         wildabbr\r
185 };\r
186 \r
187 /*\r
188 ** Section 4.12.3 of X3.159-1989 requires that\r
189 **      Except for the strftime function, these functions [asctime,\r
190 **      ctime, gmtime, localtime] return values in one of two static\r
191 **      objects: a broken-down time structure and an array of char.\r
192 ** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.\r
193 */\r
194 \r
195 static struct tm        tm;\r
196 \r
197 #ifdef USG_COMPAT\r
198 time_t                  timezone = 0;\r
199 int                     daylight = 0;\r
200 #endif /* defined USG_COMPAT */\r
201 \r
202 #ifdef ALTZONE\r
203 time_t                  altzone = 0;\r
204 #endif /* defined ALTZONE */\r
205 \r
206 static long\r
207 detzcode(codep)\r
208 const char * const      codep;\r
209 {\r
210         register long   result;\r
211         register int    i;\r
212 \r
213         result = (codep[0] & 0x80) ? ~0L : 0L;\r
214         for (i = 0; i < 4; ++i)\r
215                 result = (result << 8) | (codep[i] & 0xff);\r
216         return result;\r
217 }\r
218 \r
219 static void\r
220 settzname P((void))\r
221 {\r
222         register struct state * const   sp = lclptr;\r
223         register int                    i;\r
224 \r
225         tzname[0] = wildabbr;\r
226         tzname[1] = wildabbr;\r
227 #ifdef USG_COMPAT\r
228         daylight = 0;\r
229         timezone = 0;\r
230 #endif /* defined USG_COMPAT */\r
231 #ifdef ALTZONE\r
232         altzone = 0;\r
233 #endif /* defined ALTZONE */\r
234 #ifdef ALL_STATE\r
235         if (sp == NULL) {\r
236                 tzname[0] = tzname[1] = gmt;\r
237                 return;\r
238         }\r
239 #endif /* defined ALL_STATE */\r
240         for (i = 0; i < sp->typecnt; ++i) {\r
241                 register const struct ttinfo * const    ttisp = &sp->ttis[i];\r
242 \r
243                 tzname[ttisp->tt_isdst] =\r
244                         &sp->chars[ttisp->tt_abbrind];\r
245 #ifdef USG_COMPAT\r
246                 if (ttisp->tt_isdst)\r
247                         daylight = 1;\r
248                 if (i == 0 || !ttisp->tt_isdst)\r
249                         timezone = -(ttisp->tt_gmtoff);\r
250 #endif /* defined USG_COMPAT */\r
251 #ifdef ALTZONE\r
252                 if (i == 0 || ttisp->tt_isdst)\r
253                         altzone = -(ttisp->tt_gmtoff);\r
254 #endif /* defined ALTZONE */\r
255         }\r
256         /*\r
257         ** And to get the latest zone names into tzname. . .\r
258         */\r
259         for (i = 0; i < sp->timecnt; ++i) {\r
260                 register const struct ttinfo * const    ttisp =\r
261                                                         &sp->ttis[\r
262                                                                 sp->types[i]];\r
263 \r
264                 tzname[ttisp->tt_isdst] =\r
265                         &sp->chars[ttisp->tt_abbrind];\r
266         }\r
267 }\r
268 \r
269 static int\r
270 tzload(name, sp)\r
271 register const char *           name;\r
272 register struct state * const   sp;\r
273 {\r
274         register const char *   p;\r
275         register int            i;\r
276         register int            fid;\r
277 \r
278         if (name == NULL && (name = TZDEFAULT) == NULL)\r
279                 return -1;\r
280         {\r
281                 register int    doaccess;\r
282                 /*\r
283                 ** Section 4.9.1 of the C standard says that\r
284                 ** "FILENAME_MAX expands to an integral constant expression\r
285                 ** that is the size needed for an array of char large enough\r
286                 ** to hold the longest file name string that the implementation\r
287                 ** guarantees can be opened."\r
288                 */\r
289                 char            fullname[FILENAME_MAX + 1];\r
290 \r
291                 if (name[0] == ':')\r
292                         ++name;\r
293                 doaccess = name[0] == '/';\r
294                 if (!doaccess) {\r
295                         if ((p = TZDIR) == NULL)\r
296                                 return -1;\r
297                         if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)\r
298                                 return -1;\r
299                         (void) strcpy(fullname, p);\r
300                         (void) strcat(fullname, "/");\r
301                         (void) strcat(fullname, name);\r
302                         /*\r
303                         ** Set doaccess if '.' (as in "../") shows up in name.\r
304                         */\r
305                         if (strchr(name, '.') != NULL)\r
306                                 doaccess = TRUE;\r
307                         name = fullname;\r
308                 }\r
309                 if (doaccess && access(name, R_OK) != 0)\r
310                         return -1;\r
311                 if ((fid = open(name, OPEN_MODE)) == -1)\r
312                         return -1;\r
313         }\r
314         {\r
315                 struct tzhead * tzhp;\r
316                 union {\r
317                         struct tzhead   tzhead;\r
318                         char            buf[sizeof *sp + sizeof *tzhp];\r
319                 } u;\r
320                 int             ttisstdcnt;\r
321                 int             ttisgmtcnt;\r
322 \r
323                 i = read(fid, u.buf, sizeof u.buf);\r
324                 if (close(fid) != 0)\r
325                         return -1;\r
326                 ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);\r
327                 ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);\r
328                 sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);\r
329                 sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);\r
330                 sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);\r
331                 sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);\r
332                 p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;\r
333                 if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||\r
334                         sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||\r
335                         sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||\r
336                         sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||\r
337                         (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||\r
338                         (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))\r
339                                 return -1;\r
340                 if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */\r
341                         sp->timecnt +                   /* types */\r
342                         sp->typecnt * (4 + 2) +         /* ttinfos */\r
343                         sp->charcnt +                   /* chars */\r
344                         sp->leapcnt * (4 + 4) +         /* lsinfos */\r
345                         ttisstdcnt +                    /* ttisstds */\r
346                         ttisgmtcnt)                     /* ttisgmts */\r
347                                 return -1;\r
348                 for (i = 0; i < sp->timecnt; ++i) {\r
349                         sp->ats[i] = detzcode(p);\r
350                         p += 4;\r
351                 }\r
352                 for (i = 0; i < sp->timecnt; ++i) {\r
353                         sp->types[i] = (unsigned char) *p++;\r
354                         if (sp->types[i] >= sp->typecnt)\r
355                                 return -1;\r
356                 }\r
357                 for (i = 0; i < sp->typecnt; ++i) {\r
358                         register struct ttinfo *        ttisp;\r
359 \r
360                         ttisp = &sp->ttis[i];\r
361                         ttisp->tt_gmtoff = detzcode(p);\r
362                         p += 4;\r
363                         ttisp->tt_isdst = (unsigned char) *p++;\r
364                         if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)\r
365                                 return -1;\r
366                         ttisp->tt_abbrind = (unsigned char) *p++;\r
367                         if (ttisp->tt_abbrind < 0 ||\r
368                                 ttisp->tt_abbrind > sp->charcnt)\r
369                                         return -1;\r
370                 }\r
371                 for (i = 0; i < sp->charcnt; ++i)\r
372                         sp->chars[i] = *p++;\r
373                 sp->chars[i] = '\0';    /* ensure '\0' at end */\r
374                 for (i = 0; i < sp->leapcnt; ++i) {\r
375                         register struct lsinfo *        lsisp;\r
376 \r
377                         lsisp = &sp->lsis[i];\r
378                         lsisp->ls_trans = detzcode(p);\r
379                         p += 4;\r
380                         lsisp->ls_corr = detzcode(p);\r
381                         p += 4;\r
382                 }\r
383                 for (i = 0; i < sp->typecnt; ++i) {\r
384                         register struct ttinfo *        ttisp;\r
385 \r
386                         ttisp = &sp->ttis[i];\r
387                         if (ttisstdcnt == 0)\r
388                                 ttisp->tt_ttisstd = FALSE;\r
389                         else {\r
390                                 ttisp->tt_ttisstd = *p++;\r
391                                 if (ttisp->tt_ttisstd != TRUE &&\r
392                                         ttisp->tt_ttisstd != FALSE)\r
393                                                 return -1;\r
394                         }\r
395                 }\r
396                 for (i = 0; i < sp->typecnt; ++i) {\r
397                         register struct ttinfo *        ttisp;\r
398 \r
399                         ttisp = &sp->ttis[i];\r
400                         if (ttisgmtcnt == 0)\r
401                                 ttisp->tt_ttisgmt = FALSE;\r
402                         else {\r
403                                 ttisp->tt_ttisgmt = *p++;\r
404                                 if (ttisp->tt_ttisgmt != TRUE &&\r
405                                         ttisp->tt_ttisgmt != FALSE)\r
406                                                 return -1;\r
407                         }\r
408                 }\r
409         }\r
410         return 0;\r
411 }\r
412 \r
413 static const int        mon_lengths[2][MONSPERYEAR] = {\r
414         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },\r
415         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }\r
416 };\r
417 \r
418 static const int        year_lengths[2] = {\r
419         DAYSPERNYEAR, DAYSPERLYEAR\r
420 };\r
421 \r
422 /*\r
423 ** Given a pointer into a time zone string, scan until a character that is not\r
424 ** a valid character in a zone name is found.  Return a pointer to that\r
425 ** character.\r
426 */\r
427 \r
428 static const char *\r
429 getzname(strp)\r
430 register const char *   strp;\r
431 {\r
432         register char   c;\r
433 \r
434         while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&\r
435                 c != '+')\r
436                         ++strp;\r
437         return strp;\r
438 }\r
439 \r
440 /*\r
441 ** Given a pointer into a time zone string, extract a number from that string.\r
442 ** Check that the number is within a specified range; if it is not, return\r
443 ** NULL.\r
444 ** Otherwise, return a pointer to the first character not part of the number.\r
445 */\r
446 \r
447 static const char *\r
448 getnum(strp, nump, min, max)\r
449 register const char *   strp;\r
450 int * const             nump;\r
451 const int               min;\r
452 const int               max;\r
453 {\r
454         register char   c;\r
455         register int    num;\r
456 \r
457         if (strp == NULL || !is_digit(c = *strp))\r
458                 return NULL;\r
459         num = 0;\r
460         do {\r
461                 num = num * 10 + (c - '0');\r
462                 if (num > max)\r
463                         return NULL;    /* illegal value */\r
464                 c = *++strp;\r
465         } while (is_digit(c));\r
466         if (num < min)\r
467                 return NULL;            /* illegal value */\r
468         *nump = num;\r
469         return strp;\r
470 }\r
471 \r
472 /*\r
473 ** Given a pointer into a time zone string, extract a number of seconds,\r
474 ** in hh[:mm[:ss]] form, from the string.\r
475 ** If any error occurs, return NULL.\r
476 ** Otherwise, return a pointer to the first character not part of the number\r
477 ** of seconds.\r
478 */\r
479 \r
480 static const char *\r
481 getsecs(strp, secsp)\r
482 register const char *   strp;\r
483 long * const            secsp;\r
484 {\r
485         int     num;\r
486 \r
487         /*\r
488         ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like\r
489         ** "M10.4.6/26", which does not conform to Posix,\r
490         ** but which specifies the equivalent of\r
491         ** ``02:00 on the first Sunday on or after 23 Oct''.\r
492         */\r
493         strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);\r
494         if (strp == NULL)\r
495                 return NULL;\r
496         *secsp = num * (long) SECSPERHOUR;\r
497         if (*strp == ':') {\r
498                 ++strp;\r
499                 strp = getnum(strp, &num, 0, MINSPERHOUR - 1);\r
500                 if (strp == NULL)\r
501                         return NULL;\r
502                 *secsp += num * SECSPERMIN;\r
503                 if (*strp == ':') {\r
504                         ++strp;\r
505                         /* `SECSPERMIN' allows for leap seconds.  */\r
506                         strp = getnum(strp, &num, 0, SECSPERMIN);\r
507                         if (strp == NULL)\r
508                                 return NULL;\r
509                         *secsp += num;\r
510                 }\r
511         }\r
512         return strp;\r
513 }\r
514 \r
515 /*\r
516 ** Given a pointer into a time zone string, extract an offset, in\r
517 ** [+-]hh[:mm[:ss]] form, from the string.\r
518 ** If any error occurs, return NULL.\r
519 ** Otherwise, return a pointer to the first character not part of the time.\r
520 */\r
521 \r
522 static const char *\r
523 getoffset(strp, offsetp)\r
524 register const char *   strp;\r
525 long * const            offsetp;\r
526 {\r
527         register int    neg = 0;\r
528 \r
529         if (*strp == '-') {\r
530                 neg = 1;\r
531                 ++strp;\r
532         } else if (*strp == '+')\r
533                 ++strp;\r
534         strp = getsecs(strp, offsetp);\r
535         if (strp == NULL)\r
536                 return NULL;            /* illegal time */\r
537         if (neg)\r
538                 *offsetp = -*offsetp;\r
539         return strp;\r
540 }\r
541 \r
542 /*\r
543 ** Given a pointer into a time zone string, extract a rule in the form\r
544 ** date[/time].  See POSIX section 8 for the format of "date" and "time".\r
545 ** If a valid rule is not found, return NULL.\r
546 ** Otherwise, return a pointer to the first character not part of the rule.\r
547 */\r
548 \r
549 static const char *\r
550 getrule(strp, rulep)\r
551 const char *                    strp;\r
552 register struct rule * const    rulep;\r
553 {\r
554         if (*strp == 'J') {\r
555                 /*\r
556                 ** Julian day.\r
557                 */\r
558                 rulep->r_type = JULIAN_DAY;\r
559                 ++strp;\r
560                 strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);\r
561         } else if (*strp == 'M') {\r
562                 /*\r
563                 ** Month, week, day.\r
564                 */\r
565                 rulep->r_type = MONTH_NTH_DAY_OF_WEEK;\r
566                 ++strp;\r
567                 strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);\r
568                 if (strp == NULL)\r
569                         return NULL;\r
570                 if (*strp++ != '.')\r
571                         return NULL;\r
572                 strp = getnum(strp, &rulep->r_week, 1, 5);\r
573                 if (strp == NULL)\r
574                         return NULL;\r
575                 if (*strp++ != '.')\r
576                         return NULL;\r
577                 strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);\r
578         } else if (is_digit(*strp)) {\r
579                 /*\r
580                 ** Day of year.\r
581                 */\r
582                 rulep->r_type = DAY_OF_YEAR;\r
583                 strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);\r
584         } else  return NULL;            /* invalid format */\r
585         if (strp == NULL)\r
586                 return NULL;\r
587         if (*strp == '/') {\r
588                 /*\r
589                 ** Time specified.\r
590                 */\r
591                 ++strp;\r
592                 strp = getsecs(strp, &rulep->r_time);\r
593         } else  rulep->r_time = 2 * SECSPERHOUR;        /* default = 2:00:00 */\r
594         return strp;\r
595 }\r
596 \r
597 /*\r
598 ** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the\r
599 ** year, a rule, and the offset from UTC at the time that rule takes effect,\r
600 ** calculate the Epoch-relative time that rule takes effect.\r
601 */\r
602 \r
603 static time_t\r
604 transtime(janfirst, year, rulep, offset)\r
605 const time_t                            janfirst;\r
606 const int                               year;\r
607 register const struct rule * const      rulep;\r
608 const long                              offset;\r
609 {\r
610         register int    leapyear;\r
611         register time_t value;\r
612         register int    i;\r
613         int             d, m1, yy0, yy1, yy2, dow;\r
614 \r
615         INITIALIZE(value);\r
616         leapyear = isleap(year);\r
617         switch (rulep->r_type) {\r
618 \r
619         case JULIAN_DAY:\r
620                 /*\r
621                 ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap\r
622                 ** years.\r
623                 ** In non-leap years, or if the day number is 59 or less, just\r
624                 ** add SECSPERDAY times the day number-1 to the time of\r
625                 ** January 1, midnight, to get the day.\r
626                 */\r
627                 value = janfirst + (rulep->r_day - 1) * SECSPERDAY;\r
628                 if (leapyear && rulep->r_day >= 60)\r
629                         value += SECSPERDAY;\r
630                 break;\r
631 \r
632         case DAY_OF_YEAR:\r
633                 /*\r
634                 ** n - day of year.\r
635                 ** Just add SECSPERDAY times the day number to the time of\r
636                 ** January 1, midnight, to get the day.\r
637                 */\r
638                 value = janfirst + rulep->r_day * SECSPERDAY;\r
639                 break;\r
640 \r
641         case MONTH_NTH_DAY_OF_WEEK:\r
642                 /*\r
643                 ** Mm.n.d - nth "dth day" of month m.\r
644                 */\r
645                 value = janfirst;\r
646                 for (i = 0; i < rulep->r_mon - 1; ++i)\r
647                         value += mon_lengths[leapyear][i] * SECSPERDAY;\r
648 \r
649                 /*\r
650                 ** Use Zeller's Congruence to get day-of-week of first day of\r
651                 ** month.\r
652                 */\r
653                 m1 = (rulep->r_mon + 9) % 12 + 1;\r
654                 yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;\r
655                 yy1 = yy0 / 100;\r
656                 yy2 = yy0 % 100;\r
657                 dow = ((26 * m1 - 2) / 10 +\r
658                         1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;\r
659                 if (dow < 0)\r
660                         dow += DAYSPERWEEK;\r
661 \r
662                 /*\r
663                 ** "dow" is the day-of-week of the first day of the month.  Get\r
664                 ** the day-of-month (zero-origin) of the first "dow" day of the\r
665                 ** month.\r
666                 */\r
667                 d = rulep->r_day - dow;\r
668                 if (d < 0)\r
669                         d += DAYSPERWEEK;\r
670                 for (i = 1; i < rulep->r_week; ++i) {\r
671                         if (d + DAYSPERWEEK >=\r
672                                 mon_lengths[leapyear][rulep->r_mon - 1])\r
673                                         break;\r
674                         d += DAYSPERWEEK;\r
675                 }\r
676 \r
677                 /*\r
678                 ** "d" is the day-of-month (zero-origin) of the day we want.\r
679                 */\r
680                 value += d * SECSPERDAY;\r
681                 break;\r
682         }\r
683 \r
684         /*\r
685         ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in\r
686         ** question.  To get the Epoch-relative time of the specified local\r
687         ** time on that day, add the transition time and the current offset\r
688         ** from UTC.\r
689         */\r
690         return value + rulep->r_time + offset;\r
691 }\r
692 \r
693 /*\r
694 ** Given a POSIX section 8-style TZ string, fill in the rule tables as\r
695 ** appropriate.\r
696 */\r
697 \r
698 static int\r
699 tzparse(name, sp, lastditch)\r
700 const char *                    name;\r
701 register struct state * const   sp;\r
702 const int                       lastditch;\r
703 {\r
704         const char *                    stdname;\r
705         const char *                    dstname;\r
706         size_t                          stdlen;\r
707         size_t                          dstlen;\r
708         long                            stdoffset;\r
709         long                            dstoffset;\r
710         register time_t *               atp;\r
711         register unsigned char *        typep;\r
712         register char *                 cp;\r
713         register int                    load_result;\r
714 \r
715         INITIALIZE(dstname);\r
716         stdname = name;\r
717         if (lastditch) {\r
718                 stdlen = strlen(name);  /* length of standard zone name */\r
719                 name += stdlen;\r
720                 if (stdlen >= sizeof sp->chars)\r
721                         stdlen = (sizeof sp->chars) - 1;\r
722                 stdoffset = 0;\r
723         } else {\r
724                 name = getzname(name);\r
725                 stdlen = name - stdname;\r
726                 if (stdlen < 3)\r
727                         return -1;\r
728                 if (*name == '\0')\r
729                         return -1;\r
730                 name = getoffset(name, &stdoffset);\r
731                 if (name == NULL)\r
732                         return -1;\r
733         }\r
734         load_result = tzload(TZDEFRULES, sp);\r
735         if (load_result != 0)\r
736                 sp->leapcnt = 0;                /* so, we're off a little */\r
737         if (*name != '\0') {\r
738                 dstname = name;\r
739                 name = getzname(name);\r
740                 dstlen = name - dstname;        /* length of DST zone name */\r
741                 if (dstlen < 3)\r
742                         return -1;\r
743                 if (*name != '\0' && *name != ',' && *name != ';') {\r
744                         name = getoffset(name, &dstoffset);\r
745                         if (name == NULL)\r
746                                 return -1;\r
747                 } else  dstoffset = stdoffset - SECSPERHOUR;\r
748                 if (*name == '\0' && load_result != 0)\r
749                         name = TZDEFRULESTRING;\r
750                 if (*name == ',' || *name == ';') {\r
751                         struct rule     start;\r
752                         struct rule     end;\r
753                         register int    year;\r
754                         register time_t janfirst;\r
755                         time_t          starttime;\r
756                         time_t          endtime;\r
757 \r
758                         ++name;\r
759                         if ((name = getrule(name, &start)) == NULL)\r
760                                 return -1;\r
761                         if (*name++ != ',')\r
762                                 return -1;\r
763                         if ((name = getrule(name, &end)) == NULL)\r
764                                 return -1;\r
765                         if (*name != '\0')\r
766                                 return -1;\r
767                         sp->typecnt = 2;        /* standard time and DST */\r
768                         /*\r
769                         ** Two transitions per year, from EPOCH_YEAR to 2037.\r
770                         */\r
771                         sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);\r
772                         if (sp->timecnt > TZ_MAX_TIMES)\r
773                                 return -1;\r
774                         sp->ttis[0].tt_gmtoff = -dstoffset;\r
775                         sp->ttis[0].tt_isdst = 1;\r
776                         sp->ttis[0].tt_abbrind = stdlen + 1;\r
777                         sp->ttis[1].tt_gmtoff = -stdoffset;\r
778                         sp->ttis[1].tt_isdst = 0;\r
779                         sp->ttis[1].tt_abbrind = 0;\r
780                         atp = sp->ats;\r
781                         typep = sp->types;\r
782                         janfirst = 0;\r
783                         for (year = EPOCH_YEAR; year <= 2037; ++year) {\r
784                                 starttime = transtime(janfirst, year, &start,\r
785                                         stdoffset);\r
786                                 endtime = transtime(janfirst, year, &end,\r
787                                         dstoffset);\r
788                                 if (starttime > endtime) {\r
789                                         *atp++ = endtime;\r
790                                         *typep++ = 1;   /* DST ends */\r
791                                         *atp++ = starttime;\r
792                                         *typep++ = 0;   /* DST begins */\r
793                                 } else {\r
794                                         *atp++ = starttime;\r
795                                         *typep++ = 0;   /* DST begins */\r
796                                         *atp++ = endtime;\r
797                                         *typep++ = 1;   /* DST ends */\r
798                                 }\r
799                                 janfirst += year_lengths[isleap(year)] *\r
800                                         SECSPERDAY;\r
801                         }\r
802                 } else {\r
803                         register long   theirstdoffset;\r
804                         register long   theirdstoffset;\r
805                         register long   theiroffset;\r
806                         register int    isdst;\r
807                         register int    i;\r
808                         register int    j;\r
809 \r
810                         if (*name != '\0')\r
811                                 return -1;\r
812                         /*\r
813                         ** Initial values of theirstdoffset and theirdstoffset.\r
814                         */\r
815                         theirstdoffset = 0;\r
816                         for (i = 0; i < sp->timecnt; ++i) {\r
817                                 j = sp->types[i];\r
818                                 if (!sp->ttis[j].tt_isdst) {\r
819                                         theirstdoffset =\r
820                                                 -sp->ttis[j].tt_gmtoff;\r
821                                         break;\r
822                                 }\r
823                         }\r
824                         theirdstoffset = 0;\r
825                         for (i = 0; i < sp->timecnt; ++i) {\r
826                                 j = sp->types[i];\r
827                                 if (sp->ttis[j].tt_isdst) {\r
828                                         theirdstoffset =\r
829                                                 -sp->ttis[j].tt_gmtoff;\r
830                                         break;\r
831                                 }\r
832                         }\r
833                         /*\r
834                         ** Initially we're assumed to be in standard time.\r
835                         */\r
836                         isdst = FALSE;\r
837                         theiroffset = theirstdoffset;\r
838                         /*\r
839                         ** Now juggle transition times and types\r
840                         ** tracking offsets as you do.\r
841                         */\r
842                         for (i = 0; i < sp->timecnt; ++i) {\r
843                                 j = sp->types[i];\r
844                                 sp->types[i] = sp->ttis[j].tt_isdst;\r
845                                 if (sp->ttis[j].tt_ttisgmt) {\r
846                                         /* No adjustment to transition time */\r
847                                 } else {\r
848                                         /*\r
849                                         ** If summer time is in effect, and the\r
850                                         ** transition time was not specified as\r
851                                         ** standard time, add the summer time\r
852                                         ** offset to the transition time;\r
853                                         ** otherwise, add the standard time\r
854                                         ** offset to the transition time.\r
855                                         */\r
856                                         /*\r
857                                         ** Transitions from DST to DDST\r
858                                         ** will effectively disappear since\r
859                                         ** POSIX provides for only one DST\r
860                                         ** offset.\r
861                                         */\r
862                                         if (isdst && !sp->ttis[j].tt_ttisstd) {\r
863                                                 sp->ats[i] += dstoffset -\r
864                                                         theirdstoffset;\r
865                                         } else {\r
866                                                 sp->ats[i] += stdoffset -\r
867                                                         theirstdoffset;\r
868                                         }\r
869                                 }\r
870                                 theiroffset = -sp->ttis[j].tt_gmtoff;\r
871                                 if (sp->ttis[j].tt_isdst)\r
872                                         theirdstoffset = theiroffset;\r
873                                 else    theirstdoffset = theiroffset;\r
874                         }\r
875                         /*\r
876                         ** Finally, fill in ttis.\r
877                         ** ttisstd and ttisgmt need not be handled.\r
878                         */\r
879                         sp->ttis[0].tt_gmtoff = -stdoffset;\r
880                         sp->ttis[0].tt_isdst = FALSE;\r
881                         sp->ttis[0].tt_abbrind = 0;\r
882                         sp->ttis[1].tt_gmtoff = -dstoffset;\r
883                         sp->ttis[1].tt_isdst = TRUE;\r
884                         sp->ttis[1].tt_abbrind = stdlen + 1;\r
885                         sp->typecnt = 2;\r
886                 }\r
887         } else {\r
888                 dstlen = 0;\r
889                 sp->typecnt = 1;                /* only standard time */\r
890                 sp->timecnt = 0;\r
891                 sp->ttis[0].tt_gmtoff = -stdoffset;\r
892                 sp->ttis[0].tt_isdst = 0;\r
893                 sp->ttis[0].tt_abbrind = 0;\r
894         }\r
895         sp->charcnt = stdlen + 1;\r
896         if (dstlen != 0)\r
897                 sp->charcnt += dstlen + 1;\r
898         if ((size_t) sp->charcnt > sizeof sp->chars)\r
899                 return -1;\r
900         cp = sp->chars;\r
901         (void) strncpy(cp, stdname, stdlen);\r
902         cp += stdlen;\r
903         *cp++ = '\0';\r
904         if (dstlen != 0) {\r
905                 (void) strncpy(cp, dstname, dstlen);\r
906                 *(cp + dstlen) = '\0';\r
907         }\r
908         return 0;\r
909 }\r
910 \r
911 static void\r
912 gmtload(sp)\r
913 struct state * const    sp;\r
914 {\r
915         if (tzload(gmt, sp) != 0)\r
916                 (void) tzparse(gmt, sp, TRUE);\r
917 }\r
918 \r
919 #ifndef STD_INSPIRED\r
920 /*\r
921 ** A non-static declaration of tzsetwall in a system header file\r
922 ** may cause a warning about this upcoming static declaration...\r
923 */\r
924 static\r
925 #endif /* !defined STD_INSPIRED */\r
926 void\r
927 tzsetwall P((void))\r
928 {\r
929         if (lcl_is_set < 0)\r
930                 return;\r
931         lcl_is_set = -1;\r
932 \r
933 #ifdef ALL_STATE\r
934         if (lclptr == NULL) {\r
935                 lclptr = (struct state *) malloc(sizeof *lclptr);\r
936                 if (lclptr == NULL) {\r
937                         settzname();    /* all we can do */\r
938                         return;\r
939                 }\r
940         }\r
941 #endif /* defined ALL_STATE */\r
942         if (tzload((char *) NULL, lclptr) != 0)\r
943                 gmtload(lclptr);\r
944         settzname();\r
945 }\r
946 \r
947 void\r
948 tzset P((void))\r
949 {\r
950         register const char *   name;\r
951 \r
952         name = getenv("TZ");\r
953         if (name == NULL) {\r
954                 tzsetwall();\r
955                 return;\r
956         }\r
957 \r
958         if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)\r
959                 return;\r
960         lcl_is_set = strlen(name) < sizeof lcl_TZname;\r
961         if (lcl_is_set)\r
962                 (void) strcpy(lcl_TZname, name);\r
963 \r
964 #ifdef ALL_STATE\r
965         if (lclptr == NULL) {\r
966                 lclptr = (struct state *) malloc(sizeof *lclptr);\r
967                 if (lclptr == NULL) {\r
968                         settzname();    /* all we can do */\r
969                         return;\r
970                 }\r
971         }\r
972 #endif /* defined ALL_STATE */\r
973         if (*name == '\0') {\r
974                 /*\r
975                 ** User wants it fast rather than right.\r
976                 */\r
977                 lclptr->leapcnt = 0;            /* so, we're off a little */\r
978                 lclptr->timecnt = 0;\r
979                 lclptr->typecnt = 0;\r
980                 lclptr->ttis[0].tt_isdst = 0;\r
981                 lclptr->ttis[0].tt_gmtoff = 0;\r
982                 lclptr->ttis[0].tt_abbrind = 0;\r
983                 (void) strcpy(lclptr->chars, gmt);\r
984         } else if (tzload(name, lclptr) != 0)\r
985                 if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)\r
986                         (void) gmtload(lclptr);\r
987         settzname();\r
988 }\r
989 \r
990 /*\r
991 ** The easy way to behave "as if no library function calls" localtime\r
992 ** is to not call it--so we drop its guts into "localsub", which can be\r
993 ** freely called.  (And no, the PANS doesn't require the above behavior--\r
994 ** but it *is* desirable.)\r
995 **\r
996 ** The unused offset argument is for the benefit of mktime variants.\r
997 */\r
998 \r
999 /*ARGSUSED*/\r
1000 static void\r
1001 localsub(timep, offset, tmp)\r
1002 const time_t * const    timep;\r
1003 const long              offset;\r
1004 struct tm * const       tmp;\r
1005 {\r
1006         register struct state *         sp;\r
1007         register const struct ttinfo *  ttisp;\r
1008         register int                    i;\r
1009         const time_t                    t = *timep;\r
1010 \r
1011         sp = lclptr;\r
1012 #ifdef ALL_STATE\r
1013         if (sp == NULL) {\r
1014                 gmtsub(timep, offset, tmp);\r
1015                 return;\r
1016         }\r
1017 #endif /* defined ALL_STATE */\r
1018         if (sp->timecnt == 0 || t < sp->ats[0]) {\r
1019                 i = 0;\r
1020                 while (sp->ttis[i].tt_isdst)\r
1021                         if (++i >= sp->typecnt) {\r
1022                                 i = 0;\r
1023                                 break;\r
1024                         }\r
1025         } else {\r
1026                 for (i = 1; i < sp->timecnt; ++i)\r
1027                         if (t < sp->ats[i])\r
1028                                 break;\r
1029                 i = sp->types[i - 1];\r
1030         }\r
1031         ttisp = &sp->ttis[i];\r
1032         /*\r
1033         ** To get (wrong) behavior that's compatible with System V Release 2.0\r
1034         ** you'd replace the statement below with\r
1035         **      t += ttisp->tt_gmtoff;\r
1036         **      timesub(&t, 0L, sp, tmp);\r
1037         */\r
1038         timesub(&t, ttisp->tt_gmtoff, sp, tmp);\r
1039         tmp->tm_isdst = ttisp->tt_isdst;\r
1040         tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];\r
1041 #ifdef TM_ZONE\r
1042         tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];\r
1043 #endif /* defined TM_ZONE */\r
1044 }\r
1045 \r
1046 struct tm *\r
1047 localtime(timep)\r
1048 const time_t * const    timep;\r
1049 {\r
1050         tzset();\r
1051         localsub(timep, 0L, &tm);\r
1052         return &tm;\r
1053 }\r
1054 \r
1055 /*\r
1056 ** Re-entrant version of localtime.\r
1057 */\r
1058 \r
1059 struct tm *\r
1060 localtime_r(timep, tm)\r
1061 const time_t * const    timep;\r
1062 struct tm *             tm;\r
1063 {\r
1064         localsub(timep, 0L, tm);\r
1065         return tm;\r
1066 }\r
1067 \r
1068 /*\r
1069 ** gmtsub is to gmtime as localsub is to localtime.\r
1070 */\r
1071 \r
1072 static void\r
1073 gmtsub(timep, offset, tmp)\r
1074 const time_t * const    timep;\r
1075 const long              offset;\r
1076 struct tm * const       tmp;\r
1077 {\r
1078         if (!gmt_is_set) {\r
1079                 gmt_is_set = TRUE;\r
1080 #ifdef ALL_STATE\r
1081                 gmtptr = (struct state *) malloc(sizeof *gmtptr);\r
1082                 if (gmtptr != NULL)\r
1083 #endif /* defined ALL_STATE */\r
1084                         gmtload(gmtptr);\r
1085         }\r
1086         timesub(timep, offset, gmtptr, tmp);\r
1087 #ifdef TM_ZONE\r
1088         /*\r
1089         ** Could get fancy here and deliver something such as\r
1090         ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,\r
1091         ** but this is no time for a treasure hunt.\r
1092         */\r
1093         if (offset != 0)\r
1094                 tmp->TM_ZONE = wildabbr;\r
1095         else {\r
1096 #ifdef ALL_STATE\r
1097                 if (gmtptr == NULL)\r
1098                         tmp->TM_ZONE = gmt;\r
1099                 else    tmp->TM_ZONE = gmtptr->chars;\r
1100 #endif /* defined ALL_STATE */\r
1101 #ifndef ALL_STATE\r
1102                 tmp->TM_ZONE = gmtptr->chars;\r
1103 #endif /* State Farm */\r
1104         }\r
1105 #endif /* defined TM_ZONE */\r
1106 }\r
1107 \r
1108 struct tm *\r
1109 gmtime(timep)\r
1110 const time_t * const    timep;\r
1111 {\r
1112         gmtsub(timep, 0L, &tm);\r
1113         return &tm;\r
1114 }\r
1115 \r
1116 /*\r
1117 * Re-entrant version of gmtime.\r
1118 */\r
1119 \r
1120 struct tm *\r
1121 gmtime_r(timep, tm)\r
1122 const time_t * const    timep;\r
1123 struct tm *             tm;\r
1124 {\r
1125         gmtsub(timep, 0L, tm);\r
1126         return tm;\r
1127 }\r
1128 \r
1129 #ifdef STD_INSPIRED\r
1130 \r
1131 struct tm *\r
1132 offtime(timep, offset)\r
1133 const time_t * const    timep;\r
1134 const long              offset;\r
1135 {\r
1136         gmtsub(timep, offset, &tm);\r
1137         return &tm;\r
1138 }\r
1139 \r
1140 #endif /* defined STD_INSPIRED */\r
1141 \r
1142 static void\r
1143 timesub(timep, offset, sp, tmp)\r
1144 const time_t * const                    timep;\r
1145 const long                              offset;\r
1146 register const struct state * const     sp;\r
1147 register struct tm * const              tmp;\r
1148 {\r
1149         register const struct lsinfo *  lp;\r
1150         register long                   days;\r
1151         register long                   rem;\r
1152         register int                    y;\r
1153         register int                    yleap;\r
1154         register const int *            ip;\r
1155         register long                   corr;\r
1156         register int                    hit;\r
1157         register int                    i;\r
1158 \r
1159         corr = 0;\r
1160         hit = 0;\r
1161 #ifdef ALL_STATE\r
1162         i = (sp == NULL) ? 0 : sp->leapcnt;\r
1163 #endif /* defined ALL_STATE */\r
1164 #ifndef ALL_STATE\r
1165         i = sp->leapcnt;\r
1166 #endif /* State Farm */\r
1167         while (--i >= 0) {\r
1168                 lp = &sp->lsis[i];\r
1169                 if (*timep >= lp->ls_trans) {\r
1170                         if (*timep == lp->ls_trans) {\r
1171                                 hit = ((i == 0 && lp->ls_corr > 0) ||\r
1172                                         lp->ls_corr > sp->lsis[i - 1].ls_corr);\r
1173                                 if (hit)\r
1174                                         while (i > 0 &&\r
1175                                                 sp->lsis[i].ls_trans ==\r
1176                                                 sp->lsis[i - 1].ls_trans + 1 &&\r
1177                                                 sp->lsis[i].ls_corr ==\r
1178                                                 sp->lsis[i - 1].ls_corr + 1) {\r
1179                                                         ++hit;\r
1180                                                         --i;\r
1181                                         }\r
1182                         }\r
1183                         corr = lp->ls_corr;\r
1184                         break;\r
1185                 }\r
1186         }\r
1187         days = *timep / SECSPERDAY;\r
1188         rem = *timep % SECSPERDAY;\r
1189 #ifdef mc68k\r
1190         if (*timep == 0x80000000) {\r
1191                 /*\r
1192                 ** A 3B1 muffs the division on the most negative number.\r
1193                 */\r
1194                 days = -24855;\r
1195                 rem = -11648;\r
1196         }\r
1197 #endif /* defined mc68k */\r
1198         rem += (offset - corr);\r
1199         while (rem < 0) {\r
1200                 rem += SECSPERDAY;\r
1201                 --days;\r
1202         }\r
1203         while (rem >= SECSPERDAY) {\r
1204                 rem -= SECSPERDAY;\r
1205                 ++days;\r
1206         }\r
1207         tmp->tm_hour = (int) (rem / SECSPERHOUR);\r
1208         rem = rem % SECSPERHOUR;\r
1209         tmp->tm_min = (int) (rem / SECSPERMIN);\r
1210         /*\r
1211         ** A positive leap second requires a special\r
1212         ** representation.  This uses "... ??:59:60" et seq.\r
1213         */\r
1214         tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;\r
1215         tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);\r
1216         if (tmp->tm_wday < 0)\r
1217                 tmp->tm_wday += DAYSPERWEEK;\r
1218         y = EPOCH_YEAR;\r
1219 #define LEAPS_THRU_END_OF(y)    ((y) / 4 - (y) / 100 + (y) / 400)\r
1220         while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {\r
1221                 register int    newy;\r
1222 \r
1223                 newy = y + days / DAYSPERNYEAR;\r
1224                 if (days < 0)\r
1225                         --newy;\r
1226                 days -= (newy - y) * DAYSPERNYEAR +\r
1227                         LEAPS_THRU_END_OF(newy - 1) -\r
1228                         LEAPS_THRU_END_OF(y - 1);\r
1229                 y = newy;\r
1230         }\r
1231         tmp->tm_year = y - TM_YEAR_BASE;\r
1232         tmp->tm_yday = (int) days;\r
1233         ip = mon_lengths[yleap];\r
1234         for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))\r
1235                 days = days - (long) ip[tmp->tm_mon];\r
1236         tmp->tm_mday = (int) (days + 1);\r
1237         tmp->tm_isdst = 0;\r
1238 #ifdef TM_GMTOFF\r
1239         tmp->TM_GMTOFF = offset;\r
1240 #endif /* defined TM_GMTOFF */\r
1241 }\r
1242 \r
1243 char *\r
1244 ctime(timep)\r
1245 const time_t * const    timep;\r
1246 {\r
1247 /*\r
1248 ** Section 4.12.3.2 of X3.159-1989 requires that\r
1249 **      The ctime function converts the calendar time pointed to by timer\r
1250 **      to local time in the form of a string.  It is equivalent to\r
1251 **              asctime(localtime(timer))\r
1252 */\r
1253         return asctime(localtime(timep));\r
1254 }\r
1255 \r
1256 char *\r
1257 ctime_r(timep, buf)\r
1258 const time_t * const    timep;\r
1259 char *                  buf;\r
1260 {\r
1261         struct tm       tm;\r
1262 \r
1263         return asctime_r(localtime_r(timep, &tm), buf);\r
1264 }\r
1265 \r
1266 /*\r
1267 ** Adapted from code provided by Robert Elz, who writes:\r
1268 **      The "best" way to do mktime I think is based on an idea of Bob\r
1269 **      Kridle's (so its said...) from a long time ago.\r
1270 **      [kridle@xinet.com as of 1996-01-16.]\r
1271 **      It does a binary search of the time_t space.  Since time_t's are\r
1272 **      just 32 bits, its a max of 32 iterations (even at 64 bits it\r
1273 **      would still be very reasonable).\r
1274 */\r
1275 \r
1276 #ifndef WRONG\r
1277 #define WRONG   (-1)\r
1278 #endif /* !defined WRONG */\r
1279 \r
1280 /*\r
1281 ** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).\r
1282 */\r
1283 \r
1284 static int\r
1285 increment_overflow(number, delta)\r
1286 int *   number;\r
1287 int     delta;\r
1288 {\r
1289         int     number0;\r
1290 \r
1291         number0 = *number;\r
1292         *number += delta;\r
1293         return (*number < number0) != (delta < 0);\r
1294 }\r
1295 \r
1296 static int\r
1297 normalize_overflow(tensptr, unitsptr, base)\r
1298 int * const     tensptr;\r
1299 int * const     unitsptr;\r
1300 const int       base;\r
1301 {\r
1302         register int    tensdelta;\r
1303 \r
1304         tensdelta = (*unitsptr >= 0) ?\r
1305                 (*unitsptr / base) :\r
1306                 (-1 - (-1 - *unitsptr) / base);\r
1307         *unitsptr -= tensdelta * base;\r
1308         return increment_overflow(tensptr, tensdelta);\r
1309 }\r
1310 \r
1311 static int\r
1312 tmcomp(atmp, btmp)\r
1313 register const struct tm * const atmp;\r
1314 register const struct tm * const btmp;\r
1315 {\r
1316         register int    result;\r
1317 \r
1318         if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&\r
1319                 (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&\r
1320                 (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&\r
1321                 (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&\r
1322                 (result = (atmp->tm_min - btmp->tm_min)) == 0)\r
1323                         result = atmp->tm_sec - btmp->tm_sec;\r
1324         return result;\r
1325 }\r
1326 \r
1327 static time_t\r
1328 time2sub(tmp, funcp, offset, okayp, do_norm_secs)\r
1329 struct tm * const       tmp;\r
1330 void (* const           funcp) P((const time_t*, long, struct tm*));\r
1331 const long              offset;\r
1332 int * const             okayp;\r
1333 const int               do_norm_secs;\r
1334 {\r
1335         register const struct state *   sp;\r
1336         register int                    dir;\r
1337         register int                    bits;\r
1338         register int                    i, j ;\r
1339         register int                    saved_seconds;\r
1340         time_t                          newt;\r
1341         time_t                          t;\r
1342         struct tm                       yourtm, mytm;\r
1343 \r
1344         *okayp = FALSE;\r
1345         yourtm = *tmp;\r
1346         if (do_norm_secs) {\r
1347                 if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,\r
1348                         SECSPERMIN))\r
1349                                 return WRONG;\r
1350         }\r
1351         if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))\r
1352                 return WRONG;\r
1353         if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))\r
1354                 return WRONG;\r
1355         if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))\r
1356                 return WRONG;\r
1357         /*\r
1358         ** Turn yourtm.tm_year into an actual year number for now.\r
1359         ** It is converted back to an offset from TM_YEAR_BASE later.\r
1360         */\r
1361         if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))\r
1362                 return WRONG;\r
1363         while (yourtm.tm_mday <= 0) {\r
1364                 if (increment_overflow(&yourtm.tm_year, -1))\r
1365                         return WRONG;\r
1366                 i = yourtm.tm_year + (1 < yourtm.tm_mon);\r
1367                 yourtm.tm_mday += year_lengths[isleap(i)];\r
1368         }\r
1369         while (yourtm.tm_mday > DAYSPERLYEAR) {\r
1370                 i = yourtm.tm_year + (1 < yourtm.tm_mon);\r
1371                 yourtm.tm_mday -= year_lengths[isleap(i)];\r
1372                 if (increment_overflow(&yourtm.tm_year, 1))\r
1373                         return WRONG;\r
1374         }\r
1375         for ( ; ; ) {\r
1376                 i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];\r
1377                 if (yourtm.tm_mday <= i)\r
1378                         break;\r
1379                 yourtm.tm_mday -= i;\r
1380                 if (++yourtm.tm_mon >= MONSPERYEAR) {\r
1381                         yourtm.tm_mon = 0;\r
1382                         if (increment_overflow(&yourtm.tm_year, 1))\r
1383                                 return WRONG;\r
1384                 }\r
1385         }\r
1386         if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))\r
1387                 return WRONG;\r
1388         if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)\r
1389                 saved_seconds = 0;\r
1390         else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {\r
1391                 /*\r
1392                 ** We can't set tm_sec to 0, because that might push the\r
1393                 ** time below the minimum representable time.\r
1394                 ** Set tm_sec to 59 instead.\r
1395                 ** This assumes that the minimum representable time is\r
1396                 ** not in the same minute that a leap second was deleted from,\r
1397                 ** which is a safer assumption than using 58 would be.\r
1398                 */\r
1399                 if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))\r
1400                         return WRONG;\r
1401                 saved_seconds = yourtm.tm_sec;\r
1402                 yourtm.tm_sec = SECSPERMIN - 1;\r
1403         } else {\r
1404                 saved_seconds = yourtm.tm_sec;\r
1405                 yourtm.tm_sec = 0;\r
1406         }\r
1407         /*\r
1408         ** Divide the search space in half\r
1409         ** (this works whether time_t is signed or unsigned).\r
1410         */\r
1411         bits = TYPE_BIT(time_t) - 1;\r
1412         /*\r
1413         ** If time_t is signed, then 0 is just above the median,\r
1414         ** assuming two's complement arithmetic.\r
1415         ** If time_t is unsigned, then (1 << bits) is just above the median.\r
1416         */\r
1417         t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);\r
1418         for ( ; ; ) {\r
1419                 (*funcp)(&t, offset, &mytm);\r
1420                 dir = tmcomp(&mytm, &yourtm);\r
1421                 if (dir != 0) {\r
1422                         if (bits-- < 0)\r
1423                                 return WRONG;\r
1424                         if (bits < 0)\r
1425                                 --t; /* may be needed if new t is minimal */\r
1426                         else if (dir > 0)\r
1427                                 t -= ((time_t) 1) << bits;\r
1428                         else    t += ((time_t) 1) << bits;\r
1429                         continue;\r
1430                 }\r
1431                 if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)\r
1432                         break;\r
1433                 /*\r
1434                 ** Right time, wrong type.\r
1435                 ** Hunt for right time, right type.\r
1436                 ** It's okay to guess wrong since the guess\r
1437                 ** gets checked.\r
1438                 */\r
1439                 /*\r
1440                 ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.\r
1441                 */\r
1442                 sp = (const struct state *)\r
1443                         (((void *) funcp == (void *) localsub) ?\r
1444                         lclptr : gmtptr);\r
1445 #ifdef ALL_STATE\r
1446                 if (sp == NULL)\r
1447                         return WRONG;\r
1448 #endif /* defined ALL_STATE */\r
1449                 for (i = sp->typecnt - 1; i >= 0; --i) {\r
1450                         if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)\r
1451                                 continue;\r
1452                         for (j = sp->typecnt - 1; j >= 0; --j) {\r
1453                                 if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)\r
1454                                         continue;\r
1455                                 newt = t + sp->ttis[j].tt_gmtoff -\r
1456                                         sp->ttis[i].tt_gmtoff;\r
1457                                 (*funcp)(&newt, offset, &mytm);\r
1458                                 if (tmcomp(&mytm, &yourtm) != 0)\r
1459                                         continue;\r
1460                                 if (mytm.tm_isdst != yourtm.tm_isdst)\r
1461                                         continue;\r
1462                                 /*\r
1463                                 ** We have a match.\r
1464                                 */\r
1465                                 t = newt;\r
1466                                 goto label;\r
1467                         }\r
1468                 }\r
1469                 return WRONG;\r
1470         }\r
1471 label:\r
1472         newt = t + saved_seconds;\r
1473         if ((newt < t) != (saved_seconds < 0))\r
1474                 return WRONG;\r
1475         t = newt;\r
1476         (*funcp)(&t, offset, tmp);\r
1477         *okayp = TRUE;\r
1478         return t;\r
1479 }\r
1480 \r
1481 static time_t\r
1482 time2(tmp, funcp, offset, okayp)\r
1483 struct tm * const       tmp;\r
1484 void (* const           funcp) P((const time_t*, long, struct tm*));\r
1485 const long              offset;\r
1486 int * const             okayp;\r
1487 {\r
1488         time_t  t;\r
1489 \r
1490         /*\r
1491         ** First try without normalization of seconds\r
1492         ** (in case tm_sec contains a value associated with a leap second).\r
1493         ** If that fails, try with normalization of seconds.\r
1494         */\r
1495         t = time2sub(tmp, funcp, offset, okayp, FALSE);\r
1496         return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);\r
1497 }\r
1498 \r
1499 static time_t\r
1500 time1(tmp, funcp, offset)\r
1501 struct tm * const       tmp;\r
1502 void (* const           funcp) P((const time_t *, long, struct tm *));\r
1503 const long              offset;\r
1504 {\r
1505         register time_t                 t;\r
1506         register const struct state *   sp;\r
1507         register int                    samei, otheri;\r
1508         register int                    sameind, otherind;\r
1509         register int                    i;\r
1510         register int                    nseen;\r
1511         int                             seen[TZ_MAX_TYPES];\r
1512         int                             types[TZ_MAX_TYPES];\r
1513         int                             okay;\r
1514 \r
1515         if (tmp->tm_isdst > 1)\r
1516                 tmp->tm_isdst = 1;\r
1517         t = time2(tmp, funcp, offset, &okay);\r
1518 #ifdef PCTS\r
1519         /*\r
1520         ** PCTS code courtesy Grant Sullivan (grant@osf.org).\r
1521         */\r
1522         if (okay)\r
1523                 return t;\r
1524         if (tmp->tm_isdst < 0)\r
1525                 tmp->tm_isdst = 0;      /* reset to std and try again */\r
1526 #endif /* defined PCTS */\r
1527 #ifndef PCTS\r
1528         if (okay || tmp->tm_isdst < 0)\r
1529                 return t;\r
1530 #endif /* !defined PCTS */\r
1531         /*\r
1532         ** We're supposed to assume that somebody took a time of one type\r
1533         ** and did some math on it that yielded a "struct tm" that's bad.\r
1534         ** We try to divine the type they started from and adjust to the\r
1535         ** type they need.\r
1536         */\r
1537         /*\r
1538         ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.\r
1539         */\r
1540         sp = (const struct state *) (((void *) funcp == (void *) localsub) ?\r
1541                 lclptr : gmtptr);\r
1542 #ifdef ALL_STATE\r
1543         if (sp == NULL)\r
1544                 return WRONG;\r
1545 #endif /* defined ALL_STATE */\r
1546         for (i = 0; i < sp->typecnt; ++i)\r
1547                 seen[i] = FALSE;\r
1548         nseen = 0;\r
1549         for (i = sp->timecnt - 1; i >= 0; --i)\r
1550                 if (!seen[sp->types[i]]) {\r
1551                         seen[sp->types[i]] = TRUE;\r
1552                         types[nseen++] = sp->types[i];\r
1553                 }\r
1554         for (sameind = 0; sameind < nseen; ++sameind) {\r
1555                 samei = types[sameind];\r
1556                 if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)\r
1557                         continue;\r
1558                 for (otherind = 0; otherind < nseen; ++otherind) {\r
1559                         otheri = types[otherind];\r
1560                         if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)\r
1561                                 continue;\r
1562                         tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -\r
1563                                         sp->ttis[samei].tt_gmtoff;\r
1564                         tmp->tm_isdst = !tmp->tm_isdst;\r
1565                         t = time2(tmp, funcp, offset, &okay);\r
1566                         if (okay)\r
1567                                 return t;\r
1568                         tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -\r
1569                                         sp->ttis[samei].tt_gmtoff;\r
1570                         tmp->tm_isdst = !tmp->tm_isdst;\r
1571                 }\r
1572         }\r
1573         return WRONG;\r
1574 }\r
1575 \r
1576 time_t\r
1577 mktime(tmp)\r
1578 struct tm * const       tmp;\r
1579 {\r
1580         tzset();\r
1581         return time1(tmp, localsub, 0L);\r
1582 }\r
1583 \r
1584 #ifdef STD_INSPIRED\r
1585 \r
1586 time_t\r
1587 timelocal(tmp)\r
1588 struct tm * const       tmp;\r
1589 {\r
1590         tmp->tm_isdst = -1;     /* in case it wasn't initialized */\r
1591         return mktime(tmp);\r
1592 }\r
1593 \r
1594 time_t\r
1595 timegm(tmp)\r
1596 struct tm * const       tmp;\r
1597 {\r
1598         tmp->tm_isdst = 0;\r
1599         return time1(tmp, gmtsub, 0L);\r
1600 }\r
1601 \r
1602 time_t\r
1603 timeoff(tmp, offset)\r
1604 struct tm * const       tmp;\r
1605 const long              offset;\r
1606 {\r
1607         tmp->tm_isdst = 0;\r
1608         return time1(tmp, gmtsub, offset);\r
1609 }\r
1610 \r
1611 #endif /* defined STD_INSPIRED */\r
1612 \r
1613 #ifdef CMUCS\r
1614 \r
1615 /*\r
1616 ** The following is supplied for compatibility with\r
1617 ** previous versions of the CMUCS runtime library.\r
1618 */\r
1619 \r
1620 long\r
1621 gtime(tmp)\r
1622 struct tm * const       tmp;\r
1623 {\r
1624         const time_t    t = mktime(tmp);\r
1625 \r
1626         if (t == WRONG)\r
1627                 return -1;\r
1628         return t;\r
1629 }\r
1630 \r
1631 #endif /* defined CMUCS */\r
1632 \r
1633 /*\r
1634 ** XXX--is the below the right way to conditionalize??\r
1635 */\r
1636 \r
1637 #ifdef STD_INSPIRED\r
1638 \r
1639 /*\r
1640 ** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599\r
1641 ** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which\r
1642 ** is not the case if we are accounting for leap seconds.\r
1643 ** So, we provide the following conversion routines for use\r
1644 ** when exchanging timestamps with POSIX conforming systems.\r
1645 */\r
1646 \r
1647 static long\r
1648 leapcorr(timep)\r
1649 time_t *        timep;\r
1650 {\r
1651         register struct state *         sp;\r
1652         register struct lsinfo *        lp;\r
1653         register int                    i;\r
1654 \r
1655         sp = lclptr;\r
1656         i = sp->leapcnt;\r
1657         while (--i >= 0) {\r
1658                 lp = &sp->lsis[i];\r
1659                 if (*timep >= lp->ls_trans)\r
1660                         return lp->ls_corr;\r
1661         }\r
1662         return 0;\r
1663 }\r
1664 \r
1665 time_t\r
1666 time2posix(t)\r
1667 time_t  t;\r
1668 {\r
1669         tzset();\r
1670         return t - leapcorr(&t);\r
1671 }\r
1672 \r
1673 time_t\r
1674 posix2time(t)\r
1675 time_t  t;\r
1676 {\r
1677         time_t  x;\r
1678         time_t  y;\r
1679 \r
1680         tzset();\r
1681         /*\r
1682         ** For a positive leap second hit, the result\r
1683         ** is not unique.  For a negative leap second\r
1684         ** hit, the corresponding time doesn't exist,\r
1685         ** so we return an adjacent second.\r
1686         */\r
1687         x = t + leapcorr(&t);\r
1688         y = x - leapcorr(&x);\r
1689         if (y < t) {\r
1690                 do {\r
1691                         x++;\r
1692                         y = x - leapcorr(&x);\r
1693                 } while (y < t);\r
1694                 if (t != y)\r
1695                         return x - 1;\r
1696         } else if (y > t) {\r
1697                 do {\r
1698                         --x;\r
1699                         y = x - leapcorr(&x);\r
1700                 } while (y > t);\r
1701                 if (t != y)\r
1702                         return x + 1;\r
1703         }\r
1704         return x;\r
1705 }\r
1706 \r
1707 #endif /* defined STD_INSPIRED */\r