strftime.c revision 72445
172087Sbp/*
272087Sbp * Copyright (c) 1999 - 2000 Kungliga Tekniska H�gskolan
372087Sbp * (Royal Institute of Technology, Stockholm, Sweden).
472087Sbp * All rights reserved.
572087Sbp *
672087Sbp * Redistribution and use in source and binary forms, with or without
772087Sbp * modification, are permitted provided that the following conditions
872087Sbp * are met:
972087Sbp *
1072087Sbp * 1. Redistributions of source code must retain the above copyright
1172087Sbp *    notice, this list of conditions and the following disclaimer.
1272087Sbp *
1372087Sbp * 2. Redistributions in binary form must reproduce the above copyright
1472087Sbp *    notice, this list of conditions and the following disclaimer in the
1572087Sbp *    documentation and/or other materials provided with the distribution.
1672087Sbp *
1772087Sbp * 3. Neither the name of KTH nor the names of its contributors may be
1872087Sbp *    used to endorse or promote products derived from this software without
1972087Sbp *    specific prior written permission.
2072087Sbp *
2172087Sbp * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
2272087Sbp * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2372087Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2472087Sbp * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
2572087Sbp * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2672087Sbp * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2772100Sru * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28206622Suqs * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2972087Sbp * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3072087Sbp * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3172087Sbp * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
3272087Sbp
3372087Sbp#ifdef HAVE_CONFIG_H
3472087Sbp#include <config.h>
3572087Sbp#endif
3684306Sru#include "roken.h"
3784306Sru
3872087SbpRCSID("$Id: strftime.c,v 1.11 2000/07/08 14:22:12 assar Exp $");
3986691Sarr
4072087Sbpstatic const char *abb_weekdays[] = {
4172087Sbp    "Sun",
4272087Sbp    "Mon",
4372087Sbp    "Tue",
4472087Sbp    "Wed",
4572087Sbp    "Thu",
4672087Sbp    "Fri",
4772087Sbp    "Sat",
4872087Sbp};
4972087Sbp
5072100Srustatic const char *full_weekdays[] = {
51140931Sru    "Sunday",
5272100Sru    "Monday",
53140931Sru    "Tuesday",
5472087Sbp    "Wednesday",
5572087Sbp    "Thursday",
56236235Sgjb    "Friday",
5772087Sbp    "Saturday",
5872087Sbp};
5972087Sbp
6072087Sbpstatic const char *abb_month[] = {
6172087Sbp    "Jan",
6272087Sbp    "Feb",
6372087Sbp    "Mar",
64107788Sru    "Apr",
6572087Sbp    "May",
66107788Sru    "Jun",
6772087Sbp    "Jul",
6872087Sbp    "Aug",
6972087Sbp    "Sep",
7072087Sbp    "Oct",
7172087Sbp    "Nov",
7272087Sbp    "Dec"
7372087Sbp};
7472087Sbp
7572087Sbpstatic const char *full_month[] = {
7672087Sbp    "January",
77107788Sru    "February",
7872087Sbp    "Mars",
7972087Sbp    "April",
8072087Sbp    "May",
8172087Sbp    "June",
8272087Sbp    "July",
8372087Sbp    "August",
8472087Sbp    "September",
8572087Sbp    "October",
8672087Sbp    "November",
8772087Sbp    "December"
8872087Sbp};
8972100Sru
9072087Sbpstatic const char *ampm[] = {
9172087Sbp    "AM",
9272087Sbp    "PM"
9372087Sbp};
9472087Sbp
9572087Sbp/*
9672087Sbp * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
9772087Sbp */
9872087Sbp
9972087Sbpstatic int
10072087Sbphour_24to12 (int hour)
10172087Sbp{
10272087Sbp    int ret = hour % 12;
10372087Sbp
104107788Sru    if (ret == 0)
10572100Sru	ret = 12;
10672100Sru    return ret;
10772087Sbp}
10879366Sru
10972087Sbp/*
11079366Sru * Return AM or PM for `hour'
11172087Sbp */
112147647Shmp
11372087Sbpstatic const char *
114hour_to_ampm (int hour)
115{
116    return ampm[hour / 12];
117}
118
119/*
120 * Return the week number of `tm' (Sunday being the first day of the week)
121 * as [0, 53]
122 */
123
124static int
125week_number_sun (const struct tm *tm)
126{
127    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
128}
129
130/*
131 * Return the week number of `tm' (Monday being the first day of the week)
132 * as [0, 53]
133 */
134
135static int
136week_number_mon (const struct tm *tm)
137{
138    int wday = (tm->tm_wday + 6) % 7;
139
140    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
141}
142
143/*
144 * Return the week number of `tm' (Monday being the first day of the
145 * week) as [01, 53].  Week number one is the one that has four or more
146 * days in that year.
147 */
148
149static int
150week_number_mon4 (const struct tm *tm)
151{
152    int wday  = (tm->tm_wday + 6) % 7;
153    int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
154    int ret;
155
156    ret = (tm->tm_yday + w1day) / 7;
157    if (w1day >= 4)
158	--ret;
159    if (ret == -1)
160	ret = 53;
161    else
162	++ret;
163    return ret;
164}
165
166/*
167 *
168 */
169
170size_t
171strftime (char *buf, size_t maxsize, const char *format,
172	  const struct tm *tm)
173{
174    size_t n = 0;
175    size_t ret;
176
177    while (*format != '\0' && n < maxsize) {
178	if (*format == '%') {
179	    ++format;
180	    if(*format == 'E' || *format == 'O')
181		++format;
182	    switch (*format) {
183	    case 'a' :
184		ret = snprintf (buf, maxsize - n,
185				"%s", abb_weekdays[tm->tm_wday]);
186		break;
187	    case 'A' :
188		ret = snprintf (buf, maxsize - n,
189				"%s", full_weekdays[tm->tm_wday]);
190		break;
191	    case 'h' :
192	    case 'b' :
193		ret = snprintf (buf, maxsize - n,
194				"%s", abb_month[tm->tm_mon]);
195		break;
196	    case 'B' :
197		ret = snprintf (buf, maxsize - n,
198				"%s", full_month[tm->tm_mon]);
199		break;
200	    case 'c' :
201		ret = snprintf (buf, maxsize - n,
202				"%d:%02d:%02d %02d:%02d:%02d",
203				tm->tm_year,
204				tm->tm_mon + 1,
205				tm->tm_mday,
206				tm->tm_hour,
207				tm->tm_min,
208				tm->tm_sec);
209		break;
210	    case 'C' :
211		ret = snprintf (buf, maxsize - n,
212				"%02d", (tm->tm_year + 1900) / 100);
213		break;
214	    case 'd' :
215		ret = snprintf (buf, maxsize - n,
216				"%02d", tm->tm_mday);
217		break;
218	    case 'D' :
219		ret = snprintf (buf, maxsize - n,
220				"%02d/%02d/%02d",
221				tm->tm_mon + 1,
222				tm->tm_mday,
223				(tm->tm_year + 1900) % 100);
224		break;
225	    case 'e' :
226		ret = snprintf (buf, maxsize - n,
227				"%2d", tm->tm_mday);
228		break;
229	    case 'F':
230		ret = snprintf (buf, maxsize - n,
231				"%04d-%02d-%02d", tm->tm_year + 1900,
232				tm->tm_mon + 1, tm->tm_mday);
233		break;
234	    case 'g':
235		/* last two digits of week-based year */
236		abort();
237	    case 'G':
238		/* week-based year */
239		abort();
240	    case 'H' :
241		ret = snprintf (buf, maxsize - n,
242				"%02d", tm->tm_hour);
243		break;
244	    case 'I' :
245		ret = snprintf (buf, maxsize - n,
246				"%02d",
247				hour_24to12 (tm->tm_hour));
248		break;
249	    case 'j' :
250		ret = snprintf (buf, maxsize - n,
251				"%03d", tm->tm_yday + 1);
252		break;
253	    case 'k' :
254		ret = snprintf (buf, maxsize - n,
255				"%2d", tm->tm_hour);
256		break;
257	    case 'l' :
258		ret = snprintf (buf, maxsize - n,
259				"%2d",
260				hour_24to12 (tm->tm_hour));
261		break;
262	    case 'm' :
263		ret = snprintf (buf, maxsize - n,
264				"%02d", tm->tm_mon + 1);
265		break;
266	    case 'M' :
267		ret = snprintf (buf, maxsize - n,
268				"%02d", tm->tm_min);
269		break;
270	    case 'n' :
271		ret = snprintf (buf, maxsize - n, "\n");
272		break;
273	    case 'p' :
274		ret = snprintf (buf, maxsize - n, "%s",
275				hour_to_ampm (tm->tm_hour));
276		break;
277	    case 'r' :
278		ret = snprintf (buf, maxsize - n,
279				"%02d:%02d:%02d %s",
280				hour_24to12 (tm->tm_hour),
281				tm->tm_min,
282				tm->tm_sec,
283				hour_to_ampm (tm->tm_hour));
284		break;
285	    case 'R' :
286		ret = snprintf (buf, maxsize - n,
287				"%02d:%02d",
288				tm->tm_hour,
289				tm->tm_min);
290
291	    case 's' :
292		ret = snprintf (buf, maxsize - n,
293				"%d", (int)mktime((struct tm *)tm));
294		break;
295	    case 'S' :
296		ret = snprintf (buf, maxsize - n,
297				"%02d", tm->tm_sec);
298		break;
299	    case 't' :
300		ret = snprintf (buf, maxsize - n, "\t");
301		break;
302	    case 'T' :
303	    case 'X' :
304		ret = snprintf (buf, maxsize - n,
305				"%02d:%02d:%02d",
306				tm->tm_hour,
307				tm->tm_min,
308				tm->tm_sec);
309		break;
310	    case 'u' :
311		ret = snprintf (buf, maxsize - n,
312				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
313		break;
314	    case 'U' :
315		ret = snprintf (buf, maxsize - n,
316				"%02d", week_number_sun (tm));
317		break;
318	    case 'V' :
319		ret = snprintf (buf, maxsize - n,
320				"%02d", week_number_mon4 (tm));
321		break;
322	    case 'w' :
323		ret = snprintf (buf, maxsize - n,
324				"%d", tm->tm_wday);
325		break;
326	    case 'W' :
327		ret = snprintf (buf, maxsize - n,
328				"%02d", week_number_mon (tm));
329		break;
330	    case 'x' :
331		ret = snprintf (buf, maxsize - n,
332				"%d:%02d:%02d",
333				tm->tm_year,
334				tm->tm_mon + 1,
335				tm->tm_mday);
336		break;
337	    case 'y' :
338		ret = snprintf (buf, maxsize - n,
339				"%02d", (tm->tm_year + 1900) % 100);
340		break;
341	    case 'Y' :
342		ret = snprintf (buf, maxsize - n,
343				"%d", tm->tm_year + 1900);
344		break;
345	    case 'z':
346		ret = snprintf (buf, maxsize - n,
347				"%ld",
348#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
349				(long)tm->tm_gmtoff
350#elif defined(HAVE_TIMEZONE)
351				tm->tm_isdst ?
352				(long)altzone :
353				(long)timezone
354#else
355#error Where in timezone chaos are you?
356#endif
357				);
358		break;
359	    case 'Z' :
360		ret = snprintf (buf, maxsize - n,
361				"%s",
362
363#if defined(HAVE_STRUCT_TM_TM_ZONE)
364				tm->tm_zone
365#elif defined(HAVE_TIMEZONE)
366				tzname[tm->tm_isdst]
367#else
368#error what?
369#endif
370		    );
371		break;
372	    case '\0' :
373		--format;
374		/* FALLTHROUGH */
375	    case '%' :
376		ret = snprintf (buf, maxsize - n,
377				"%%");
378		break;
379	    default :
380		ret = snprintf (buf, maxsize - n,
381				"%%%c", *format);
382		break;
383	    }
384	    if (ret >= maxsize - n)
385		return 0;
386	    n   += ret;
387	    buf += ret;
388	    ++format;
389	} else {
390	    *buf++ = *format++;
391	    ++n;
392	}
393    }
394    *buf++ = '\0';
395    return n;
396}
397