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