1 /* Copyright (C) 1991-2002,2003,2004 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 Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the 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 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
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_initialized (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 void compute_change (tz_rule
*rule
, int year
) __THROW internal_function
;
73 static void tz_compute (const struct tm
*tm
) __THROW internal_function
;
74 static void tzset_internal (int always
, int explicit)
75 __THROW 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 static 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 /* Maximum length of a timezone name. tzset_internal keeps this up to date
125 (never decreasing it) when ! __use_tzfile.
126 tzfile.c keeps it up to date when __use_tzfile. */
127 size_t __tzname_cur_max
;
132 __libc_lock_lock (tzset_lock
);
134 tzset_internal (0, 0);
136 __libc_lock_unlock (tzset_lock
);
138 return __tzname_cur_max
;
143 /* Interpret the TZ envariable. */
146 tzset_internal (always
, explicit)
150 static int is_initialized
;
151 register const char *tz
;
154 unsigned short int hh
, mm
, ss
;
155 unsigned short int whichrule
;
157 if (is_initialized
&& !always
)
161 /* Examine the TZ environment variable. */
163 if (tz
== NULL
&& !explicit)
164 /* Use the site-wide default. This is a file name which means we
165 would not see changes to the file if we compare only the file
166 name for change. We want to notice file changes if tzset() has
167 been called explicitly. Leave TZ as NULL in this case. */
169 if (tz
&& *tz
== '\0')
170 /* User specified the empty string; use UTC explicitly. */
173 /* A leading colon means "implementation defined syntax".
174 We ignore the colon and always use the same algorithm:
175 try a data file, and if none exists parse the 1003.1 syntax. */
176 if (tz
&& *tz
== ':')
179 /* Check whether the value changes since the last run. */
180 if (old_tz
!= NULL
&& tz
!= NULL
&& strcmp (tz
, old_tz
) == 0)
181 /* No change, simply return. */
185 /* No user specification; use the site-wide default. */
188 tz_rules
[0].name
= NULL
;
189 tz_rules
[1].name
= NULL
;
191 /* Save the value of `tz'. */
194 old_tz
= tz
? __strdup (tz
) : NULL
;
196 /* Try to read a data file. */
197 __tzfile_read (tz
, 0, NULL
);
201 /* No data file found. Default to UTC if nothing specified. */
203 if (tz
== NULL
|| *tz
== '\0'
204 || (TZDEFAULT
!= NULL
&& strcmp (tz
, TZDEFAULT
) == 0))
206 tz_rules
[0].name
= tz_rules
[1].name
= "UTC";
207 tz_rules
[0].type
= tz_rules
[1].type
= J0
;
208 tz_rules
[0].m
= tz_rules
[0].n
= tz_rules
[0].d
= 0;
209 tz_rules
[1].m
= tz_rules
[1].n
= tz_rules
[1].d
= 0;
210 tz_rules
[0].secs
= tz_rules
[1].secs
= 0;
211 tz_rules
[0].offset
= tz_rules
[1].offset
= 0L;
212 tz_rules
[0].change
= tz_rules
[1].change
= (time_t) -1;
213 tz_rules
[0].computed_for
= tz_rules
[1].computed_for
= 0;
217 /* Clear out old state and reset to unnamed UTC. */
218 memset (tz_rules
, 0, sizeof tz_rules
);
219 tz_rules
[0].name
= tz_rules
[1].name
= "";
221 /* Get the standard timezone name. */
222 tzbuf
= strdupa (tz
);
224 if (sscanf (tz
, "%[^0-9,+-]", tzbuf
) != 1 ||
225 (l
= strlen (tzbuf
)) < 3)
228 tz_rules
[0].name
= __tzstring (tzbuf
);
232 /* Figure out the standard offset from UTC. */
233 if (*tz
== '\0' || (*tz
!= '+' && *tz
!= '-' && !isdigit (*tz
)))
236 if (*tz
== '-' || *tz
== '+')
237 tz_rules
[0].offset
= *tz
++ == '-' ? 1L : -1L;
239 tz_rules
[0].offset
= -1L;
240 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
243 tz_rules
[0].offset
= 0;
252 tz_rules
[0].offset
*= (min (ss
, 59) + (min (mm
, 59) * 60) +
253 (min (hh
, 24) * 60 * 60));
255 for (l
= 0; l
< 3; ++l
)
259 if (l
< 2 && *tz
== ':')
263 /* Get the DST timezone name (if any). */
266 char *n
= tzbuf
+ strlen (tzbuf
) + 1;
267 if (sscanf (tz
, "%[^0-9,+-]", n
) != 1 ||
268 (l
= strlen (n
)) < 3)
269 goto done_names
; /* Punt on name, set up the offsets. */
271 tz_rules
[1].name
= __tzstring (n
);
275 /* Figure out the DST offset from GMT. */
276 if (*tz
== '-' || *tz
== '+')
277 tz_rules
[1].offset
= *tz
++ == '-' ? 1L : -1L;
279 tz_rules
[1].offset
= -1L;
281 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
284 /* Default to one hour later than standard time. */
285 tz_rules
[1].offset
= tz_rules
[0].offset
+ (60 * 60);
293 tz_rules
[1].offset
*= (min (ss
, 59) + (min (mm
, 59) * 60) +
294 (min (hh
, 23) * (60 * 60)));
297 for (l
= 0; l
< 3; ++l
)
299 while (isdigit (*tz
))
301 if (l
< 2 && *tz
== ':')
304 if (*tz
== '\0' || (tz
[0] == ',' && tz
[1] == '\0'))
306 /* There is no rule. See if there is a default rule file. */
307 __tzfile_default (tz_rules
[0].name
, tz_rules
[1].name
,
308 tz_rules
[0].offset
, tz_rules
[1].offset
);
319 /* There is no DST. */
320 tz_rules
[1].name
= tz_rules
[0].name
;
321 tz_rules
[1].offset
= tz_rules
[0].offset
;
326 /* Figure out the standard <-> DST rules. */
327 for (whichrule
= 0; whichrule
< 2; ++whichrule
)
329 register tz_rule
*tzr
= &tz_rules
[whichrule
];
331 /* Ignore comma to support string following the incorrect
332 specification in early POSIX.1 printings. */
335 /* Get the date of the change. */
336 if (*tz
== 'J' || isdigit (*tz
))
339 tzr
->type
= *tz
== 'J' ? J1
: J0
;
340 if (tzr
->type
== J1
&& !isdigit (*++tz
))
342 tzr
->d
= (unsigned short int) strtoul (tz
, &end
, 10);
343 if (end
== tz
|| tzr
->d
> 365)
345 else if (tzr
->type
== J1
&& tzr
->d
== 0)
353 if (sscanf (tz
, "M%hu.%hu.%hu%n",
354 &tzr
->m
, &tzr
->n
, &tzr
->d
, &n
) != 3 ||
355 tzr
->m
< 1 || tzr
->m
> 12 ||
356 tzr
->n
< 1 || tzr
->n
> 5 || tzr
->d
> 6)
360 else if (*tz
== '\0')
362 /* United States Federal Law, the equivalent of "M4.1.0,M10.5.0". */
364 if (tzr
== &tz_rules
[0])
380 if (*tz
!= '\0' && *tz
!= '/' && *tz
!= ',')
384 /* Get the time of day of the change. */
388 switch (sscanf (tz
, "%hu:%hu:%hu", &hh
, &mm
, &ss
))
391 hh
= 2; /* Default to 2:00 AM. */
399 for (l
= 0; l
< 3; ++l
)
401 while (isdigit (*tz
))
403 if (l
< 2 && *tz
== ':')
406 tzr
->secs
= (hh
* 60 * 60) + (mm
* 60) + ss
;
409 /* Default to 2:00 AM. */
410 tzr
->secs
= 2 * 60 * 60;
412 tzr
->computed_for
= -1;
416 __daylight
= tz_rules
[0].offset
!= tz_rules
[1].offset
;
417 __timezone
= -tz_rules
[0].offset
;
418 __tzname
[0] = (char *) tz_rules
[0].name
;
419 __tzname
[1] = (char *) tz_rules
[1].name
;
422 /* Keep __tzname_cur_max up to date. */
423 size_t len0
= strlen (__tzname
[0]);
424 size_t len1
= strlen (__tzname
[1]);
425 if (len0
> __tzname_cur_max
)
426 __tzname_cur_max
= len0
;
427 if (len1
> __tzname_cur_max
)
428 __tzname_cur_max
= len1
;
432 /* Figure out the exact time (as a time_t) in YEAR
433 when the change described by RULE will occur and
434 put it in RULE->change, saving YEAR in RULE->computed_for. */
437 compute_change (rule
, year
)
443 if (year
!= -1 && rule
->computed_for
== year
)
444 /* Operations on times in 2 BC will be slower. Oh well. */
447 /* First set T to January 1st, 0:00:00 GMT in YEAR. */
449 t
= ((year
- 1970) * 365
450 + /* Compute the number of leapdays between 1970 and YEAR
451 (exclusive). There is a leapday every 4th year ... */
452 + ((year
- 1) / 4 - 1970 / 4)
453 /* ... except every 100th year ... */
454 - ((year
- 1) / 100 - 1970 / 100)
455 /* ... but still every 400th year. */
456 + ((year
- 1) / 400 - 1970 / 400)) * SECSPERDAY
;
463 /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years.
464 In non-leap years, or if the day number is 59 or less, just
465 add SECSPERDAY times the day number-1 to the time of
466 January 1, midnight, to get the day. */
467 t
+= (rule
->d
- 1) * SECSPERDAY
;
468 if (rule
->d
>= 60 && __isleap (year
))
474 Just add SECSPERDAY times the day number to the time of Jan 1st. */
475 t
+= rule
->d
* SECSPERDAY
;
479 /* Mm.n.d - Nth "Dth day" of month M. */
482 int d
, m1
, yy0
, yy1
, yy2
, dow
;
483 const unsigned short int *myday
=
484 &__mon_yday
[__isleap (year
)][rule
->m
];
486 /* First add SECSPERDAY for each day in months before M. */
487 t
+= myday
[-1] * SECSPERDAY
;
489 /* Use Zeller's Congruence to get day-of-week of first day of month. */
490 m1
= (rule
->m
+ 9) % 12 + 1;
491 yy0
= (rule
->m
<= 2) ? (year
- 1) : year
;
494 dow
= ((26 * m1
- 2) / 10 + 1 + yy2
+ yy2
/ 4 + yy1
/ 4 - 2 * yy1
) % 7;
498 /* DOW is the day-of-week of the first day of the month. Get the
499 day-of-month (zero-origin) of the first DOW day of the month. */
503 for (i
= 1; i
< rule
->n
; ++i
)
505 if (d
+ 7 >= (int) myday
[0] - myday
[-1])
510 /* D is the day-of-month (zero-origin) of the day we want. */
516 /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want.
517 Just add the time of day and local offset from GMT, and we're done. */
519 rule
->change
= t
- rule
->offset
+ rule
->secs
;
520 rule
->computed_for
= year
;
524 /* Figure out the correct timezone for TM and set `__tzname',
525 `__timezone', and `__daylight' accordingly. */
531 compute_change (&tz_rules
[0], 1900 + tm
->tm_year
);
532 compute_change (&tz_rules
[1], 1900 + tm
->tm_year
);
535 /* Reinterpret the TZ environment variable and set `tzname'. */
541 __libc_lock_lock (tzset_lock
);
543 tzset_internal (1, 1);
548 __tzname
[0] = (char *) tz_rules
[0].name
;
549 __tzname
[1] = (char *) tz_rules
[1].name
;
552 __libc_lock_unlock (tzset_lock
);
554 weak_alias (__tzset
, tzset
)
556 /* Return the `struct tm' representation of *TIMER in the local timezone.
557 Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
559 __tz_convert (const time_t *timer
, int use_localtime
, struct tm
*tp
)
561 long int leap_correction
;
566 __set_errno (EINVAL
);
570 __libc_lock_lock (tzset_lock
);
572 /* Update internal database according to current TZ setting.
573 POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.
574 This is a good idea since this allows at least a bit more parallelism.
575 By analogy we apply the same rule to gmtime_r. */
576 tzset_internal (tp
== &_tmbuf
, 0);
579 __tzfile_compute (*timer
, use_localtime
, &leap_correction
,
580 &leap_extra_secs
, tp
);
583 if (! __offtime (timer
, 0, tp
))
587 leap_correction
= 0L;
599 /* We have to distinguish between northern and southern
600 hemisphere. For the latter the daylight saving time
601 ends in the next year. */
602 if (__builtin_expect (tz_rules
[0].change
603 > tz_rules
[1].change
, 0))
604 isdst
= (*timer
< tz_rules
[1].change
605 || *timer
>= tz_rules
[0].change
);
607 isdst
= (*timer
>= tz_rules
[0].change
608 && *timer
< tz_rules
[1].change
);
609 tp
->tm_isdst
= isdst
;
610 tp
->tm_zone
= __tzname
[isdst
];
611 tp
->tm_gmtoff
= tz_rules
[isdst
].offset
;
621 if (__offtime (timer
, tp
->tm_gmtoff
- leap_correction
, tp
))
622 tp
->tm_sec
+= leap_extra_secs
;
627 __libc_lock_unlock (tzset_lock
);
633 libc_freeres_fn (free_mem
)
635 while (tzstring_list
!= NULL
)
637 struct tzstring_l
*old
= tzstring_list
;
639 tzstring_list
= tzstring_list
->next
;