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