1 /*
2 FUNCTION
3 <<tzset>>---set timezone characteristics from <[TZ]> environment variable
4
5 INDEX
6 tzset
7
8 SYNOPSIS
9 #include <time.h>
10 void tzset(void);
11
12 DESCRIPTION
13 <<tzset>> examines the <[TZ]> environment variable and sets up the three
14 external variables: <<_timezone>>, <<_daylight>>, and <<tzname>>.
15 The value of <<_timezone>> shall be the offset from the current time
16 to Universal Time.
17 The value of <<_daylight>> shall be 0 if there is no daylight savings
18 time for the current time zone, otherwise it will be non-zero.
19 The <<tzname>> array has two entries: the first is the designation of the
20 standard time period, the second is the designation of the alternate time
21 period.
22
23 The <[TZ]> environment variable is expected to be in the following POSIX
24 format (spaces inserted for clarity):
25
26 <[std]> <[offset1]> [<[dst]> [<[offset2]>] [,<[start]> [/<[time1]>], <[end]> [/<[time2]>]]]
27
28 where:
29
30 <[std]> is the designation for the standard time period (minimum 3,
31 maximum <<TZNAME_MAX>> bytes) in one of two forms:
32
33 *- quoted within angle bracket '<' '>' characters: portable numeric
34 sign or alphanumeric characters in the current locale; the
35 quoting characters are not included in the designation
36
37 *- unquoted: portable alphabetic characters in the current locale
38
39 <[offset1]> is the value to add to local standard time to get Universal Time;
40 it has the format:
41
42 [<[S]>]<[hh]>[:<[mm]>[:<[ss]>]]
43
44 where:
45
46 <[S]> is an optional numeric sign character; if negative '-', the
47 time zone is East of the International Reference
48 Meridian; else it is positive and West, and '+' may be used
49
50 <[hh]> is the required numeric hour between 0 and 24
51
52 <[mm]> is the optional numeric minute between 0 and 59
53
54 <[ss]> is the optional numeric second between 0 and 59
55
56 <[dst]> is the designation of the alternate (daylight saving or
57 summer) time period, with the same limits and forms as
58 the standard time period designation
59
60 <[offset2]> is the value to add to local alternate time to get
61 Universal Time; it has the same format as <[offset1]>
62
63 <[start]> is the date in the year that alternate time starts;
64 the form may be one of:
65 (quotes "'" around characters below are used only to distinguish literals)
66
67 <[n]> zero based Julian day (0-365), counting February 29 Leap days
68
69 'J'<[n]> one based Julian day (1-365), not counting February 29 Leap
70 days; in all years day 59 is February 28 and day 60 is March 1
71
72 'M'<[m]>'.'<[w]>'.'<[d]>
73 month <[m]> (1-12) week <[w]> (1-5) day <[d]> (0-6) where week 1 is
74 the first week in month <[m]> with day <[d]>; week 5 is the last
75 week in the month; day 0 is Sunday
76
77 <[time1]> is the optional local time that alternate time starts, in
78 the same format as <[offset1]> without any sign, and defaults
79 to 02:00:00
80
81 <[end]> is the date in the year that alternate time ends, in the same
82 forms as <[start]>
83
84 <[time2]> is the optional local time that alternate time ends, in
85 the same format as <[offset1]> without any sign, and
86 defaults to 02:00:00
87
88 Note that there is no white-space padding between fields. Also note that
89 if <[TZ]> is null, the default is Universal Time which has no daylight saving
90 time. If <[TZ]> is empty, the default EST5EDT is used.
91
92 RETURNS
93 There is no return value.
94
95 PORTABILITY
96 <<tzset>> is part of the POSIX standard.
97
98 Supporting OS subroutine required: None
99 */
100
101 #define _DEFAULT_SOURCE
102 #include <_ansi.h>
103 #include <stdio.h>
104 #include <stdlib.h>
105 #include <string.h>
106 #include <sys/types.h>
107 #include <time.h>
108 #include "local.h"
109
110 #define TZNAME_MIN 3 /* POSIX min TZ abbr size local def */
111 #define TZNAME_MAX 10 /* POSIX max TZ abbr size local def */
112
113 static char __tzname_std[TZNAME_MAX + 2];
114 static char __tzname_dst[TZNAME_MAX + 2];
115
116 void
_tzset_unlocked(void)117 _tzset_unlocked (void)
118 {
119 char *tzenv;
120 unsigned short hh, mm, ss, m, w, d;
121 int sign, n;
122 int i, ch;
123 long offset0, offset1;
124 __tzinfo_type *tz = __gettzinfo ();
125 static const struct __tzrule_struct default_tzrule = {'J', 0, 0, 0, 0, (time_t)0, 0L };
126
127 if ((tzenv = getenv ("TZ")) == NULL)
128 {
129 _timezone = 0;
130 _daylight = 0;
131 _tzname[0] = "GMT";
132 _tzname[1] = "GMT";
133 tz->__tzrule[0] = default_tzrule;
134 tz->__tzrule[1] = default_tzrule;
135 return;
136 }
137
138 /* default to unnamed UTC in case of error */
139 _timezone = 0;
140 _daylight = 0;
141 _tzname[0] = "";
142 _tzname[1] = "";
143 tz->__tzrule[0] = default_tzrule;
144 tz->__tzrule[1] = default_tzrule;
145
146 /* ignore implementation-specific format specifier */
147 if (*tzenv == ':')
148 ++tzenv;
149
150 /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
151 if (*tzenv == '<')
152 {
153 ++tzenv;
154
155 /* quit if no items, too few or too many chars, or no close quote '>' */
156 if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_std, &n) <= 0
157 || n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
158 return;
159
160 ++tzenv; /* bump for close quote '>' */
161 }
162 else
163 {
164 /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
165 if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_std, &n) <= 0
166 || n < TZNAME_MIN || TZNAME_MAX < n)
167 return;
168 }
169
170 tzenv += n;
171
172 sign = 1;
173 if (*tzenv == '-')
174 {
175 sign = -1;
176 ++tzenv;
177 }
178 else if (*tzenv == '+')
179 ++tzenv;
180
181 mm = 0;
182 ss = 0;
183
184 if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) < 1)
185 return;
186
187 offset0 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh);
188 tzenv += n;
189
190 /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
191 if (*tzenv == '<')
192 {
193 ++tzenv;
194
195 /* quit if no items, too few or too many chars, or no close quote '>' */
196 if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_dst, &n) <= 0 && tzenv[0] == '>')
197 { /* No dst */
198 _tzname[0] = __tzname_std;
199 _tzname[1] = _tzname[0];
200 tz->__tzrule[0].offset = offset0;
201 _timezone = offset0;
202 return;
203 }
204 else if (n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
205 { /* error */
206 return;
207 }
208
209 ++tzenv; /* bump for close quote '>' */
210 }
211 else
212 {
213 /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
214 if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_dst, &n) <= 0)
215 { /* No dst */
216 _tzname[0] = __tzname_std;
217 _tzname[1] = _tzname[0];
218 tz->__tzrule[0].offset = offset0;
219 _timezone = offset0;
220 return;
221 }
222 else if (n < TZNAME_MIN || TZNAME_MAX < n)
223 { /* error */
224 return;
225 }
226 }
227
228 tzenv += n;
229
230 /* otherwise we have a dst name, look for the offset */
231 sign = 1;
232 if (*tzenv == '-')
233 {
234 sign = -1;
235 ++tzenv;
236 }
237 else if (*tzenv == '+')
238 ++tzenv;
239
240 hh = 0;
241 mm = 0;
242 ss = 0;
243
244 n = 0;
245 if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0)
246 offset1 = offset0 - 3600;
247 else
248 offset1 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh);
249
250 tzenv += n;
251
252 for (i = 0; i < 2; ++i)
253 {
254 if (*tzenv == ',')
255 ++tzenv;
256
257 if (*tzenv == 'M')
258 {
259 if (sscanf (tzenv, "M%hu%n.%hu%n.%hu%n", &m, &n, &w, &n, &d, &n) != 3 ||
260 m < 1 || m > 12 || w < 1 || w > 5 || d > 6)
261 return;
262
263 tz->__tzrule[i].ch = 'M';
264 tz->__tzrule[i].m = m;
265 tz->__tzrule[i].n = w;
266 tz->__tzrule[i].d = d;
267
268 tzenv += n;
269 }
270 else
271 {
272 char *end;
273 if (*tzenv == 'J')
274 {
275 ch = 'J';
276 ++tzenv;
277 }
278 else
279 ch = 'D';
280
281 d = strtoul (tzenv, &end, 10);
282
283 /* if unspecified, default to US settings */
284 /* From 1987-2006, US was M4.1.0,M10.5.0, but starting in 2007 is
285 * M3.2.0,M11.1.0 (2nd Sunday March through 1st Sunday November) */
286 if (end == tzenv)
287 {
288 if (i == 0)
289 {
290 tz->__tzrule[0].ch = 'M';
291 tz->__tzrule[0].m = 3;
292 tz->__tzrule[0].n = 2;
293 tz->__tzrule[0].d = 0;
294 }
295 else
296 {
297 tz->__tzrule[1].ch = 'M';
298 tz->__tzrule[1].m = 11;
299 tz->__tzrule[1].n = 1;
300 tz->__tzrule[1].d = 0;
301 }
302 }
303 else
304 {
305 tz->__tzrule[i].ch = ch;
306 tz->__tzrule[i].d = d;
307 }
308
309 tzenv = end;
310 }
311
312 /* default time is 02:00:00 am */
313 hh = 2;
314 mm = 0;
315 ss = 0;
316 n = 0;
317
318 if (*tzenv == '/')
319 if (sscanf (tzenv, "/%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0)
320 {
321 /* error in time format, restore tz rules to default and return */
322 tz->__tzrule[0] = default_tzrule;
323 tz->__tzrule[1] = default_tzrule;
324 return;
325 }
326
327 tz->__tzrule[i].s = ss + SECSPERMIN * mm + SECSPERHOUR * hh;
328
329 tzenv += n;
330 }
331
332 tz->__tzrule[0].offset = offset0;
333 tz->__tzrule[1].offset = offset1;
334 _tzname[0] = __tzname_std;
335 _tzname[1] = __tzname_dst;
336 __tzcalc_limits (tz->__tzyear);
337 _timezone = tz->__tzrule[0].offset;
338 _daylight = tz->__tzrule[0].offset != tz->__tzrule[1].offset;
339 }
340
341 void
tzset(void)342 tzset (void)
343 {
344 TZ_LOCK;
345 _tzset_unlocked ();
346 TZ_UNLOCK;
347 }
348