]>
Commit | Line | Data |
---|---|---|
320054e8 DG |
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 "locale_impl.h" | |
9 | #include "time_impl.h" | |
10 | ||
11 | static int is_leap(int y) | |
12 | { | |
13 | /* Avoid overflow */ | |
14 | if (y>INT_MAX-1900) y -= 2000; | |
15 | y += 1900; | |
16 | return !(y%4) && ((y%100) || !(y%400)); | |
17 | } | |
18 | ||
19 | static int week_num(const struct tm *tm) | |
20 | { | |
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 *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) | |
46 | { | |
47 | nl_item item; | |
48 | long long val; | |
49 | const char *fmt = "-"; | |
50 | int width = 2, def_pad = '0'; | |
51 | ||
52 | switch (f) { | |
53 | case 'a': | |
54 | if (tm->tm_wday > 6U) goto string; | |
55 | item = ABDAY_1 + tm->tm_wday; | |
56 | goto nl_strcat; | |
57 | case 'A': | |
58 | if (tm->tm_wday > 6U) goto string; | |
59 | item = DAY_1 + tm->tm_wday; | |
60 | goto nl_strcat; | |
61 | case 'h': | |
62 | case 'b': | |
63 | if (tm->tm_mon > 11U) goto string; | |
64 | item = ABMON_1 + tm->tm_mon; | |
65 | goto nl_strcat; | |
66 | case 'B': | |
67 | if (tm->tm_mon > 11U) goto string; | |
68 | item = MON_1 + tm->tm_mon; | |
69 | goto nl_strcat; | |
70 | case 'c': | |
71 | item = D_T_FMT; | |
72 | goto nl_strftime; | |
73 | case 'C': | |
74 | val = (1900LL+tm->tm_year) / 100; | |
75 | goto number; | |
76 | case 'e': | |
77 | def_pad = '_'; | |
78 | case 'd': | |
79 | val = tm->tm_mday; | |
80 | goto number; | |
81 | case 'D': | |
82 | fmt = "%m/%d/%y"; | |
83 | goto recu_strftime; | |
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; | |
320054e8 DG |
125 | case 's': |
126 | val = __tm_to_secs(tm) - tm->__tm_gmtoff; | |
127 | width = 1; | |
128 | goto number; | |
320054e8 DG |
129 | case 'S': |
130 | val = tm->tm_sec; | |
131 | goto number; | |
132 | case 't': | |
133 | *l = 1; | |
134 | return "\t"; | |
135 | case 'T': | |
136 | fmt = "%H:%M:%S"; | |
137 | goto recu_strftime; | |
138 | case 'u': | |
139 | val = tm->tm_wday ? tm->tm_wday : 7; | |
140 | width = 1; | |
141 | goto number; | |
142 | case 'U': | |
143 | val = (tm->tm_yday + 7U - tm->tm_wday) / 7; | |
144 | goto number; | |
145 | case 'W': | |
146 | val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; | |
147 | goto number; | |
148 | case 'V': | |
149 | val = week_num(tm); | |
150 | goto number; | |
151 | case 'w': | |
152 | val = tm->tm_wday; | |
153 | width = 1; | |
154 | goto number; | |
155 | case 'x': | |
156 | item = D_FMT; | |
157 | goto nl_strftime; | |
158 | case 'X': | |
159 | item = T_FMT; | |
160 | goto nl_strftime; | |
161 | case 'y': | |
162 | val = (tm->tm_year + 1900LL) % 100; | |
163 | if (val < 0) val = -val; | |
164 | goto number; | |
165 | case 'Y': | |
166 | val = tm->tm_year + 1900LL; | |
167 | if (val >= 10000) { | |
168 | *l = snprintf(*s, sizeof *s, "+%lld", val); | |
169 | return *s; | |
170 | } | |
171 | width = 4; | |
172 | goto number; | |
173 | case 'z': | |
174 | if (tm->tm_isdst < 0) { | |
175 | *l = 0; | |
176 | return ""; | |
177 | } | |
178 | *l = snprintf(*s, sizeof *s, "%+.4ld", | |
179 | tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); | |
180 | return *s; | |
181 | case 'Z': | |
182 | if (tm->tm_isdst < 0) { | |
183 | *l = 0; | |
184 | return ""; | |
185 | } | |
186 | fmt = __tm_to_tzname(tm); | |
187 | goto string; | |
188 | case '%': | |
189 | *l = 1; | |
190 | return "%"; | |
191 | default: | |
192 | return 0; | |
193 | } | |
194 | number: | |
195 | switch (pad ? pad : def_pad) { | |
196 | case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; | |
197 | case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; | |
198 | case '0': | |
199 | default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; | |
200 | } | |
201 | return *s; | |
202 | nl_strcat: | |
203 | fmt = __nl_langinfo_l(item, loc); | |
204 | string: | |
205 | *l = strlen(fmt); | |
206 | return fmt; | |
207 | nl_strftime: | |
208 | fmt = __nl_langinfo_l(item, loc); | |
209 | recu_strftime: | |
210 | *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); | |
211 | if (!*l) return 0; | |
212 | return *s; | |
213 | } | |
214 | ||
215 | size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) | |
216 | { | |
217 | size_t l, k; | |
218 | char buf[100]; | |
219 | char *p; | |
220 | const char *t; | |
221 | int pad, plus; | |
222 | unsigned long width; | |
223 | for (l=0; l<n; f++) { | |
224 | if (!*f) { | |
225 | s[l] = 0; | |
226 | return l; | |
227 | } | |
228 | if (*f != '%') { | |
229 | s[l++] = *f; | |
230 | continue; | |
231 | } | |
232 | f++; | |
233 | pad = 0; | |
234 | if (*f == '-' || *f == '_' || *f == '0') pad = *f++; | |
235 | if ((plus = (*f == '+'))) f++; | |
236 | width = strtoul(f, &p, 10); | |
237 | if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') { | |
238 | if (!width && p!=f) width = 1; | |
239 | } else { | |
240 | width = 0; | |
241 | } | |
242 | f = p; | |
243 | if (*f == 'E' || *f == 'O') f++; | |
244 | t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad); | |
245 | if (!t) break; | |
246 | if (width) { | |
247 | /* Trim off any sign and leading zeros, then | |
248 | * count remaining digits to determine behavior | |
249 | * for the + flag. */ | |
250 | if (*t=='+' || *t=='-') t++, k--; | |
251 | for (; *t=='0' && t[1]-'0'<10U; t++, k--); | |
252 | if (width < k) width = k; | |
253 | size_t d; | |
254 | for (d=0; t[d]-'0'<10U; d++); | |
255 | if (tm->tm_year < -1900) { | |
256 | s[l++] = '-'; | |
257 | width--; | |
258 | } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { | |
259 | s[l++] = '+'; | |
260 | width--; | |
261 | } | |
262 | for (; width > k && l < n; width--) | |
263 | s[l++] = '0'; | |
264 | } | |
265 | if (k > n-l) k = n-l; | |
266 | memcpy(s+l, t, k); | |
267 | l += k; | |
268 | } | |
269 | if (n) { | |
270 | if (l==n) l=n-1; | |
271 | s[l] = 0; | |
272 | } | |
273 | return 0; | |
274 | } | |
275 | ||
276 | size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) | |
277 | { | |
278 | return __strftime_l(s, n, f, tm, CURRENT_LOCALE); | |
279 | } | |
280 | ||
281 | weak_alias(__strftime_l, strftime_l); |