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(char *str, const char *fmt, ...);
50 /**
51 *
52 * size_t strftime_(char *str,
53 * size_t maxs,
54 * const char *fmt,
55 * const struct tm *t)
56 *
57 * this functions acts much like a sprintf for time/date output.
58 * given a pointer to an output buffer, a format string and a
59 * time, it copies the time to the output buffer formatted in
60 * accordance with the format string. the parameters are used
61 * as follows:
62 *
63 * str is a pointer to the output buffer, there should
64 * be at least maxs characters available at the address
65 * pointed to by str.
66 *
67 * maxs is the maximum number of characters to be copied
68 * into the output buffer, included the '\0' terminator
69 *
70 * fmt is the format string. a percent sign (%) is used
71 * to indicate that the following character is a special
72 * format character. the following are valid format
73 * characters:
74 *
75 * %A full weekday name (Monday)
76 * %a abbreviated weekday name (Mon)
77 * %B full month name (January)
78 * %b abbreviated month name (Jan)
79 * %c standard date and time representation
80 * %d day-of-month (01-31)
81 * %H hour (24 hour clock) (00-23)
82 * %I hour (12 hour clock) (01-12)
83 * %j day-of-year (001-366)
84 * %M minute (00-59)
85 * %m month (01-12)
86 * %p local equivalent of AM or PM
87 * %S second (00-59)
88 * %U week-of-year, first day sunday (00-53)
89 * %W week-of-year, first day monday (00-53)
90 * %V ISO 8601 Week number
91 * %w weekday (0-6, sunday is 0)
92 * %X standard time representation
93 * %x standard date representation
94 * %Y year with century
95 * %y year without century (00-99)
96 * %Z timezone name
97 * %% percent sign
98 *
99 * the standard date string is equivalent to:
100 *
101 * %a %b %d %Y
102 *
103 * the standard time string is equivalent to:
104 *
105 * %H:%M:%S
106 *
107 * the standard date and time string is equivalent to:
108 *
109 * %a %b %d %H:%M:%S %Y
110 *
111 * strftime_() returns the number of characters placed in the
112 * buffer, not including the terminating \0, or zero if more
113 * than maxs characters were produced.
114 *
115 **/
117 size_t strftime_(char *s, size_t maxs, const char *f, const struct tm *t)
118 {
119 int w,d;
120 char *p, *q, *r;
122 p = s;
123 q = s + maxs - 1;
124 while ((*f != '\0'))
125 {
126 if (*f++ == '%')
127 {
128 r = buf;
129 switch (*f++)
130 {
131 case '%' :
132 r = "%";
133 break;
135 case 'a' :
136 r = aday[t->tm_wday];
137 break;
139 case 'A' :
140 r = day[t->tm_wday];
141 break;
143 case 'b' :
144 r = amonth[t->tm_mon];
145 break;
147 case 'B' :
148 r = month[t->tm_mon];
149 break;
151 case 'c' :
152 strfmt(r, "%0 %0 %2 %2:%2:%2 %4",
153 aday[t->tm_wday], amonth[t->tm_mon],
154 t->tm_mday,t->tm_hour, t->tm_min,
155 t->tm_sec, t->tm_year+1900);
156 break;
158 case 'd' :
159 strfmt(r,"%2",t->tm_mday);
160 break;
162 case 'H' :
163 strfmt(r,"%2",t->tm_hour);
164 break;
166 case 'I' :
167 strfmt(r,"%2",(t->tm_hour%12)?t->tm_hour%12:12);
168 break;
170 case 'j' :
171 strfmt(r,"%3",t->tm_yday+1);
172 break;
174 case 'm' :
175 strfmt(r,"%2",t->tm_mon+1);
176 break;
178 case 'M' :
179 strfmt(r,"%2",t->tm_min);
180 break;
182 case 'p' :
183 r = (t->tm_hour>11)?"PM":"AM";
184 break;
186 case 'S' :
187 strfmt(r,"%2",t->tm_sec);
188 break;
190 case 'U' :
191 w = t->tm_yday/7;
192 if (t->tm_yday%7 > t->tm_wday)
193 w++;
194 strfmt(r, "%2", w);
195 break;
197 case 'W' :
198 w = t->tm_yday/7;
199 if (t->tm_yday%7 > (t->tm_wday+6)%7)
200 w++;
201 strfmt(r, "%2", w);
202 break;
204 case 'V':
206 /* ISO 8601 Week Of Year:
207 If the week (Monday - Sunday) containing January 1 has four or more
208 days in the new year, then it is week 1; otherwise it is week 53 of
209 the previous year and the next week is week one. */
211 w = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) / 7;
212 d = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) % 7;
214 if (d >= 4) { w++; } else if (w == 0) { w = 53; }
215 strfmt(r, "%2", w);
216 break;
218 case 'w' :
219 strfmt(r,"%1",t->tm_wday);
220 break;
222 case 'x' :
223 strfmt(r, "%3s %3s %2 %4", aday[t->tm_wday],
224 amonth[t->tm_mon], t->tm_mday, t->tm_year+1900);
225 break;
227 case 'X' :
228 strfmt(r, "%2:%2:%2", t->tm_hour,
229 t->tm_min, t->tm_sec);
230 break;
232 case 'y' :
233 strfmt(r,"%2",t->tm_year%100);
234 break;
236 case 'Y' :
237 strfmt(r,"%4",t->tm_year+1900);
238 break;
240 case 'Z' :
241 r = (t->tm_isdst && tzname_[1][0]) ?
242 tzname_[1] : tzname_[0];
243 break;
245 default:
246 buf[0] = '%'; /* reconstruct the format */
247 buf[1] = f[-1];
248 buf[2] = '\0';
249 if (buf[1] == 0)
250 f--; /* back up if at end of string */
251 }
252 while (*r)
253 {
254 if (p == q)
255 {
256 *q = '\0';
257 return 0;
258 }
259 *p++ = *r++;
260 }
261 }
262 else
263 {
264 if (p == q)
265 {
266 *q = '\0';
267 return 0;
268 }
269 *p++ = f[-1];
270 }
271 }
272 *p = '\0';
273 return p - s;
274 }
276 /*
277 * stdarg.h
278 *
279 typedef void *va_list;
280 #define va_start(vp,v) (vp=((char*)&v)+sizeof(v))
281 #define va_arg(vp,t) (*((t*)(vp))++)
282 #define va_end(vp)
283 *
284 */
286 static int powers[5] = { 1, 10, 100, 1000, 10000 };
288 /**
289 * static void strfmt(char *str, char *fmt);
290 *
291 * simple sprintf for strftime
292 *
293 * each format descriptor is of the form %n
294 * where n goes from zero to four
295 *
296 * 0 -- string %s
297 * 1..4 -- int %?.?d
298 *
299 **/
301 static void strfmt(char *str, const char *fmt, ...)
302 {
303 int ival, ilen;
304 char *sval;
305 va_list vp;
307 va_start(vp, fmt);
308 while (*fmt)
309 {
310 if (*fmt++ == '%')
311 {
312 ilen = *fmt++ - '0';
313 if (ilen == 0) /* zero means string arg */
314 {
315 sval = va_arg(vp, char*);
316 while (*sval)
317 *str++ = *sval++;
318 }
319 else /* always leading zeros */
320 {
321 ival = va_arg(vp, int);
322 while (ilen)
323 {
324 ival %= powers[ilen--];
325 *str++ = (char)('0' + ival / powers[ilen]);
326 }
327 }
328 }
329 else *str++ = fmt[-1];
330 }
331 *str = '\0';
332 va_end(vp);
333 }
335 #ifdef TEST
337 #include <stdio.h> /* for printf */
338 #include <time.h> /* for strftime */
340 char test[80];
342 int main(int argc, char *argv[])
343 {
344 int len;
345 char *fmt;
346 time_t now;
348 time(&now);
350 fmt = (argc == 1) ? "%I:%M %p\n%c\n" : argv[1];
351 len = strftime_(test,sizeof test, fmt, localtime(&now));
352 printf("%d: %s\n", len, test);
353 return !len;
354 }
356 #endif /* TEST */