strftime.c - scc - simple c99 compiler
(HTM) git clone git://git.simple-cc.org/scc
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Submodules
(DIR) README
(DIR) LICENSE
---
strftime.c (5471B)
---
1 #include <string.h>
2 #include <time.h>
3
4 #include "../libc.h"
5
6 #undef strftime
7
8 static char *days[] = {
9 "Sunday", "Monday", "Tuesday", "Wednesday",
10 "Thursday", "Friday", "Saturday",
11 };
12
13 static char *months[] = {
14 "January", "February", "March", "April",
15 "May", "June", "July", "August",
16 "September", "October", "November", "December"
17 };
18
19 static char *am_pm[] = {"AM", "PM"};
20
21 static int
22 first(int day, int year)
23 {
24 int ny;
25
26 ny = _newyear(year);
27 if (ny == day)
28 return 0;
29 if (day - ny < 0)
30 return 7 - (ny - day);
31 return day - ny;
32 }
33
34 static int
35 weeknum(const struct tm *tm, int day)
36 {
37 int fday, val;
38
39 fday = first(day, tm->tm_year);
40 if (tm->tm_yday < fday) {
41 val = 0;
42 } else {
43 val = tm->tm_yday - fday;
44 val /= 7;
45 val++;
46 }
47 return val;
48 }
49
50 static int
51 isoyear(const struct tm *tm)
52 {
53 int monday;
54
55 if (tm->tm_yday < 7) {
56 monday = first(THU, tm->tm_year) - 3;
57 if (tm->tm_yday < monday)
58 return tm->tm_year - 1;
59 } else if (tm->tm_yday > 357) {
60 monday = first(THU, tm->tm_year + 1) - 3;
61 if (tm->tm_mday >= (31 + monday))
62 return tm->tm_year + 1;
63 }
64 return tm->tm_year;
65 }
66
67 static int
68 isoweek(const struct tm *tm)
69 {
70 int year, monday, yday, val;
71
72 year = isoyear(tm);
73 monday = first(THU, year) - 3;
74 yday = tm->tm_yday;
75 if (year > tm->tm_year)
76 yday = tm->tm_mday - 31 + monday;
77 else if (year < tm->tm_year)
78 yday = _daysyear(year) + yday;
79 val = yday - monday;
80 val /= 7;
81 val++;
82 return val;
83 }
84
85 static int
86 isoday(const struct tm *tm)
87 {
88 if (tm->tm_wday == 0)
89 return 7;
90 return tm->tm_wday;
91 }
92
93 static size_t
94 sval(char *s, size_t siz, char **strs, int abrev, int idx, int max)
95 {
96 char *str;
97 size_t len;
98
99 if (siz == 0)
100 return 0;
101
102 if (idx < 0 && idx >= max) {
103 *s = '?';
104 return 1;
105 }
106
107 str = strs[idx];
108 len = (!abrev) ? strlen(str) : 3;
109 if (len > siz)
110 len = siz;
111
112 memcpy(s, str, len);
113 return len;
114 }
115
116 static size_t
117 dval(char *s, size_t siz, int prec, int fill, int val)
118 {
119 char *t;
120 int n;
121 static char digits[] = "0123456789";
122
123 if (siz == 0)
124 return 0;
125
126 if (prec > siz)
127 prec = siz;
128
129 if (val < 0) {
130 *s = '?';
131 return 1;
132 }
133
134 n = prec;
135 do {
136 s[--n] = digits[val % 10];
137 val /= 10;
138 } while (n > 0 && val > 0);
139
140 while (n > 0)
141 s[--n] = fill;
142
143 return prec;
144 }
145
146 static size_t
147 timezone(char *s, size_t siz, const struct tm * restrict tm)
148 {
149 size_t n;
150 long off = tm->tm_gmtoff;
151
152 if (off >= 0) {
153 *s++ = '+';
154 } else {
155 *s++ = '-';
156 off = -off;
157 }
158
159 n = 1;
160 n += dval(s, siz-n, 2, '0', off / 3600);
161 n += dval(s + n, siz-n, 2, '0', (off % 3600) / 60);
162
163 return n;
164 }
165
166 size_t
167 strftime(char *restrict s, size_t maxsize,
168 const char *restrict format, const struct tm *restrict timeptr)
169 {
170 int ch, abrev, val, fill, width;
171 size_t n, inc;
172 char *tfmt, *begin;
173
174 begin = s;
175 for (n = maxsize; (ch = *format++) && n > 0; s += inc, n -= inc) {
176 if (ch != '%') {
177 *s = ch;
178 inc = 1;
179 continue;
180 }
181
182 abrev = 0;
183 fill = '0';
184 width = 2;
185
186 if (*format == 'E' || *format == 'O')
187 format++;
188
189 switch (*format++) {
190 case 'a':
191 abrev = 1;
192 case 'A':
193 inc = sval(s, n, days, abrev, timeptr->tm_wday, 7);
194 break;
195 case 'h':
196 case 'b':
197 abrev = 1;
198 case 'B':
199 inc = sval(s, n, months, abrev, timeptr->tm_mon, 12);
200 break;
201 case 'c':
202 tfmt = "%a %b %e %T %Y";
203 goto recursive;
204 case 'C':
205 val = (timeptr->tm_year + 1900) / 100;
206 goto number;
207 case 'd':
208 val = timeptr->tm_mday;
209 goto number;
210 case 'D':
211 tfmt = "%m/%d/%y";
212 goto recursive;
213 case 'e':
214 fill = ' ';
215 val = timeptr->tm_mday;
216 goto number;
217 case 'F':
218 tfmt = "%Y-%m-%d";
219 goto recursive;
220 case 'g':
221 val = isoyear(timeptr);
222 goto number;
223 case 'G':
224 val = isoyear(timeptr);
225 val += 1900;
226 width = 4;
227 goto number;
228 case 'H':
229 val = timeptr->tm_hour;
230 goto number;
231 case 'I':
232 val = timeptr->tm_hour;
233 if (val == 0)
234 val = 12;
235 if (val > 12)
236 val -= 12;
237 goto number;
238 case 'j':
239 width = 3;
240 val = timeptr->tm_yday+1;
241 goto number;
242 case 'm':
243 val = timeptr->tm_mon+1;
244 goto number;
245 case 'M':
246 val = timeptr->tm_min;
247 goto number;
248 case 'n':
249 val = '\n';
250 goto character;
251 case 'p':
252 inc = sval(s, n, am_pm, 0, timeptr->tm_hour > 12, 2);
253 break;
254 case 'r':
255 tfmt = "%I:%M:%S %p";
256 goto recursive;
257 case 'R':
258 tfmt = "%H:%M";
259 goto recursive;
260 case 'S':
261 val = timeptr->tm_sec;
262 goto number;
263 case 't':
264 val = '\t';
265 goto character;
266 case 'u':
267 width = 1;
268 val = isoday(timeptr);
269 goto number;
270 case 'U':
271 val = weeknum(timeptr, SUN);
272 goto number;
273 case 'V':
274 val = isoweek(timeptr);
275 goto number;
276 case 'w':
277 width = 1;
278 val = timeptr->tm_wday;
279 goto number;
280 case 'W':
281 val = weeknum(timeptr, MON);
282 goto number;
283 case 'X':
284 case 'T':
285 tfmt = "%H:%M:%S";
286 goto recursive;
287 case 'x':
288 tfmt = "%m/%d/%y";
289 goto recursive;
290 case 'y':
291 val = timeptr->tm_year%100;
292 goto number;
293 case 'Y':
294 width = 4;
295 val = 1900 + timeptr->tm_year;
296 goto number;
297 case 'z':
298 inc = timezone(s, n, timeptr);
299 break;
300 case 'Z':
301 inc = strlen(timeptr->tm_zone);
302 if (inc > n)
303 inc = n;
304 memcpy(s, timeptr->tm_zone, inc);
305 break;
306 case '\0':
307 inc = 0;
308 --format;
309 break;
310 case '%':
311 val = '%';
312 character:
313 *s = val;
314 inc = 1;
315 break;
316 number:
317 inc = dval(s, n, width, fill, val);
318 break;
319 recursive:
320 inc = strftime(s, n+1, tfmt, timeptr);
321 break;
322 }
323 }
324
325 n = s - begin;
326 if (n == maxsize)
327 return 0;
328 *s = '\0';
329
330 return n;
331 }