]> git.saurik.com Git - apple/libc.git/blob - gen/ctime.c
d8b06566bf0b447fa263ff1d701c0a119ef1e5ee
[apple/libc.git] / gen / ctime.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1987, 1989, 1993
24 * The Regents of the University of California. All rights reserved.
25 *
26 * This code is derived from software contributed to Berkeley by
27 * Arthur David Olson of the National Cancer Institute.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58
59 /*
60 ** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
61 ** POSIX-style TZ environment variable handling from Guy Harris
62 ** (guy@auspex.com).
63 */
64
65 /*LINTLIBRARY*/
66
67 #include <sys/param.h>
68 #include <fcntl.h>
69 #include <time.h>
70 #include <tzfile.h>
71 #include <string.h>
72 #include <ctype.h>
73 #include <stdio.h>
74 #include <unistd.h>
75
76 #ifdef __STDC__
77 #include <stdlib.h>
78
79 #define P(s) s
80 #define alloc_size_t size_t
81 #define qsort_size_t size_t
82 #define fread_size_t size_t
83 #define fwrite_size_t size_t
84
85 #else /* !defined __STDC__ */
86
87 #define P(s) ()
88
89 typedef char * genericptr_t;
90 typedef unsigned alloc_size_t;
91 typedef int qsort_size_t;
92 typedef int fread_size_t;
93 typedef int fwrite_size_t;
94
95 extern char * calloc();
96 extern char * malloc();
97 extern char * realloc();
98 extern char * getenv();
99
100 #endif /* !defined __STDC__ */
101
102 extern time_t time();
103
104 #define ACCESS_MODE O_RDONLY
105 #define OPEN_MODE O_RDONLY
106
107 #ifndef WILDABBR
108 /*
109 ** Someone might make incorrect use of a time zone abbreviation:
110 ** 1. They might reference tzname[0] before calling tzset (explicitly
111 ** or implicitly).
112 ** 2. They might reference tzname[1] before calling tzset (explicitly
113 ** or implicitly).
114 ** 3. They might reference tzname[1] after setting to a time zone
115 ** in which Daylight Saving Time is never observed.
116 ** 4. They might reference tzname[0] after setting to a time zone
117 ** in which Standard Time is never observed.
118 ** 5. They might reference tm.TM_ZONE after calling offtime.
119 ** What's best to do in the above cases is open to debate;
120 ** for now, we just set things up so that in any of the five cases
121 ** WILDABBR is used. Another possibility: initialize tzname[0] to the
122 ** string "tzname[0] used before set", and similarly for the other cases.
123 ** And another: initialize tzname[0] to "ERA", with an explanation in the
124 ** manual page of what this "time zone abbreviation" means (doing this so
125 ** that tzname[0] has the "normal" length of three characters).
126 */
127 #define WILDABBR " "
128 #endif /* !defined WILDABBR */
129
130 #ifndef TRUE
131 #define TRUE 1
132 #define FALSE 0
133 #endif /* !defined TRUE */
134
135 static const char GMT[] = "GMT";
136
137 struct ttinfo { /* time type information */
138 long tt_gmtoff; /* GMT offset in seconds */
139 int tt_isdst; /* used to set tm_isdst */
140 int tt_abbrind; /* abbreviation list index */
141 int tt_ttisstd; /* TRUE if transition is std time */
142 };
143
144 struct lsinfo { /* leap second information */
145 time_t ls_trans; /* transition time */
146 long ls_corr; /* correction to apply */
147 };
148
149 struct state {
150 int leapcnt;
151 int timecnt;
152 int typecnt;
153 int charcnt;
154 time_t ats[TZ_MAX_TIMES];
155 unsigned char types[TZ_MAX_TIMES];
156 struct ttinfo ttis[TZ_MAX_TYPES];
157 char chars[(TZ_MAX_CHARS + 1 > sizeof GMT) ?
158 TZ_MAX_CHARS + 1 : sizeof GMT];
159 struct lsinfo lsis[TZ_MAX_LEAPS];
160 };
161
162 struct rule {
163 int r_type; /* type of rule--see below */
164 int r_day; /* day number of rule */
165 int r_week; /* week number of rule */
166 int r_mon; /* month number of rule */
167 long r_time; /* transition time of rule */
168 };
169
170 #define JULIAN_DAY 0 /* Jn - Julian day */
171 #define DAY_OF_YEAR 1 /* n - day of year */
172 #define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
173
174 /*
175 ** Prototypes for static functions.
176 */
177
178 static long detzcode P((const char * codep));
179 static const char * getzname P((const char * strp));
180 static const char * getnum P((const char * strp, int * nump, int min,
181 int max));
182 static const char * getsecs P((const char * strp, long * secsp));
183 static const char * getoffset P((const char * strp, long * offsetp));
184 static const char * getrule P((const char * strp, struct rule * rulep));
185 static void gmtload P((struct state * sp));
186 static void gmtsub P((const time_t * timep, long offset,
187 struct tm * tmp));
188 static void localsub P((const time_t * timep, long offset,
189 struct tm * tmp));
190 static void normalize P((int * tensptr, int * unitsptr, int base));
191 static void settzname P((void));
192 static time_t time1 P((struct tm * tmp, void (* funcp)(),
193 long offset));
194 static time_t time2 P((struct tm *tmp, void (* funcp)(),
195 long offset, int * okayp));
196 static void timesub P((const time_t * timep, long offset,
197 const struct state * sp, struct tm * tmp));
198 static int tmcomp P((const struct tm * atmp,
199 const struct tm * btmp));
200 static time_t transtime P((time_t janfirst, int year,
201 const struct rule * rulep, long offset));
202 static int tzload P((const char * name, struct state * sp));
203 static int tzparse P((const char * name, struct state * sp,
204 int lastditch));
205
206 #ifdef ALL_STATE
207 static struct state * lclptr;
208 static struct state * gmtptr;
209 #endif /* defined ALL_STATE */
210
211 #ifndef ALL_STATE
212 static struct state lclmem;
213 static struct state gmtmem;
214 #define lclptr (&lclmem)
215 #define gmtptr (&gmtmem)
216 #endif /* State Farm */
217
218 static int lcl_is_set;
219 static int gmt_is_set;
220
221 char * tzname[2] = {
222 WILDABBR,
223 WILDABBR
224 };
225
226 #ifdef USG_COMPAT
227 time_t timezone = 0;
228 int daylight = 0;
229 #endif /* defined USG_COMPAT */
230
231 #ifdef ALTZONE
232 time_t altzone = 0;
233 #endif /* defined ALTZONE */
234
235 static long
236 detzcode(codep)
237 const char * const codep;
238 {
239 register long result;
240 register int i;
241
242 result = 0;
243 for (i = 0; i < 4; ++i)
244 result = (result << 8) | (codep[i] & 0xff);
245 return result;
246 }
247
248 static void
249 settzname()
250 {
251 register const struct state * const sp = lclptr;
252 register int i;
253
254 tzname[0] = WILDABBR;
255 tzname[1] = WILDABBR;
256 #ifdef USG_COMPAT
257 daylight = 0;
258 timezone = 0;
259 #endif /* defined USG_COMPAT */
260 #ifdef ALTZONE
261 altzone = 0;
262 #endif /* defined ALTZONE */
263 #ifdef ALL_STATE
264 if (sp == NULL) {
265 tzname[0] = tzname[1] = GMT;
266 return;
267 }
268 #endif /* defined ALL_STATE */
269 for (i = 0; i < sp->typecnt; ++i) {
270 register const struct ttinfo * const ttisp = &sp->ttis[i];
271
272 tzname[ttisp->tt_isdst] =
273 (char *) &sp->chars[ttisp->tt_abbrind];
274 #ifdef USG_COMPAT
275 if (ttisp->tt_isdst)
276 daylight = 1;
277 if (i == 0 || !ttisp->tt_isdst)
278 timezone = -(ttisp->tt_gmtoff);
279 #endif /* defined USG_COMPAT */
280 #ifdef ALTZONE
281 if (i == 0 || ttisp->tt_isdst)
282 altzone = -(ttisp->tt_gmtoff);
283 #endif /* defined ALTZONE */
284 }
285 /*
286 ** And to get the latest zone names into tzname. . .
287 */
288 for (i = 0; i < sp->timecnt; ++i) {
289 register const struct ttinfo * const ttisp =
290 &sp->ttis[sp->types[i]];
291
292 tzname[ttisp->tt_isdst] =
293 (char *) &sp->chars[ttisp->tt_abbrind];
294 }
295 }
296
297 static int
298 tzload(name, sp)
299 register const char * name;
300 register struct state * const sp;
301 {
302 register const char * p;
303 register int i;
304 register int fid;
305
306 if (name == NULL && (name = TZDEFAULT) == NULL)
307 return -1;
308 {
309 char fullname[FILENAME_MAX + 1];
310
311 if (name[0] == ':')
312 ++name;
313 if (name[0] != '/') {
314 if ((p = TZDIR) == NULL)
315 return -1;
316 if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
317 return -1;
318 (void) strcpy(fullname, p);
319 (void) strcat(fullname, "/");
320 (void) strcat(fullname, name);
321 name = fullname;
322 }
323 if ((fid = open(name, OPEN_MODE)) == -1)
324 return -1;
325 }
326 {
327 register const struct tzhead * tzhp;
328 char buf[sizeof *sp + sizeof *tzhp];
329 int ttisstdcnt;
330
331 i = read(fid, buf, sizeof buf);
332 if (close(fid) != 0 || i < sizeof *tzhp)
333 return -1;
334 tzhp = (struct tzhead *) buf;
335 ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt);
336 sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt);
337 sp->timecnt = (int) detzcode(tzhp->tzh_timecnt);
338 sp->typecnt = (int) detzcode(tzhp->tzh_typecnt);
339 sp->charcnt = (int) detzcode(tzhp->tzh_charcnt);
340 if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
341 sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
342 sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
343 sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
344 (ttisstdcnt != sp->typecnt && ttisstdcnt != 0))
345 return -1;
346 if (i < sizeof *tzhp +
347 sp->timecnt * (4 + sizeof (char)) +
348 sp->typecnt * (4 + 2 * sizeof (char)) +
349 sp->charcnt * sizeof (char) +
350 sp->leapcnt * 2 * 4 +
351 ttisstdcnt * sizeof (char))
352 return -1;
353 p = buf + sizeof *tzhp;
354 for (i = 0; i < sp->timecnt; ++i) {
355 sp->ats[i] = detzcode(p);
356 p += 4;
357 }
358 for (i = 0; i < sp->timecnt; ++i) {
359 sp->types[i] = (unsigned char) *p++;
360 if (sp->types[i] >= sp->typecnt)
361 return -1;
362 }
363 for (i = 0; i < sp->typecnt; ++i) {
364 register struct ttinfo * ttisp;
365
366 ttisp = &sp->ttis[i];
367 ttisp->tt_gmtoff = detzcode(p);
368 p += 4;
369 ttisp->tt_isdst = (unsigned char) *p++;
370 if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
371 return -1;
372 ttisp->tt_abbrind = (unsigned char) *p++;
373 if (ttisp->tt_abbrind < 0 ||
374 ttisp->tt_abbrind > sp->charcnt)
375 return -1;
376 }
377 for (i = 0; i < sp->charcnt; ++i)
378 sp->chars[i] = *p++;
379 sp->chars[i] = '\0'; /* ensure '\0' at end */
380 for (i = 0; i < sp->leapcnt; ++i) {
381 register struct lsinfo * lsisp;
382
383 lsisp = &sp->lsis[i];
384 lsisp->ls_trans = detzcode(p);
385 p += 4;
386 lsisp->ls_corr = detzcode(p);
387 p += 4;
388 }
389 for (i = 0; i < sp->typecnt; ++i) {
390 register struct ttinfo * ttisp;
391
392 ttisp = &sp->ttis[i];
393 if (ttisstdcnt == 0)
394 ttisp->tt_ttisstd = FALSE;
395 else {
396 ttisp->tt_ttisstd = *p++;
397 if (ttisp->tt_ttisstd != TRUE &&
398 ttisp->tt_ttisstd != FALSE)
399 return -1;
400 }
401 }
402 }
403 return 0;
404 }
405
406 static const int mon_lengths[2][MONSPERYEAR] = {
407 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
408 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
409 };
410
411 static const int year_lengths[2] = {
412 DAYSPERNYEAR, DAYSPERLYEAR
413 };
414
415 /*
416 ** Given a pointer into a time zone string, scan until a character that is not
417 ** a valid character in a zone name is found. Return a pointer to that
418 ** character.
419 */
420
421 static const char *
422 getzname(strp)
423 register const char * strp;
424 {
425 register char c;
426
427 while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
428 c != '+')
429 ++strp;
430 return strp;
431 }
432
433 /*
434 ** Given a pointer into a time zone string, extract a number from that string.
435 ** Check that the number is within a specified range; if it is not, return
436 ** NULL.
437 ** Otherwise, return a pointer to the first character not part of the number.
438 */
439
440 static const char *
441 getnum(strp, nump, min, max)
442 register const char * strp;
443 int * const nump;
444 const int min;
445 const int max;
446 {
447 register char c;
448 register int num;
449
450 if (strp == NULL || !isdigit(*strp))
451 return NULL;
452 num = 0;
453 while ((c = *strp) != '\0' && isdigit(c)) {
454 num = num * 10 + (c - '0');
455 if (num > max)
456 return NULL; /* illegal value */
457 ++strp;
458 }
459 if (num < min)
460 return NULL; /* illegal value */
461 *nump = num;
462 return strp;
463 }
464
465 /*
466 ** Given a pointer into a time zone string, extract a number of seconds,
467 ** in hh[:mm[:ss]] form, from the string.
468 ** If any error occurs, return NULL.
469 ** Otherwise, return a pointer to the first character not part of the number
470 ** of seconds.
471 */
472
473 static const char *
474 getsecs(strp, secsp)
475 register const char * strp;
476 long * const secsp;
477 {
478 int num;
479
480 strp = getnum(strp, &num, 0, HOURSPERDAY);
481 if (strp == NULL)
482 return NULL;
483 *secsp = num * SECSPERHOUR;
484 if (*strp == ':') {
485 ++strp;
486 strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
487 if (strp == NULL)
488 return NULL;
489 *secsp += num * SECSPERMIN;
490 if (*strp == ':') {
491 ++strp;
492 strp = getnum(strp, &num, 0, SECSPERMIN - 1);
493 if (strp == NULL)
494 return NULL;
495 *secsp += num;
496 }
497 }
498 return strp;
499 }
500
501 /*
502 ** Given a pointer into a time zone string, extract an offset, in
503 ** [+-]hh[:mm[:ss]] form, from the string.
504 ** If any error occurs, return NULL.
505 ** Otherwise, return a pointer to the first character not part of the time.
506 */
507
508 static const char *
509 getoffset(strp, offsetp)
510 register const char * strp;
511 long * const offsetp;
512 {
513 register int neg;
514
515 if (*strp == '-') {
516 neg = 1;
517 ++strp;
518 } else if (isdigit(*strp) || *strp++ == '+')
519 neg = 0;
520 else return NULL; /* illegal offset */
521 strp = getsecs(strp, offsetp);
522 if (strp == NULL)
523 return NULL; /* illegal time */
524 if (neg)
525 *offsetp = -*offsetp;
526 return strp;
527 }
528
529 /*
530 ** Given a pointer into a time zone string, extract a rule in the form
531 ** date[/time]. See POSIX section 8 for the format of "date" and "time".
532 ** If a valid rule is not found, return NULL.
533 ** Otherwise, return a pointer to the first character not part of the rule.
534 */
535
536 static const char *
537 getrule(strp, rulep)
538 const char * strp;
539 register struct rule * const rulep;
540 {
541 if (*strp == 'J') {
542 /*
543 ** Julian day.
544 */
545 rulep->r_type = JULIAN_DAY;
546 ++strp;
547 strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
548 } else if (*strp == 'M') {
549 /*
550 ** Month, week, day.
551 */
552 rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
553 ++strp;
554 strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
555 if (strp == NULL)
556 return NULL;
557 if (*strp++ != '.')
558 return NULL;
559 strp = getnum(strp, &rulep->r_week, 1, 5);
560 if (strp == NULL)
561 return NULL;
562 if (*strp++ != '.')
563 return NULL;
564 strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
565 } else if (isdigit(*strp)) {
566 /*
567 ** Day of year.
568 */
569 rulep->r_type = DAY_OF_YEAR;
570 strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
571 } else return NULL; /* invalid format */
572 if (strp == NULL)
573 return NULL;
574 if (*strp == '/') {
575 /*
576 ** Time specified.
577 */
578 ++strp;
579 strp = getsecs(strp, &rulep->r_time);
580 } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
581 return strp;
582 }
583
584 /*
585 ** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
586 ** year, a rule, and the offset from GMT at the time that rule takes effect,
587 ** calculate the Epoch-relative time that rule takes effect.
588 */
589
590 static time_t
591 transtime(janfirst, year, rulep, offset)
592 const time_t janfirst;
593 const int year;
594 register const struct rule * const rulep;
595 const long offset;
596 {
597 register int leapyear;
598 register time_t value;
599 register int i;
600 int d, m1, yy0, yy1, yy2, dow;
601
602 leapyear = isleap(year);
603 switch (rulep->r_type) {
604
605 case JULIAN_DAY:
606 /*
607 ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
608 ** years.
609 ** In non-leap years, or if the day number is 59 or less, just
610 ** add SECSPERDAY times the day number-1 to the time of
611 ** January 1, midnight, to get the day.
612 */
613 value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
614 if (leapyear && rulep->r_day >= 60)
615 value += SECSPERDAY;
616 break;
617
618 case DAY_OF_YEAR:
619 /*
620 ** n - day of year.
621 ** Just add SECSPERDAY times the day number to the time of
622 ** January 1, midnight, to get the day.
623 */
624 value = janfirst + rulep->r_day * SECSPERDAY;
625 break;
626
627 case MONTH_NTH_DAY_OF_WEEK:
628 /*
629 ** Mm.n.d - nth "dth day" of month m.
630 */
631 value = janfirst;
632 for (i = 0; i < rulep->r_mon - 1; ++i)
633 value += mon_lengths[leapyear][i] * SECSPERDAY;
634
635 /*
636 ** Use Zeller's Congruence to get day-of-week of first day of
637 ** month.
638 */
639 m1 = (rulep->r_mon + 9) % 12 + 1;
640 yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
641 yy1 = yy0 / 100;
642 yy2 = yy0 % 100;
643 dow = ((26 * m1 - 2) / 10 +
644 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
645 if (dow < 0)
646 dow += DAYSPERWEEK;
647
648 /*
649 ** "dow" is the day-of-week of the first day of the month. Get
650 ** the day-of-month (zero-origin) of the first "dow" day of the
651 ** month.
652 */
653 d = rulep->r_day - dow;
654 if (d < 0)
655 d += DAYSPERWEEK;
656 for (i = 1; i < rulep->r_week; ++i) {
657 if (d + DAYSPERWEEK >=
658 mon_lengths[leapyear][rulep->r_mon - 1])
659 break;
660 d += DAYSPERWEEK;
661 }
662
663 /*
664 ** "d" is the day-of-month (zero-origin) of the day we want.
665 */
666 value += d * SECSPERDAY;
667 break;
668 }
669
670 /*
671 ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
672 ** question. To get the Epoch-relative time of the specified local
673 ** time on that day, add the transition time and the current offset
674 ** from GMT.
675 */
676 return value + rulep->r_time + offset;
677 }
678
679 /*
680 ** Given a POSIX section 8-style TZ string, fill in the rule tables as
681 ** appropriate.
682 */
683
684 static int
685 tzparse(name, sp, lastditch)
686 const char * name;
687 register struct state * const sp;
688 const int lastditch;
689 {
690 const char * stdname;
691 const char * dstname;
692 int stdlen;
693 int dstlen;
694 long stdoffset;
695 long dstoffset;
696 register time_t * atp;
697 register unsigned char * typep;
698 register char * cp;
699 register int load_result;
700
701 stdname = name;
702 if (lastditch) {
703 stdlen = strlen(name); /* length of standard zone name */
704 name += stdlen;
705 if (stdlen >= sizeof sp->chars)
706 stdlen = (sizeof sp->chars) - 1;
707 } else {
708 name = getzname(name);
709 stdlen = name - stdname;
710 if (stdlen < 3)
711 return -1;
712 }
713 if (*name == '\0')
714 return -1;
715 else {
716 name = getoffset(name, &stdoffset);
717 if (name == NULL)
718 return -1;
719 }
720 load_result = tzload(TZDEFRULES, sp);
721 if (load_result != 0)
722 sp->leapcnt = 0; /* so, we're off a little */
723 if (*name != '\0') {
724 dstname = name;
725 name = getzname(name);
726 dstlen = name - dstname; /* length of DST zone name */
727 if (dstlen < 3)
728 return -1;
729 if (*name != '\0' && *name != ',' && *name != ';') {
730 name = getoffset(name, &dstoffset);
731 if (name == NULL)
732 return -1;
733 } else dstoffset = stdoffset - SECSPERHOUR;
734 if (*name == ',' || *name == ';') {
735 struct rule start;
736 struct rule end;
737 register int year;
738 register time_t janfirst;
739 time_t starttime;
740 time_t endtime;
741
742 ++name;
743 if ((name = getrule(name, &start)) == NULL)
744 return -1;
745 if (*name++ != ',')
746 return -1;
747 if ((name = getrule(name, &end)) == NULL)
748 return -1;
749 if (*name != '\0')
750 return -1;
751 sp->typecnt = 2; /* standard time and DST */
752 /*
753 ** Two transitions per year, from EPOCH_YEAR to 2037.
754 */
755 sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
756 if (sp->timecnt > TZ_MAX_TIMES)
757 return -1;
758 sp->ttis[0].tt_gmtoff = -dstoffset;
759 sp->ttis[0].tt_isdst = 1;
760 sp->ttis[0].tt_abbrind = stdlen + 1;
761 sp->ttis[1].tt_gmtoff = -stdoffset;
762 sp->ttis[1].tt_isdst = 0;
763 sp->ttis[1].tt_abbrind = 0;
764 atp = sp->ats;
765 typep = sp->types;
766 janfirst = 0;
767 for (year = EPOCH_YEAR; year <= 2037; ++year) {
768 starttime = transtime(janfirst, year, &start,
769 stdoffset);
770 endtime = transtime(janfirst, year, &end,
771 dstoffset);
772 if (starttime > endtime) {
773 *atp++ = endtime;
774 *typep++ = 1; /* DST ends */
775 *atp++ = starttime;
776 *typep++ = 0; /* DST begins */
777 } else {
778 *atp++ = starttime;
779 *typep++ = 0; /* DST begins */
780 *atp++ = endtime;
781 *typep++ = 1; /* DST ends */
782 }
783 janfirst +=
784 year_lengths[isleap(year)] * SECSPERDAY;
785 }
786 } else {
787 int sawstd;
788 int sawdst;
789 long stdfix;
790 long dstfix;
791 long oldfix;
792 int isdst;
793 register int i;
794
795 if (*name != '\0')
796 return -1;
797 if (load_result != 0)
798 return -1;
799 /*
800 ** Compute the difference between the real and
801 ** prototype standard and summer time offsets
802 ** from GMT, and put the real standard and summer
803 ** time offsets into the rules in place of the
804 ** prototype offsets.
805 */
806 sawstd = FALSE;
807 sawdst = FALSE;
808 stdfix = 0;
809 dstfix = 0;
810 for (i = 0; i < sp->typecnt; ++i) {
811 if (sp->ttis[i].tt_isdst) {
812 oldfix = dstfix;
813 dstfix =
814 sp->ttis[i].tt_gmtoff + dstoffset;
815 if (sawdst && (oldfix != dstfix))
816 return -1;
817 sp->ttis[i].tt_gmtoff = -dstoffset;
818 sp->ttis[i].tt_abbrind = stdlen + 1;
819 sawdst = TRUE;
820 } else {
821 oldfix = stdfix;
822 stdfix =
823 sp->ttis[i].tt_gmtoff + stdoffset;
824 if (sawstd && (oldfix != stdfix))
825 return -1;
826 sp->ttis[i].tt_gmtoff = -stdoffset;
827 sp->ttis[i].tt_abbrind = 0;
828 sawstd = TRUE;
829 }
830 }
831 /*
832 ** Make sure we have both standard and summer time.
833 */
834 if (!sawdst || !sawstd)
835 return -1;
836 /*
837 ** Now correct the transition times by shifting
838 ** them by the difference between the real and
839 ** prototype offsets. Note that this difference
840 ** can be different in standard and summer time;
841 ** the prototype probably has a 1-hour difference
842 ** between standard and summer time, but a different
843 ** difference can be specified in TZ.
844 */
845 isdst = FALSE; /* we start in standard time */
846 for (i = 0; i < sp->timecnt; ++i) {
847 register const struct ttinfo * ttisp;
848
849 /*
850 ** If summer time is in effect, and the
851 ** transition time was not specified as
852 ** standard time, add the summer time
853 ** offset to the transition time;
854 ** otherwise, add the standard time offset
855 ** to the transition time.
856 */
857 ttisp = &sp->ttis[sp->types[i]];
858 sp->ats[i] +=
859 (isdst && !ttisp->tt_ttisstd) ?
860 dstfix : stdfix;
861 isdst = ttisp->tt_isdst;
862 }
863 }
864 } else {
865 dstlen = 0;
866 sp->typecnt = 1; /* only standard time */
867 sp->timecnt = 0;
868 sp->ttis[0].tt_gmtoff = -stdoffset;
869 sp->ttis[0].tt_isdst = 0;
870 sp->ttis[0].tt_abbrind = 0;
871 }
872 sp->charcnt = stdlen + 1;
873 if (dstlen != 0)
874 sp->charcnt += dstlen + 1;
875 if (sp->charcnt > sizeof sp->chars)
876 return -1;
877 cp = sp->chars;
878 (void) strncpy(cp, stdname, stdlen);
879 cp += stdlen;
880 *cp++ = '\0';
881 if (dstlen != 0) {
882 (void) strncpy(cp, dstname, dstlen);
883 *(cp + dstlen) = '\0';
884 }
885 return 0;
886 }
887
888 static void
889 gmtload(sp)
890 struct state * const sp;
891 {
892 if (tzload(GMT, sp) != 0)
893 (void) tzparse(GMT, sp, TRUE);
894 }
895
896 void
897 tzset()
898 {
899 register const char * name;
900 void tzsetwall();
901
902 name = getenv("TZ");
903 if (name == NULL) {
904 tzsetwall();
905 return;
906 }
907 lcl_is_set = TRUE;
908 #ifdef ALL_STATE
909 if (lclptr == NULL) {
910 lclptr = (struct state *) malloc(sizeof *lclptr);
911 if (lclptr == NULL) {
912 settzname(); /* all we can do */
913 return;
914 }
915 }
916 #endif /* defined ALL_STATE */
917 if (*name == '\0') {
918 /*
919 ** User wants it fast rather than right.
920 */
921 lclptr->leapcnt = 0; /* so, we're off a little */
922 lclptr->timecnt = 0;
923 lclptr->ttis[0].tt_gmtoff = 0;
924 lclptr->ttis[0].tt_abbrind = 0;
925 (void) strcpy(lclptr->chars, GMT);
926 } else if (tzload(name, lclptr) != 0)
927 if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
928 (void) gmtload(lclptr);
929 settzname();
930 }
931
932 void
933 tzsetwall()
934 {
935 lcl_is_set = TRUE;
936 #ifdef ALL_STATE
937 if (lclptr == NULL) {
938 lclptr = (struct state *) malloc(sizeof *lclptr);
939 if (lclptr == NULL) {
940 settzname(); /* all we can do */
941 return;
942 }
943 }
944 #endif /* defined ALL_STATE */
945 if (tzload((char *) NULL, lclptr) != 0)
946 gmtload(lclptr);
947 settzname();
948 }
949
950 /*
951 ** The easy way to behave "as if no library function calls" localtime
952 ** is to not call it--so we drop its guts into "localsub", which can be
953 ** freely called. (And no, the PANS doesn't require the above behavior--
954 ** but it *is* desirable.)
955 **
956 ** The unused offset argument is for the benefit of mktime variants.
957 */
958
959 /*ARGSUSED*/
960 static void
961 localsub(timep, offset, tmp)
962 const time_t * const timep;
963 const long offset;
964 struct tm * const tmp;
965 {
966 register struct state * sp;
967 register const struct ttinfo * ttisp;
968 register int i;
969 const time_t t = *timep;
970
971 if (!lcl_is_set)
972 tzset();
973 sp = lclptr;
974 #ifdef ALL_STATE
975 if (sp == NULL) {
976 gmtsub(timep, offset, tmp);
977 return;
978 }
979 #endif /* defined ALL_STATE */
980 if (sp->timecnt == 0 || t < sp->ats[0]) {
981 i = 0;
982 while (sp->ttis[i].tt_isdst)
983 if (++i >= sp->typecnt) {
984 i = 0;
985 break;
986 }
987 } else {
988 for (i = 1; i < sp->timecnt; ++i)
989 if (t < sp->ats[i])
990 break;
991 i = sp->types[i - 1];
992 }
993 ttisp = &sp->ttis[i];
994 /*
995 ** To get (wrong) behavior that's compatible with System V Release 2.0
996 ** you'd replace the statement below with
997 ** t += ttisp->tt_gmtoff;
998 ** timesub(&t, 0L, sp, tmp);
999 */
1000 timesub(&t, ttisp->tt_gmtoff, sp, tmp);
1001 tmp->tm_isdst = ttisp->tt_isdst;
1002 tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
1003 tmp->tm_zone = &sp->chars[ttisp->tt_abbrind];
1004 }
1005
1006 struct tm *
1007 localtime(timep)
1008 const time_t * const timep;
1009 {
1010 static struct tm tm;
1011
1012 localsub(timep, 0L, &tm);
1013 return &tm;
1014 }
1015
1016 /*
1017 ** gmtsub is to gmtime as localsub is to localtime.
1018 */
1019
1020 static void
1021 gmtsub(timep, offset, tmp)
1022 const time_t * const timep;
1023 const long offset;
1024 struct tm * const tmp;
1025 {
1026 if (!gmt_is_set) {
1027 gmt_is_set = TRUE;
1028 #ifdef ALL_STATE
1029 gmtptr = (struct state *) malloc(sizeof *gmtptr);
1030 if (gmtptr != NULL)
1031 #endif /* defined ALL_STATE */
1032 gmtload(gmtptr);
1033 }
1034 timesub(timep, offset, gmtptr, tmp);
1035 /*
1036 ** Could get fancy here and deliver something such as
1037 ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
1038 ** but this is no time for a treasure hunt.
1039 */
1040 if (offset != 0)
1041 tmp->tm_zone = WILDABBR;
1042 else {
1043 #ifdef ALL_STATE
1044 if (gmtptr == NULL)
1045 tmp->TM_ZONE = GMT;
1046 else tmp->TM_ZONE = gmtptr->chars;
1047 #endif /* defined ALL_STATE */
1048 #ifndef ALL_STATE
1049 tmp->tm_zone = gmtptr->chars;
1050 #endif /* State Farm */
1051 }
1052 }
1053
1054 struct tm *
1055 gmtime(timep)
1056 const time_t * const timep;
1057 {
1058 static struct tm tm;
1059
1060 gmtsub(timep, 0L, &tm);
1061 return &tm;
1062 }
1063
1064 static void
1065 timesub(timep, offset, sp, tmp)
1066 const time_t * const timep;
1067 const long offset;
1068 register const struct state * const sp;
1069 register struct tm * const tmp;
1070 {
1071 register const struct lsinfo * lp;
1072 register long days;
1073 register long rem;
1074 register int y;
1075 register int yleap;
1076 register const int * ip;
1077 register long corr;
1078 register int hit;
1079 register int i;
1080
1081 corr = 0;
1082 hit = FALSE;
1083 #ifdef ALL_STATE
1084 i = (sp == NULL) ? 0 : sp->leapcnt;
1085 #endif /* defined ALL_STATE */
1086 #ifndef ALL_STATE
1087 i = sp->leapcnt;
1088 #endif /* State Farm */
1089 while (--i >= 0) {
1090 lp = &sp->lsis[i];
1091 if (*timep >= lp->ls_trans) {
1092 if (*timep == lp->ls_trans)
1093 hit = ((i == 0 && lp->ls_corr > 0) ||
1094 lp->ls_corr > sp->lsis[i - 1].ls_corr);
1095 corr = lp->ls_corr;
1096 break;
1097 }
1098 }
1099 days = *timep / SECSPERDAY;
1100 rem = *timep % SECSPERDAY;
1101 #ifdef mc68k
1102 if (*timep == 0x80000000) {
1103 /*
1104 ** A 3B1 muffs the division on the most negative number.
1105 */
1106 days = -24855;
1107 rem = -11648;
1108 }
1109 #endif /* mc68k */
1110 rem += (offset - corr);
1111 while (rem < 0) {
1112 rem += SECSPERDAY;
1113 --days;
1114 }
1115 while (rem >= SECSPERDAY) {
1116 rem -= SECSPERDAY;
1117 ++days;
1118 }
1119 tmp->tm_hour = (int) (rem / SECSPERHOUR);
1120 rem = rem % SECSPERHOUR;
1121 tmp->tm_min = (int) (rem / SECSPERMIN);
1122 tmp->tm_sec = (int) (rem % SECSPERMIN);
1123 if (hit)
1124 /*
1125 ** A positive leap second requires a special
1126 ** representation. This uses "... ??:59:60".
1127 */
1128 ++(tmp->tm_sec);
1129 tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
1130 if (tmp->tm_wday < 0)
1131 tmp->tm_wday += DAYSPERWEEK;
1132 y = EPOCH_YEAR;
1133 if (days >= 0)
1134 for ( ; ; ) {
1135 yleap = isleap(y);
1136 if (days < (long) year_lengths[yleap])
1137 break;
1138 ++y;
1139 days = days - (long) year_lengths[yleap];
1140 }
1141 else do {
1142 --y;
1143 yleap = isleap(y);
1144 days = days + (long) year_lengths[yleap];
1145 } while (days < 0);
1146 tmp->tm_year = y - TM_YEAR_BASE;
1147 tmp->tm_yday = (int) days;
1148 ip = mon_lengths[yleap];
1149 for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
1150 days = days - (long) ip[tmp->tm_mon];
1151 tmp->tm_mday = (int) (days + 1);
1152 tmp->tm_isdst = 0;
1153 tmp->tm_gmtoff = offset;
1154 }
1155
1156 /*
1157 ** A la X3J11
1158 */
1159
1160 char *
1161 asctime(timeptr)
1162 register const struct tm * timeptr;
1163 {
1164 static const char wday_name[DAYSPERWEEK][3] = {
1165 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1166 };
1167 static const char mon_name[MONSPERYEAR][3] = {
1168 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1169 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1170 };
1171 static char result[26];
1172
1173 (void) sprintf(result, "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n",
1174 wday_name[timeptr->tm_wday],
1175 mon_name[timeptr->tm_mon],
1176 timeptr->tm_mday, timeptr->tm_hour,
1177 timeptr->tm_min, timeptr->tm_sec,
1178 TM_YEAR_BASE + timeptr->tm_year);
1179 return result;
1180 }
1181
1182 char *
1183 ctime(timep)
1184 const time_t * const timep;
1185 {
1186 return asctime(localtime(timep));
1187 }
1188
1189 /*
1190 ** Adapted from code provided by Robert Elz, who writes:
1191 ** The "best" way to do mktime I think is based on an idea of Bob
1192 ** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
1193 ** It does a binary search of the time_t space. Since time_t's are
1194 ** just 32 bits, its a max of 32 iterations (even at 64 bits it
1195 ** would still be very reasonable).
1196 */
1197
1198 #ifndef WRONG
1199 #define WRONG (-1)
1200 #endif /* !defined WRONG */
1201
1202 static void
1203 normalize(tensptr, unitsptr, base)
1204 int * const tensptr;
1205 int * const unitsptr;
1206 const int base;
1207 {
1208 if (*unitsptr >= base) {
1209 *tensptr += *unitsptr / base;
1210 *unitsptr %= base;
1211 } else if (*unitsptr < 0) {
1212 *tensptr -= 1 + (-(*unitsptr + 1)) / base;
1213 *unitsptr = base - 1 - (-(*unitsptr + 1)) % base;
1214 }
1215 }
1216
1217 static int
1218 tmcomp(atmp, btmp)
1219 register const struct tm * const atmp;
1220 register const struct tm * const btmp;
1221 {
1222 register int result;
1223
1224 if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
1225 (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
1226 (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
1227 (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
1228 (result = (atmp->tm_min - btmp->tm_min)) == 0)
1229 result = atmp->tm_sec - btmp->tm_sec;
1230 return result;
1231 }
1232
1233 static time_t
1234 time2(tmp, funcp, offset, okayp)
1235 struct tm * const tmp;
1236 void (* const funcp)();
1237 const long offset;
1238 int * const okayp;
1239 {
1240 register const struct state * sp;
1241 register int dir;
1242 register int bits;
1243 register int i, j ;
1244 register int saved_seconds;
1245 time_t newt;
1246 time_t t;
1247 struct tm yourtm, mytm;
1248
1249 *okayp = FALSE;
1250 yourtm = *tmp;
1251 if (yourtm.tm_sec >= SECSPERMIN + 2 || yourtm.tm_sec < 0)
1252 normalize(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN);
1253 normalize(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR);
1254 normalize(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY);
1255 normalize(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR);
1256 while (yourtm.tm_mday <= 0) {
1257 --yourtm.tm_year;
1258 yourtm.tm_mday +=
1259 year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
1260 }
1261 while (yourtm.tm_mday > DAYSPERLYEAR) {
1262 yourtm.tm_mday -=
1263 year_lengths[isleap(yourtm.tm_year + TM_YEAR_BASE)];
1264 ++yourtm.tm_year;
1265 }
1266 for ( ; ; ) {
1267 i = mon_lengths[isleap(yourtm.tm_year +
1268 TM_YEAR_BASE)][yourtm.tm_mon];
1269 if (yourtm.tm_mday <= i)
1270 break;
1271 yourtm.tm_mday -= i;
1272 if (++yourtm.tm_mon >= MONSPERYEAR) {
1273 yourtm.tm_mon = 0;
1274 ++yourtm.tm_year;
1275 }
1276 }
1277 saved_seconds = yourtm.tm_sec;
1278 yourtm.tm_sec = 0;
1279 /*
1280 ** Calculate the number of magnitude bits in a time_t
1281 ** (this works regardless of whether time_t is
1282 ** signed or unsigned, though lint complains if unsigned).
1283 */
1284 for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
1285 ;
1286 /*
1287 ** If time_t is signed, then 0 is the median value,
1288 ** if time_t is unsigned, then 1 << bits is median.
1289 */
1290 t = (t < 0) ? 0 : ((time_t) 1 << bits);
1291 for ( ; ; ) {
1292 (*funcp)(&t, offset, &mytm);
1293 dir = tmcomp(&mytm, &yourtm);
1294 if (dir != 0) {
1295 if (bits-- < 0)
1296 return WRONG;
1297 if (bits < 0)
1298 --t;
1299 else if (dir > 0)
1300 t -= (time_t) 1 << bits;
1301 else t += (time_t) 1 << bits;
1302 continue;
1303 }
1304 if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
1305 break;
1306 /*
1307 ** Right time, wrong type.
1308 ** Hunt for right time, right type.
1309 ** It's okay to guess wrong since the guess
1310 ** gets checked.
1311 */
1312 sp = (const struct state *)
1313 ((funcp == localsub) ? lclptr : gmtptr);
1314 #ifdef ALL_STATE
1315 if (sp == NULL)
1316 return WRONG;
1317 #endif /* defined ALL_STATE */
1318 for (i = 0; i < sp->typecnt; ++i) {
1319 if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
1320 continue;
1321 for (j = 0; j < sp->typecnt; ++j) {
1322 if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
1323 continue;
1324 newt = t + sp->ttis[j].tt_gmtoff -
1325 sp->ttis[i].tt_gmtoff;
1326 (*funcp)(&newt, offset, &mytm);
1327 if (tmcomp(&mytm, &yourtm) != 0)
1328 continue;
1329 if (mytm.tm_isdst != yourtm.tm_isdst)
1330 continue;
1331 /*
1332 ** We have a match.
1333 */
1334 t = newt;
1335 goto label;
1336 }
1337 }
1338 return WRONG;
1339 }
1340 label:
1341 t += saved_seconds;
1342 (*funcp)(&t, offset, tmp);
1343 *okayp = TRUE;
1344 return t;
1345 }
1346
1347 static time_t
1348 time1(tmp, funcp, offset)
1349 struct tm * const tmp;
1350 void (* const funcp)();
1351 const long offset;
1352 {
1353 register time_t t;
1354 register const struct state * sp;
1355 register int samei, otheri;
1356 int okay;
1357
1358 if (tmp->tm_isdst > 1)
1359 tmp->tm_isdst = 1;
1360 t = time2(tmp, funcp, offset, &okay);
1361 if (okay || tmp->tm_isdst < 0)
1362 return t;
1363 /*
1364 ** We're supposed to assume that somebody took a time of one type
1365 ** and did some math on it that yielded a "struct tm" that's bad.
1366 ** We try to divine the type they started from and adjust to the
1367 ** type they need.
1368 */
1369 sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
1370 #ifdef ALL_STATE
1371 if (sp == NULL)
1372 return WRONG;
1373 #endif /* defined ALL_STATE */
1374 for (samei = 0; samei < sp->typecnt; ++samei) {
1375 if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
1376 continue;
1377 for (otheri = 0; otheri < sp->typecnt; ++otheri) {
1378 if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
1379 continue;
1380 tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
1381 sp->ttis[samei].tt_gmtoff;
1382 tmp->tm_isdst = !tmp->tm_isdst;
1383 t = time2(tmp, funcp, offset, &okay);
1384 if (okay)
1385 return t;
1386 tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
1387 sp->ttis[samei].tt_gmtoff;
1388 tmp->tm_isdst = !tmp->tm_isdst;
1389 }
1390 }
1391 return WRONG;
1392 }
1393
1394 time_t
1395 mktime(tmp)
1396 struct tm * const tmp;
1397 {
1398 return time1(tmp, localsub, 0L);
1399 }