fix missing string.h in strftime.c (needed by new strftime code)
[musl] / src / time / strftime.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <langinfo.h>
5 #include <locale.h>
6 #include <time.h>
7 #include <limits.h>
8 #include "libc.h"
9
10 const char *__nl_langinfo_l(nl_item, locale_t);
11
12 static int is_leap(int y)
13 {
14         /* Avoid overflow */
15         if (y>INT_MAX-1900) y -= 2000;
16         y += 1900;
17         return !(y%4) && ((y%100) || !(y%400));
18 }
19
20 static int week_num(const struct tm *tm)
21 {
22         int val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
23         /* If 1 Jan is just 1-3 days past Monday,
24          * the previous week is also in this year. */
25         if ((tm->tm_wday - tm->tm_yday - 2 + 371) % 7 <= 2)
26                 val++;
27         if (!val) {
28                 val = 52;
29                 /* If 31 December of prev year a Thursday,
30                  * or Friday of a leap year, then the
31                  * prev year has 53 weeks. */
32                 int dec31 = (tm->tm_wday - tm->tm_yday - 1 + 7) % 7;
33                 if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
34                         val++;
35         } else if (val == 53) {
36                 /* If 1 January is not a Thursday, and not
37                  * a Wednesday of a leap year, then this
38                  * year has only 52 weeks. */
39                 int jan1 = (tm->tm_wday - tm->tm_yday + 371) % 7;
40                 if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year)))
41                         val = 1;
42         }
43         return val;
44 }
45
46 size_t __strftime_l(char *restrict, size_t, const char *restrict, const struct tm *restrict, locale_t);
47
48 const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc)
49 {
50         nl_item item;
51         long long val;
52         const char *fmt;
53         int width = 2;
54
55         switch (f) {
56         case 'a':
57                 item = ABDAY_1 + tm->tm_wday;
58                 goto nl_strcat;
59         case 'A':
60                 item = DAY_1 + tm->tm_wday;
61                 goto nl_strcat;
62         case 'h':
63         case 'b':
64                 item = ABMON_1 + tm->tm_mon;
65                 goto nl_strcat;
66         case 'B':
67                 item = MON_1 + tm->tm_mon;
68                 goto nl_strcat;
69         case 'c':
70                 item = D_T_FMT;
71                 goto nl_strftime;
72         case 'C':
73                 val = (1900LL+tm->tm_year) / 100;
74                 goto number;
75         case 'd':
76                 val = tm->tm_mday;
77                 goto number;
78         case 'D':
79                 fmt = "%m/%d/%y";
80                 goto recu_strftime;
81         case 'e':
82                 val = tm->tm_mday;
83                 goto number;
84         case 'F':
85                 fmt = "%Y-%m-%d";
86                 goto recu_strftime;
87         case 'g':
88         case 'G':
89                 val = tm->tm_year + 1900LL;
90                 if (tm->tm_yday < 3 && week_num(tm) != 1) val--;
91                 else if (tm->tm_yday > 360 && week_num(tm) == 1) val++;
92                 if (f=='g') val %= 100;
93                 else width = 4;
94                 goto number;
95         case 'H':
96                 val = tm->tm_hour;
97                 goto number;
98         case 'I':
99                 val = tm->tm_hour;
100                 if (!val) val = 12;
101                 else if (val > 12) val -= 12;
102                 goto number;
103         case 'j':
104                 val = tm->tm_yday+1;
105                 width = 3;
106                 goto number;
107         case 'm':
108                 val = tm->tm_mon+1;
109                 goto number;
110         case 'M':
111                 val = tm->tm_min;
112                 goto number;
113         case 'n':
114                 *l = 1;
115                 return "\n";
116         case 'p':
117                 item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
118                 goto nl_strcat;
119         case 'r':
120                 item = T_FMT_AMPM;
121                 goto nl_strftime;
122         case 'R':
123                 fmt = "%H:%M";
124                 goto recu_strftime;
125         case 'S':
126                 val = tm->tm_sec;
127                 goto number;
128         case 't':
129                 *l = 1;
130                 return "\t";
131         case 'T':
132                 fmt = "%H:%M:%S";
133                 goto recu_strftime;
134         case 'u':
135                 val = tm->tm_wday ? tm->tm_wday : 7;
136                 width = 1;
137                 goto number;
138         case 'U':
139                 val = (tm->tm_yday + 7 - tm->tm_wday) / 7;
140                 goto number;
141         case 'W':
142                 val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
143                 goto number;
144         case 'V':
145                 val = week_num(tm);
146                 goto number;
147         case 'w':
148                 val = tm->tm_wday;
149                 width = 1;
150                 goto number;
151         case 'x':
152                 item = D_FMT;
153                 goto nl_strftime;
154         case 'X':
155                 item = T_FMT;
156                 goto nl_strftime;
157         case 'y':
158                 val = tm->tm_year % 100;
159                 goto number;
160         case 'Y':
161                 val = tm->tm_year + 1900;
162                 if (val >= 10000) {
163                         *l = snprintf(*s, sizeof *s, "+%lld", val);
164                         return *s;
165                 }
166                 width = 4;
167                 goto number;
168         case 'z':
169                 val = -tm->__tm_gmtoff;
170                 *l = snprintf(*s, sizeof *s, "%+.2d%.2d", val/3600, abs(val%3600)/60);
171                 return *s;
172         case 'Z':
173                 fmt = tm->__tm_zone;
174                 goto string;
175         case '%':
176                 *l = 1;
177                 return "%";
178         default:
179                 return 0;
180         }
181 number:
182         *l = snprintf(*s, sizeof *s, "%0*lld", width, val);
183         return *s;
184 nl_strcat:
185         fmt = __nl_langinfo_l(item, loc);
186 string:
187         *l = strlen(fmt);
188         return fmt;
189 nl_strftime:
190         fmt = __nl_langinfo_l(item, loc);
191 recu_strftime:
192         *l = __strftime_l(*s, sizeof *s, fmt, tm, loc);
193         if (!*l) return 0;
194         return *s;
195 }
196
197 size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
198 {
199         size_t l, k;
200         char buf[100];
201         char *p;
202         const char *t;
203         int plus;
204         unsigned long width;
205         for (l=0; l+1<n; f++) {
206                 if (!*f) {
207                         s[l] = 0;
208                         return l;
209                 }
210                 if (*f != '%') {
211                         s[l++] = *f;
212                         continue;
213                 }
214                 f++;
215                 if ((plus = (*f == '+'))) f++;
216                 width = strtoul(f, &p, 10);
217                 if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
218                         if (!width && p!=f) width = 1;
219                         if (width >= n-l) return 0;
220                 } else {
221                         width = 0;
222                 }
223                 f = p;
224                 if (*f == 'E' || *f == 'O') f++;
225                 t = __strftime_fmt_1(&buf, &k, *f, tm, loc);
226                 if (!t) return 0;
227                 if (width) {
228                         for (; *t=='+' || *t=='-' || (*t=='0'&&t[1]); t++, k--);
229                         width--;
230                         if (plus && tm->tm_year >= 10000-1900)
231                                 s[l++] = '+';
232                         else if (tm->tm_year < -1900)
233                                 s[l++] = '-';
234                         else
235                                 width++;
236                         if (width >= n-l) return 0;
237                         for (; width > k; width--)
238                                 s[l++] = '0';
239                 }
240                 if (k >= n-l) return 0;
241                 memcpy(s+l, t, k);
242                 l += k;
243         }
244         return 0;
245 }
246
247 size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
248 {
249         return __strftime_l(s, n, f, tm, 0);
250 }
251
252 weak_alias(__strftime_l, strftime_l);