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 static char *prev_tzenv = NULL;
116 
117 void
_tzset_unlocked(void)118 _tzset_unlocked (void)
119 {
120   char *tzenv;
121   unsigned short hh, mm, ss, m, w, d;
122   int sign, n;
123   int i, ch;
124   long offset0, offset1;
125   __tzinfo_type *tz = __gettzinfo ();
126   static const struct __tzrule_struct default_tzrule = {'J', 0, 0, 0, 0, (time_t)0, 0L };
127 
128   if ((tzenv = getenv ("TZ")) == NULL)
129       {
130 	_timezone = 0;
131 	_daylight = 0;
132 	_tzname[0] = "GMT";
133 	_tzname[1] = "GMT";
134 	tz->__tzrule[0] = default_tzrule;
135 	tz->__tzrule[1] = default_tzrule;
136 	free(prev_tzenv);
137 	prev_tzenv = NULL;
138 	return;
139       }
140 
141   if (prev_tzenv != NULL && strcmp(tzenv, prev_tzenv) == 0)
142     return;
143 
144   free(prev_tzenv);
145   prev_tzenv = malloc (strlen(tzenv) + 1);
146   if (prev_tzenv != NULL)
147     strcpy (prev_tzenv, tzenv);
148 
149   /* default to unnamed UTC in case of error */
150   _timezone = 0;
151   _daylight = 0;
152   _tzname[0] = "";
153   _tzname[1] = "";
154   tz->__tzrule[0] = default_tzrule;
155   tz->__tzrule[1] = default_tzrule;
156 
157   /* ignore implementation-specific format specifier */
158   if (*tzenv == ':')
159     ++tzenv;
160 
161   /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
162   if (*tzenv == '<')
163     {
164       ++tzenv;
165 
166       /* quit if no items, too few or too many chars, or no close quote '>' */
167       if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_std, &n) <= 0
168 		|| n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
169         return;
170 
171       ++tzenv;	/* bump for close quote '>' */
172     }
173   else
174     {
175       /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
176       if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_std, &n) <= 0
177 				|| n < TZNAME_MIN || TZNAME_MAX < n)
178         return;
179     }
180 
181   tzenv += n;
182 
183   sign = 1;
184   if (*tzenv == '-')
185     {
186       sign = -1;
187       ++tzenv;
188     }
189   else if (*tzenv == '+')
190     ++tzenv;
191 
192   mm = 0;
193   ss = 0;
194 
195   if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) < 1)
196     return;
197 
198   offset0 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh);
199   tzenv += n;
200 
201   /* allow POSIX angle bracket < > quoted signed alphanumeric tz abbr e.g. <MESZ+0330> */
202   if (*tzenv == '<')
203     {
204       ++tzenv;
205 
206       /* quit if no items, too few or too many chars, or no close quote '>' */
207       if (sscanf (tzenv, "%11[-+0-9A-Za-z]%n", __tzname_dst, &n) <= 0 && tzenv[0] == '>')
208 	{ /* No dst */
209           _tzname[0] = __tzname_std;
210           _tzname[1] = _tzname[0];
211           tz->__tzrule[0].offset = offset0;
212           _timezone = offset0;
213 	  return;
214         }
215       else if (n < TZNAME_MIN || TZNAME_MAX < n || '>' != tzenv[n])
216 	{ /* error */
217 	  return;
218 	}
219 
220       ++tzenv;	/* bump for close quote '>' */
221     }
222   else
223     {
224       /* allow POSIX unquoted alphabetic tz abbr e.g. MESZ */
225       if (sscanf (tzenv, "%11[A-Za-z]%n", __tzname_dst, &n) <= 0)
226 	{ /* No dst */
227           _tzname[0] = __tzname_std;
228           _tzname[1] = _tzname[0];
229           tz->__tzrule[0].offset = offset0;
230           _timezone = offset0;
231 	  return;
232         }
233       else if (n < TZNAME_MIN || TZNAME_MAX < n)
234 	{ /* error */
235 	  return;
236 	}
237     }
238 
239   tzenv += n;
240 
241   /* otherwise we have a dst name, look for the offset */
242   sign = 1;
243   if (*tzenv == '-')
244     {
245       sign = -1;
246       ++tzenv;
247     }
248   else if (*tzenv == '+')
249     ++tzenv;
250 
251   hh = 0;
252   mm = 0;
253   ss = 0;
254 
255   n  = 0;
256   if (sscanf (tzenv, "%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0)
257     offset1 = offset0 - 3600;
258   else
259     offset1 = sign * (ss + SECSPERMIN * mm + SECSPERHOUR * hh);
260 
261   tzenv += n;
262 
263   for (i = 0; i < 2; ++i)
264     {
265       if (*tzenv == ',')
266         ++tzenv;
267 
268       if (*tzenv == 'M')
269 	{
270 	  if (sscanf (tzenv, "M%hu%n.%hu%n.%hu%n", &m, &n, &w, &n, &d, &n) != 3 ||
271 	      m < 1 || m > 12 || w < 1 || w > 5 || d > 6)
272 	    return;
273 
274 	  tz->__tzrule[i].ch = 'M';
275 	  tz->__tzrule[i].m = m;
276 	  tz->__tzrule[i].n = w;
277 	  tz->__tzrule[i].d = d;
278 
279 	  tzenv += n;
280 	}
281       else
282 	{
283 	  char *end;
284 	  if (*tzenv == 'J')
285 	    {
286 	      ch = 'J';
287 	      ++tzenv;
288 	    }
289 	  else
290 	    ch = 'D';
291 
292 	  d = strtoul (tzenv, &end, 10);
293 
294 	  /* if unspecified, default to US settings */
295 	  /* From 1987-2006, US was M4.1.0,M10.5.0, but starting in 2007 is
296 	   * M3.2.0,M11.1.0 (2nd Sunday March through 1st Sunday November)  */
297 	  if (end == tzenv)
298 	    {
299 	      if (i == 0)
300 		{
301 		  tz->__tzrule[0].ch = 'M';
302 		  tz->__tzrule[0].m = 3;
303 		  tz->__tzrule[0].n = 2;
304 		  tz->__tzrule[0].d = 0;
305 		}
306 	      else
307 		{
308 		  tz->__tzrule[1].ch = 'M';
309 		  tz->__tzrule[1].m = 11;
310 		  tz->__tzrule[1].n = 1;
311 		  tz->__tzrule[1].d = 0;
312 		}
313 	    }
314 	  else
315 	    {
316 	      tz->__tzrule[i].ch = ch;
317 	      tz->__tzrule[i].d = d;
318 	    }
319 
320 	  tzenv = end;
321 	}
322 
323       /* default time is 02:00:00 am */
324       hh = 2;
325       mm = 0;
326       ss = 0;
327       n = 0;
328 
329       if (*tzenv == '/')
330 	if (sscanf (tzenv, "/%hu%n:%hu%n:%hu%n", &hh, &n, &mm, &n, &ss, &n) <= 0)
331 	  {
332 	    /* error in time format, restore tz rules to default and return */
333 	    tz->__tzrule[0] = default_tzrule;
334 	    tz->__tzrule[1] = default_tzrule;
335             return;
336           }
337 
338       tz->__tzrule[i].s = ss + SECSPERMIN * mm + SECSPERHOUR  * hh;
339 
340       tzenv += n;
341     }
342 
343   tz->__tzrule[0].offset = offset0;
344   tz->__tzrule[1].offset = offset1;
345   _tzname[0] = __tzname_std;
346   _tzname[1] = __tzname_dst;
347   __tzcalc_limits (tz->__tzyear);
348   _timezone = tz->__tzrule[0].offset;
349   _daylight = tz->__tzrule[0].offset != tz->__tzrule[1].offset;
350 }
351 
352 void
tzset(void)353 tzset (void)
354 {
355   TZ_LOCK;
356   _tzset_unlocked ();
357   TZ_UNLOCK;
358 }
359