1 /* Copyright (C) 1991,92,93,94,95,96,97,98 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
21 #include <bits/libc-lock.h>
30 #include <timezone/tzfile.h>
32 char *__tzname
[2] = { (char *) "GMT", (char *) "GMT" };
34 long int __timezone
= 0L;
36 weak_alias (__tzname
, tzname
)
37 weak_alias (__daylight
, daylight
)
38 weak_alias (__timezone
, timezone
)
40 /* This locks all the state variables in tzfile.c and this file. */
41 __libc_lock_define (static, tzset_lock
)
44 #define min(a, b) ((a) < (b) ? (a) : (b))
45 #define max(a, b) ((a) > (b) ? (a) : (b))
46 #define sign(x) ((x) < 0 ? -1 : 1)
49 /* This structure contains all the information about a
50 timezone given in the POSIX standard TZ envariable. */
56 enum { J0
, J1
, M
} type
; /* Interpretation of: */
57 unsigned short int m
, n
, d
; /* Month, week, day. */
58 unsigned int secs
; /* Time of day. */
60 long int offset
; /* Seconds east of GMT (west if < 0). */
62 /* We cache the computed time of change for a
63 given year so we don't have to recompute it. */
64 time_t change
; /* When to change to this zone. */
65 int computed_for
; /* Year above is computed for. */
68 /* tz_rules[0] is standard, tz_rules[1] is daylight. */
69 static tz_rule tz_rules
[2];
72 static int compute_change
__P ((tz_rule
*rule
, int year
)) internal_function
;
73 static int tz_compute
__P ((const struct tm
*tm
))
75 static void tzset_internal
__P ((int always
)) internal_function
;
77 /* List of buffers containing time zone strings. */
80 struct tzstring_l
*next
;
81 size_t len
; /* strlen(data) - doesn't count terminating NUL! */
85 struct tzstring_l
*tzstring_list
;
87 /* Allocate a permanent home for S. It will never be moved or deallocated,
88 but may share space with other strings.
89 Don't modify the returned string. */
91 __tzstring (const char *s
)
94 struct tzstring_l
*t
, *u
, *new;
95 size_t len
= strlen(s
);
97 /* Walk the list and look for a match. If this string is the same
98 as the end of an already-allocated string, it can share space. */
99 for (u
= t
= tzstring_list
; t
; u
= t
, t
= t
->next
)
102 p
= &t
->data
[t
->len
- len
];
103 if (strcmp (s
, p
) == 0)
107 /* Not found; allocate a new buffer. */
108 new = malloc (sizeof (struct tzstring_l
) + len
+ 1);
114 strcpy (new->data
, s
);
124 static char *old_tz
= NULL
;
126 /* Interpret the TZ envariable. */
129 tzset_internal (always
)
132 static int is_initialized
= 0;
133 register const char *tz
;
136 unsigned short int hh
, mm
, ss
;
137 unsigned short int whichrule
;
139 if (is_initialized
&& !always
)
143 /* Examine the TZ environment variable. */
146 /* No user specification; use the site-wide default. */
148 else if (*tz
== '\0')
149 /* User specified the empty string; use UTC explicitly. */
152 /* A leading colon means "implementation defined syntax".
153 We ignore the colon and always use the same algorithm:
154 try a data file, and if none exists parse the 1003.1 syntax. */
155 if (tz
&& *tz
== ':')
158 /* Check whether the value changes since the last run. */
159 if (old_tz
!= NULL
&& tz
!= NULL
&& strcmp (tz
, old_tz
) == 0)
160 /* No change, simply return. */
163 tz_rules
[0].name
= NULL
;
164 tz_rules
[1].name
= NULL
;
166 /* Save the value of `tz'. */
169 old_tz
= tz
? __strdup (tz
) : NULL
;
171 /* Try to read a data file. */
176 /* No data file found. Default to UTC if nothing specified. */
178 if (tz
== NULL
|| *tz
== '\0')
180 tz_rules
[0].name
= tz_rules
[1].name
= "UTC";
181 tz_rules
[0].type
= tz_rules
[1].type
= J0
;
182 tz_rules
[0].m
= tz_rules
[0].n
= tz_rules
[0].d
= 0;
183 tz_rules
[1].m
= tz_rules
[1].n
= tz_rules
[1].d
= 0;
184 tz_rules
[0].secs
= tz_rules
[1].secs
= 0;
185 tz_rules
[0].offset
= tz_rules
[1].offset
= 0L;
186 tz_rules
[0].change
= tz_rules
[1].change
= (time_t) -1;
187 tz_rules
[0].computed_for
= tz_rules
[1].computed_for
= 0;
191 /* Clear out old state and reset to unnamed UTC. */
192 memset (tz_rules
, 0, sizeof tz_rules
);
193 tz_rules
[0].name
= tz_rules
[1].name
= "";
195 /* Get the standard timezone name. */
196 tzbuf
= strdupa (tz
);
198 if (sscanf (tz
, "%[^0-9,+-]", tzbuf
) != 1 ||
199 (l
= strlen (tzbuf
)) < 3)
202 tz_rules
[0].name
= __tzstring (tzbuf
);
206 /* Figure out the standard offset from UTC. */
207 if (*tz
== '\0' || (*tz
!= '+' && *tz
!= '-' && !isdigit (*tz
)))
210 if (*tz
== '-' || *tz
== '+')
211 tz_rules
[0].offset
= *tz
++ == '-' ? 1L : -1L;
213 tz_rules
[0].offset
= -1L;
214 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
225 tz_rules
[0].offset
*= (min (ss
, 59) + (min (mm
, 59) * 60) +
226 (min (hh
, 23) * 60 * 60));
228 for (l
= 0; l
< 3; ++l
)
232 if (l
< 2 && *tz
== ':')
236 /* Get the DST timezone name (if any). */
239 char *n
= tzbuf
+ strlen (tzbuf
) + 1;
240 if (sscanf (tz
, "%[^0-9,+-]", n
) != 1 ||
241 (l
= strlen (n
)) < 3)
242 goto done_names
; /* Punt on name, set up the offsets. */
244 tz_rules
[1].name
= __tzstring (n
);
248 /* Figure out the DST offset from GMT. */
249 if (*tz
== '-' || *tz
== '+')
250 tz_rules
[1].offset
= *tz
++ == '-' ? 1L : -1L;
252 tz_rules
[1].offset
= -1L;
254 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
257 /* Default to one hour later than standard time. */
258 tz_rules
[1].offset
= tz_rules
[0].offset
+ (60 * 60);
266 tz_rules
[1].offset
*= (min (ss
, 59) + (min (mm
, 59) * 60) +
267 (min (hh
, 23) * (60 * 60)));
270 for (l
= 0; l
< 3; ++l
)
272 while (isdigit (*tz
))
274 if (l
< 2 && *tz
== ':')
277 if (*tz
== '\0' || (tz
[0] == ',' && tz
[1] == '\0'))
279 /* There is no rule. See if there is a default rule file. */
280 __tzfile_default (tz_rules
[0].name
, tz_rules
[1].name
,
281 tz_rules
[0].offset
, tz_rules
[1].offset
);
292 /* There is no DST. */
293 tz_rules
[1].name
= tz_rules
[0].name
;
294 tz_rules
[1].offset
= tz_rules
[0].offset
;
299 /* Figure out the standard <-> DST rules. */
300 for (whichrule
= 0; whichrule
< 2; ++whichrule
)
302 register tz_rule
*tzr
= &tz_rules
[whichrule
];
304 /* Ignore comma to support string following the incorrect
305 specification in early POSIX.1 printings. */
308 /* Get the date of the change. */
309 if (*tz
== 'J' || isdigit (*tz
))
312 tzr
->type
= *tz
== 'J' ? J1
: J0
;
313 if (tzr
->type
== J1
&& !isdigit (*++tz
))
315 tzr
->d
= (unsigned short int) strtoul (tz
, &end
, 10);
316 if (end
== tz
|| tzr
->d
> 365)
318 else if (tzr
->type
== J1
&& tzr
->d
== 0)
326 if (sscanf (tz
, "M%hu.%hu.%hu%n",
327 &tzr
->m
, &tzr
->n
, &tzr
->d
, &n
) != 3 ||
328 tzr
->m
< 1 || tzr
->m
> 12 ||
329 tzr
->n
< 1 || tzr
->n
> 5 || tzr
->d
> 6)
333 else if (*tz
== '\0')
335 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
337 if (tzr
== &tz_rules
[0])
353 if (*tz
!= '\0' && *tz
!= '/' && *tz
!= ',')
357 /* Get the time of day of the change. */
361 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
364 hh
= 2; /* Default to 2:00 AM. */
372 for (l
= 0; l
< 3; ++l
)
374 while (isdigit (*tz
))
376 if (l
< 2 && *tz
== ':')
379 tzr
->secs
= (hh
* 60 * 60) + (mm
* 60) + ss
;
382 /* Default to 2:00 AM. */
383 tzr
->secs
= 2 * 60 * 60;
385 tzr
->computed_for
= -1;
389 /* We know the offset now, set `__timezone'. */
390 __timezone
= -tz_rules
[0].offset
;
393 /* Maximum length of a timezone name. __tz_compute keeps this up to date
394 (never decreasing it) when ! __use_tzfile.
395 tzfile.c keeps it up to date when __use_tzfile. */
396 size_t __tzname_cur_max
;
401 __libc_lock_lock (tzset_lock
);
405 __libc_lock_unlock (tzset_lock
);
407 return __tzname_cur_max
;
410 /* Figure out the exact time (as a time_t) in YEAR
411 when the change described by RULE will occur and
412 put it in RULE->change, saving YEAR in RULE->computed_for.
413 Return nonzero if successful, zero on failure. */
416 compute_change (rule
, year
)
423 if (year
!= -1 && rule
->computed_for
== year
)
424 /* Operations on times in 1969 will be slower. Oh well. */
427 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
429 for (y
= 1970; y
< year
; ++y
)
430 t
+= SECSPERDAY
* (__isleap (y
) ? 366 : 365);
435 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
436 In non-leap years, or if the day number is 59 or less, just
437 add SECSPERDAY times the day number-1 to the time of
438 January 1, midnight, to get the day. */
439 t
+= (rule
->d
- 1) * SECSPERDAY
;
440 if (rule
->d
>= 60 && __isleap (year
))
446 Just add SECSPERDAY times the day number to the time of Jan 1st. */
447 t
+= rule
->d
* SECSPERDAY
;
451 /* Mm.n.d - Nth "Dth day" of month M. */
454 int d
, m1
, yy0
, yy1
, yy2
, dow
;
455 const unsigned short int *myday
=
456 &__mon_yday
[__isleap (year
)][rule
->m
];
458 /* First add SECSPERDAY for each day in months before M. */
459 t
+= myday
[-1] * SECSPERDAY
;
461 /* Use Zeller's Congruence to get day-of-week of first day of month. */
462 m1
= (rule
->m
+ 9) % 12 + 1;
463 yy0
= (rule
->m
<= 2) ? (year
- 1) : year
;
466 dow
= ((26 * m1
- 2) / 10 + 1 + yy2
+ yy2
/ 4 + yy1
/ 4 - 2 * yy1
) % 7;
470 /* DOW is the day-of-week of the first day of the month. Get the
471 day-of-month (zero-origin) of the first DOW day of the month. */
475 for (i
= 1; i
< rule
->n
; ++i
)
477 if (d
+ 7 >= (int) myday
[0] - myday
[-1])
482 /* D is the day-of-month (zero-origin) of the day we want. */
488 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
489 Just add the time of day and local offset from GMT, and we're done. */
491 rule
->change
= t
- rule
->offset
+ rule
->secs
;
492 rule
->computed_for
= year
;
497 /* Figure out the correct timezone for TM and set `__tzname',
498 `__timezone', and `__daylight' accordingly. Return nonzero on
499 success, zero on failure. */
505 if (! compute_change (&tz_rules
[0], 1900 + tm
->tm_year
)
506 || ! compute_change (&tz_rules
[1], 1900 + tm
->tm_year
))
508 /* We have to distinguish between northern and southern hemisphere.
509 For the later the daylight saving time ends in the next year.
510 It is easier to detect this after first computing the time for the
511 wrong year since now we simply can compare the times to switch. */
512 if (tz_rules
[0].change
> tz_rules
[1].change
513 && ! compute_change (&tz_rules
[1], 1900 + tm
->tm_year
+ 1))
516 __daylight
= tz_rules
[0].offset
!= tz_rules
[1].offset
;
517 __timezone
= -tz_rules
[0].offset
;
518 __tzname
[0] = (char *) tz_rules
[0].name
;
519 __tzname
[1] = (char *) tz_rules
[1].name
;
522 /* Keep __tzname_cur_max up to date. */
523 size_t len0
= strlen (__tzname
[0]);
524 size_t len1
= strlen (__tzname
[1]);
525 if (len0
> __tzname_cur_max
)
526 __tzname_cur_max
= len0
;
527 if (len1
> __tzname_cur_max
)
528 __tzname_cur_max
= len1
;
534 /* Reinterpret the TZ environment variable and set `tzname'. */
540 __libc_lock_lock (tzset_lock
);
547 __tzname
[0] = (char *) tz_rules
[0].name
;
548 __tzname
[1] = (char *) tz_rules
[1].name
;
551 __libc_lock_unlock (tzset_lock
);
553 weak_alias (__tzset
, tzset
)
555 /* Return the `struct tm' representation of *TIMER in the local timezone.
556 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
558 __tz_convert (const time_t *timer
, int use_localtime
, struct tm
*tp
)
560 long int leap_correction
;
565 __set_errno (EINVAL
);
569 __libc_lock_lock (tzset_lock
);
571 /* Update internal database according to current TZ setting.
572 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
573 This is a good idea since this allows at least a bit more parallelism.
574 By analogy we apply the same rule to gmtime_r. */
575 tzset_internal (tp
== &_tmbuf
);
579 if (! __tzfile_compute (*timer
, use_localtime
,
580 &leap_correction
, &leap_extra_secs
, tp
))
585 if (! (__offtime (timer
, 0, tp
) && tz_compute (tp
)))
587 leap_correction
= 0L;
597 int isdst
= (*timer
>= tz_rules
[0].change
598 && *timer
< tz_rules
[1].change
);
599 tp
->tm_isdst
= isdst
;
600 tp
->tm_zone
= __tzname
[isdst
];
601 tp
->tm_gmtoff
= tz_rules
[isdst
].offset
;
611 if (__offtime (timer
, tp
->tm_gmtoff
- leap_correction
, tp
))
612 tp
->tm_sec
+= leap_extra_secs
;
617 __libc_lock_unlock (tzset_lock
);