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