remove incorrectly-made-visible internal dst offset variable
[musl] / src / time / tzset.c
1 #include <time.h>
2 #include <ctype.h>
3 #include <limits.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "libc.h"
7
8 #include "__time.h"
9
10 long  __timezone = 0;
11 int   __daylight = 0;
12 char *__tzname[2] = { 0, 0 };
13 int   __dst_offset = 0;
14
15 weak_alias(__timezone, timezone);
16 weak_alias(__daylight, daylight);
17 weak_alias(__tzname, tzname);
18
19 static char std_name[TZNAME_MAX+1];
20 static char dst_name[TZNAME_MAX+1];
21
22 /* all elements are zero-based */
23 static struct rule {
24         char month;
25         char week;
26         short day;
27         int time;
28 } __dst_start, __dst_end;
29
30 static void zname(char *d, char **s)
31 {
32         int i;
33         for (i=0; i<TZNAME_MAX && isalpha(d[i]=**s); i++, (*s)++);
34         d[i] = 0;
35 }
36
37 static int hhmmss(char **s)
38 {
39         int ofs = strtol(*s, s, 10)*3600;
40         if (ofs >= 0) {
41                 if (**s == ':') ofs += strtol(*s+1, s, 10)*60;
42                 if (**s == ':') ofs += strtol(*s+1, s, 10);
43         } else {
44                 if (**s == ':') ofs -= strtol(*s+1, s, 10)*60;
45                 if (**s == ':') ofs -= strtol(*s+1, s, 10);
46         }
47         return ofs;
48 }
49
50 static int dstrule(struct rule *rule, char **s)
51 {
52         if (**s != ',') return -1;
53         switch (*++*s) {
54         case 'J':
55                 rule->month = 'J';
56                 rule->day = strtol(*s+1, s, 10)-1;
57                 break;
58         case 'M':
59                 rule->month = strtol(*s+1, s, 10)-1;
60                 if (**s != '.' || rule->month < 0 || rule->month > 11)
61                         return -1;
62                 rule->week = strtol(*s+1, s, 10)-1;
63                 if (**s != '.' || rule->week < 0 || rule->week > 4)
64                         return -1;
65                 rule->day = strtol(*s+1, s, 10);
66                 if (rule->day < 0 || rule->day > 6)
67                         return -1;
68                 break;
69         default:
70                 rule->month = 'L';
71                 rule->day = strtol(*s+1, s, 10);
72                 break;
73         }
74         if (**s == '/') {
75                 (*s)++;
76                 rule->time = hhmmss(s);
77         } else rule->time = 7200;
78         return 0;
79 }
80
81 void tzset(void)
82 {
83         char *z, *a;
84         
85         strcpy(std_name, "GMT");
86         strcpy(dst_name, "GMT");
87         __tzname[0] = std_name;
88         __tzname[1] = dst_name;
89         __timezone = 0;
90         __daylight = 0;
91         
92         if (!(z = getenv("TZ")) || !isalpha(*z)) return;
93
94         zname(std_name, &z);
95         __timezone = hhmmss(&z);
96
97         zname(dst_name, &z);
98         if (dst_name[0]) __daylight=1;
99         a = z;
100         __dst_offset = hhmmss(&z) - __timezone;
101         if (z==a) __dst_offset = -3600;
102
103         if (dstrule(&__dst_start, &z) || dstrule(&__dst_end, &z))
104                 __daylight = 0;
105 }
106
107 void __tzset(void)
108 {
109         static int lock, init;
110         if (init) return;
111         LOCK(&lock);
112         if (!init) tzset();
113         init=1;
114         UNLOCK(&lock);
115 }
116
117 static int is_leap(int year)
118 {
119         year -= 100;
120         return !(year&3) && ((year%100) || !(year%400));
121 }
122
123 static int cutoff_yday(struct tm *tm, struct rule *rule)
124 {
125         static const char days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
126         static const int first_day[] = {0,31,59,90,120,151,181,212,243,273,304,335};
127         int yday, mday, leap;
128         
129         switch (rule->month) {
130         case 'J':
131                 return rule->day + (tm->tm_mon > 1 && is_leap(tm->tm_year));
132         case 'L':
133                 return rule->day;
134         default:
135                 yday = first_day[rule->month];
136                 leap = is_leap(tm->tm_year);
137                 if (rule->month > 1 && leap) yday++;
138                 mday = (rule->day - (yday + tm->tm_wday - tm->tm_yday) + 1400)%7 + 7*rule->week;
139                 if (mday >= days_in_month[rule->month] + (leap && rule->month == 1))
140                         mday -= 7;
141                 return mday + yday;
142         }
143 }
144
145 struct tm *__dst_adjust(struct tm *tm)
146 {
147         time_t t;
148         int start, end, secs;
149         int after_start, before_end;
150
151         if (tm->tm_isdst >= 0) return tm;
152         if (!__daylight) {
153                 tm->tm_isdst = 0;
154                 return tm;
155         }
156         
157         secs = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec;
158         start = cutoff_yday(tm, &__dst_start);
159         end = cutoff_yday(tm, &__dst_end);
160
161         after_start = (tm->tm_yday > start || (tm->tm_yday == start && secs >= __dst_start.time));
162         before_end = (tm->tm_yday < end || (tm->tm_yday == end && secs < __dst_end.time));
163
164         if ((after_start && before_end) || ((end < start) && (after_start || before_end))) {
165                 tm->tm_sec -= __dst_offset;
166                 tm->tm_isdst = 1;
167                 t = __tm_to_time(tm);
168                 return __time_to_tm(t, tm);
169         } else tm->tm_isdst = 0;
170
171         return tm;
172 }