1 /**
2 *
3 * strftime.c
4 *
5 * implements the ansi c function strftime()
6 *
7 * written 6 september 1989 by jim nutt
8 * released into the public domain by jim nutt
9 *
10 * modified 21-Oct-89 by Rob Duff
11 *
12 * modified 08-Dec-04 by Tobi Oetiker (added %V)
13 **/
15 #include <stddef.h> /* for size_t */
16 #include <stdarg.h> /* for va_arg */
17 #include <time.h> /* for struct tm */
18 #include "strftime.h"
20 /* Define your own defaults in config.h if necessary */
21 #if defined(TZNAME_STD) && defined(TZNAME_DST)
22 char *tzname_[2] = { TZNAME_STD, TZNAME_DST };
23 #else
24 #define tzname_ tzname
25 #endif
27 static char *aday[] = {
28 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
29 };
31 static char *day[] = {
32 "Sunday", "Monday", "Tuesday", "Wednesday",
33 "Thursday", "Friday", "Saturday"
34 };
36 static char *amonth[] = {
37 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
38 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
39 };
41 static char *month[] = {
42 "January", "February", "March", "April", "May", "June",
43 "July", "August", "September", "October", "November", "December"
44 };
46 static char buf[26];
48 static void strfmt(
49 char *str,
50 const char *fmt,
51 ...);
53 /**
54 *
55 * size_t strftime_(char *str,
56 * size_t maxs,
57 * const char *fmt,
58 * const struct tm *t)
59 *
60 * this functions acts much like a sprintf for time/date output.
61 * given a pointer to an output buffer, a format string and a
62 * time, it copies the time to the output buffer formatted in
63 * accordance with the format string. the parameters are used
64 * as follows:
65 *
66 * str is a pointer to the output buffer, there should
67 * be at least maxs characters available at the address
68 * pointed to by str.
69 *
70 * maxs is the maximum number of characters to be copied
71 * into the output buffer, included the '\0' terminator
72 *
73 * fmt is the format string. a percent sign (%) is used
74 * to indicate that the following character is a special
75 * format character. the following are valid format
76 * characters:
77 *
78 * %A full weekday name (Monday)
79 * %a abbreviated weekday name (Mon)
80 * %B full month name (January)
81 * %b abbreviated month name (Jan)
82 * %c standard date and time representation
83 * %d day-of-month (01-31)
84 * %H hour (24 hour clock) (00-23)
85 * %I hour (12 hour clock) (01-12)
86 * %j day-of-year (001-366)
87 * %M minute (00-59)
88 * %m month (01-12)
89 * %p local equivalent of AM or PM
90 * %S second (00-59)
91 * %U week-of-year, first day sunday (00-53)
92 * %W week-of-year, first day monday (00-53)
93 * %V ISO 8601 Week number
94 * %w weekday (0-6, sunday is 0)
95 * %X standard time representation
96 * %x standard date representation
97 * %Y year with century
98 * %y year without century (00-99)
99 * %Z timezone name
100 * %% percent sign
101 *
102 * the standard date string is equivalent to:
103 *
104 * %a %b %d %Y
105 *
106 * the standard time string is equivalent to:
107 *
108 * %H:%M:%S
109 *
110 * the standard date and time string is equivalent to:
111 *
112 * %a %b %d %H:%M:%S %Y
113 *
114 * strftime_() returns the number of characters placed in the
115 * buffer, not including the terminating \0, or zero if more
116 * than maxs characters were produced.
117 *
118 **/
120 size_t strftime_(
121 char *s,
122 size_t maxs,
123 const char *f,
124 const struct tm *t)
125 {
126 int w, d;
127 char *p, *q, *r;
129 p = s;
130 q = s + maxs - 1;
131 while ((*f != '\0')) {
132 if (*f++ == '%') {
133 r = buf;
134 switch (*f++) {
135 case '%':
136 r = "%";
137 break;
139 case 'a':
140 r = aday[t->tm_wday];
141 break;
143 case 'A':
144 r = day[t->tm_wday];
145 break;
147 case 'b':
148 r = amonth[t->tm_mon];
149 break;
151 case 'B':
152 r = month[t->tm_mon];
153 break;
155 case 'c':
156 strfmt(r, "%0 %0 %2 %2:%2:%2 %4",
157 aday[t->tm_wday], amonth[t->tm_mon],
158 t->tm_mday, t->tm_hour, t->tm_min,
159 t->tm_sec, t->tm_year + 1900);
160 break;
162 case 'd':
163 strfmt(r, "%2", t->tm_mday);
164 break;
166 case 'H':
167 strfmt(r, "%2", t->tm_hour);
168 break;
170 case 'I':
171 strfmt(r, "%2", (t->tm_hour % 12) ? t->tm_hour % 12 : 12);
172 break;
174 case 'j':
175 strfmt(r, "%3", t->tm_yday + 1);
176 break;
178 case 'm':
179 strfmt(r, "%2", t->tm_mon + 1);
180 break;
182 case 'M':
183 strfmt(r, "%2", t->tm_min);
184 break;
186 case 'p':
187 r = (t->tm_hour > 11) ? "PM" : "AM";
188 break;
190 case 'S':
191 strfmt(r, "%2", t->tm_sec);
192 break;
194 case 'U':
195 w = t->tm_yday / 7;
196 if (t->tm_yday % 7 > t->tm_wday)
197 w++;
198 strfmt(r, "%2", w);
199 break;
201 case 'W':
202 w = t->tm_yday / 7;
203 if (t->tm_yday % 7 > (t->tm_wday + 6) % 7)
204 w++;
205 strfmt(r, "%2", w);
206 break;
208 case 'V':
210 /* ISO 8601 Week Of Year:
211 If the week (Monday - Sunday) containing January 1 has four or more
212 days in the new year, then it is week 1; otherwise it is week 53 of
213 the previous year and the next week is week one. */
215 w = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) / 7;
216 d = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) % 7;
218 if (d >= 4) {
219 w++;
220 } else if (w == 0) {
221 w = 53;
222 }
223 strfmt(r, "%2", w);
224 break;
226 case 'w':
227 strfmt(r, "%1", t->tm_wday);
228 break;
230 case 'x':
231 strfmt(r, "%3s %3s %2 %4", aday[t->tm_wday],
232 amonth[t->tm_mon], t->tm_mday, t->tm_year + 1900);
233 break;
235 case 'X':
236 strfmt(r, "%2:%2:%2", t->tm_hour, t->tm_min, t->tm_sec);
237 break;
239 case 'y':
240 strfmt(r, "%2", t->tm_year % 100);
241 break;
243 case 'Y':
244 strfmt(r, "%4", t->tm_year + 1900);
245 break;
247 case 'Z':
248 r = (t->tm_isdst && tzname_[1][0]) ? tzname_[1] : tzname_[0];
249 break;
251 default:
252 buf[0] = '%'; /* reconstruct the format */
253 buf[1] = f[-1];
254 buf[2] = '\0';
255 if (buf[1] == 0)
256 f--; /* back up if at end of string */
257 }
258 while (*r) {
259 if (p == q) {
260 *q = '\0';
261 return 0;
262 }
263 *p++ = *r++;
264 }
265 } else {
266 if (p == q) {
267 *q = '\0';
268 return 0;
269 }
270 *p++ = f[-1];
271 }
272 }
273 *p = '\0';
274 return p - s;
275 }
277 /*
278 * stdarg.h
279 *
280 typedef void *va_list;
281 #define va_start(vp,v) (vp=((char*)&v)+sizeof(v))
282 #define va_arg(vp,t) (*((t*)(vp))++)
283 #define va_end(vp)
284 *
285 */
287 static int powers[5] = { 1, 10, 100, 1000, 10000 };
289 /**
290 * static void strfmt(char *str, char *fmt);
291 *
292 * simple sprintf for strftime
293 *
294 * each format descriptor is of the form %n
295 * where n goes from zero to four
296 *
297 * 0 -- string %s
298 * 1..4 -- int %?.?d
299 *
300 **/
302 static void strfmt(
303 char *str,
304 const char *fmt,
305 ...)
306 {
307 int ival, ilen;
308 char *sval;
309 va_list vp;
311 va_start(vp, fmt);
312 while (*fmt) {
313 if (*fmt++ == '%') {
314 ilen = *fmt++ - '0';
315 if (ilen == 0) { /* zero means string arg */
316 sval = va_arg(vp, char *);
318 while (*sval)
319 *str++ = *sval++;
320 } else { /* always leading zeros */
322 ival = va_arg(vp, int);
324 while (ilen) {
325 ival %= powers[ilen--];
326 *str++ = (char) ('0' + ival / powers[ilen]);
327 }
328 }
329 } else
330 *str++ = fmt[-1];
331 }
332 *str = '\0';
333 va_end(vp);
334 }
336 #ifdef TEST
338 #include <stdio.h> /* for printf */
339 #include <time.h> /* for strftime */
341 char test[80];
343 int main(
344 int argc,
345 char *argv[])
346 {
347 int len;
348 char *fmt;
349 time_t now;
351 time(&now);
353 fmt = (argc == 1) ? "%I:%M %p\n%c\n" : argv[1];
354 len = strftime_(test, sizeof test, fmt, localtime(&now));
355 printf("%d: %s\n", len, test);
356 return !len;
357 }
359 #endif /* TEST */