155682Smarkm/*
2102644Snectar * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of KTH nor the names of its contributors may be
1855682Smarkm *    used to endorse or promote products derived from this software without
1955682Smarkm *    specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
2255682Smarkm * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2455682Smarkm * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
2555682Smarkm * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2655682Smarkm * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2755682Smarkm * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2855682Smarkm * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2955682Smarkm * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3055682Smarkm * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3155682Smarkm * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
3255682Smarkm
3355682Smarkm#ifdef HAVE_CONFIG_H
3455682Smarkm#include <config.h>
3555682Smarkm#endif
36178825Sdfr#ifdef TEST_STRPFTIME
37178825Sdfr#include "strpftime-test.h"
38178825Sdfr#endif
3955682Smarkm#include "roken.h"
4055682Smarkm
41178825SdfrRCSID("$Id: strftime.c 21896 2007-08-09 08:46:08Z lha $");
4255682Smarkm
4355682Smarkmstatic const char *abb_weekdays[] = {
4455682Smarkm    "Sun",
4555682Smarkm    "Mon",
4655682Smarkm    "Tue",
4755682Smarkm    "Wed",
4855682Smarkm    "Thu",
4955682Smarkm    "Fri",
5055682Smarkm    "Sat",
5155682Smarkm};
5255682Smarkm
5355682Smarkmstatic const char *full_weekdays[] = {
5455682Smarkm    "Sunday",
5555682Smarkm    "Monday",
5655682Smarkm    "Tuesday",
5755682Smarkm    "Wednesday",
5855682Smarkm    "Thursday",
5955682Smarkm    "Friday",
6055682Smarkm    "Saturday",
6155682Smarkm};
6255682Smarkm
6355682Smarkmstatic const char *abb_month[] = {
6455682Smarkm    "Jan",
6555682Smarkm    "Feb",
6655682Smarkm    "Mar",
6755682Smarkm    "Apr",
6855682Smarkm    "May",
6955682Smarkm    "Jun",
7055682Smarkm    "Jul",
7155682Smarkm    "Aug",
7255682Smarkm    "Sep",
7355682Smarkm    "Oct",
7455682Smarkm    "Nov",
7555682Smarkm    "Dec"
7655682Smarkm};
7755682Smarkm
7855682Smarkmstatic const char *full_month[] = {
7955682Smarkm    "January",
8055682Smarkm    "February",
8155682Smarkm    "Mars",
8255682Smarkm    "April",
8355682Smarkm    "May",
8455682Smarkm    "June",
8555682Smarkm    "July",
8655682Smarkm    "August",
8755682Smarkm    "September",
8855682Smarkm    "October",
8955682Smarkm    "November",
9055682Smarkm    "December"
9155682Smarkm};
9255682Smarkm
9355682Smarkmstatic const char *ampm[] = {
9455682Smarkm    "AM",
9555682Smarkm    "PM"
9655682Smarkm};
9755682Smarkm
9855682Smarkm/*
9955682Smarkm * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
10055682Smarkm */
10155682Smarkm
10255682Smarkmstatic int
10355682Smarkmhour_24to12 (int hour)
10455682Smarkm{
10555682Smarkm    int ret = hour % 12;
10655682Smarkm
10755682Smarkm    if (ret == 0)
10855682Smarkm	ret = 12;
10955682Smarkm    return ret;
11055682Smarkm}
11155682Smarkm
11255682Smarkm/*
11355682Smarkm * Return AM or PM for `hour'
11455682Smarkm */
11555682Smarkm
11655682Smarkmstatic const char *
11755682Smarkmhour_to_ampm (int hour)
11855682Smarkm{
11955682Smarkm    return ampm[hour / 12];
12055682Smarkm}
12155682Smarkm
12255682Smarkm/*
12355682Smarkm * Return the week number of `tm' (Sunday being the first day of the week)
12455682Smarkm * as [0, 53]
12555682Smarkm */
12655682Smarkm
12755682Smarkmstatic int
12855682Smarkmweek_number_sun (const struct tm *tm)
12955682Smarkm{
13055682Smarkm    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
13155682Smarkm}
13255682Smarkm
13355682Smarkm/*
13455682Smarkm * Return the week number of `tm' (Monday being the first day of the week)
13555682Smarkm * as [0, 53]
13655682Smarkm */
13755682Smarkm
13855682Smarkmstatic int
13955682Smarkmweek_number_mon (const struct tm *tm)
14055682Smarkm{
14155682Smarkm    int wday = (tm->tm_wday + 6) % 7;
14255682Smarkm
14355682Smarkm    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
14455682Smarkm}
14555682Smarkm
14655682Smarkm/*
14755682Smarkm * Return the week number of `tm' (Monday being the first day of the
14855682Smarkm * week) as [01, 53].  Week number one is the one that has four or more
14955682Smarkm * days in that year.
15055682Smarkm */
15155682Smarkm
15255682Smarkmstatic int
15355682Smarkmweek_number_mon4 (const struct tm *tm)
15455682Smarkm{
15555682Smarkm    int wday  = (tm->tm_wday + 6) % 7;
15655682Smarkm    int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
15755682Smarkm    int ret;
15855682Smarkm
15955682Smarkm    ret = (tm->tm_yday + w1day) / 7;
16055682Smarkm    if (w1day >= 4)
16155682Smarkm	--ret;
16255682Smarkm    if (ret == -1)
16355682Smarkm	ret = 53;
16455682Smarkm    else
16555682Smarkm	++ret;
16655682Smarkm    return ret;
16755682Smarkm}
16855682Smarkm
16955682Smarkm/*
17055682Smarkm *
17155682Smarkm */
17255682Smarkm
173178825Sdfrsize_t ROKEN_LIB_FUNCTION
17455682Smarkmstrftime (char *buf, size_t maxsize, const char *format,
17555682Smarkm	  const struct tm *tm)
17655682Smarkm{
17755682Smarkm    size_t n = 0;
17890926Snectar    int ret;
17955682Smarkm
18055682Smarkm    while (*format != '\0' && n < maxsize) {
18155682Smarkm	if (*format == '%') {
18255682Smarkm	    ++format;
18355682Smarkm	    if(*format == 'E' || *format == 'O')
18455682Smarkm		++format;
18555682Smarkm	    switch (*format) {
18655682Smarkm	    case 'a' :
18755682Smarkm		ret = snprintf (buf, maxsize - n,
18855682Smarkm				"%s", abb_weekdays[tm->tm_wday]);
18955682Smarkm		break;
19055682Smarkm	    case 'A' :
19155682Smarkm		ret = snprintf (buf, maxsize - n,
19255682Smarkm				"%s", full_weekdays[tm->tm_wday]);
19355682Smarkm		break;
19455682Smarkm	    case 'h' :
19555682Smarkm	    case 'b' :
19655682Smarkm		ret = snprintf (buf, maxsize - n,
19755682Smarkm				"%s", abb_month[tm->tm_mon]);
19855682Smarkm		break;
19955682Smarkm	    case 'B' :
20055682Smarkm		ret = snprintf (buf, maxsize - n,
20155682Smarkm				"%s", full_month[tm->tm_mon]);
20255682Smarkm		break;
20355682Smarkm	    case 'c' :
20455682Smarkm		ret = snprintf (buf, maxsize - n,
20555682Smarkm				"%d:%02d:%02d %02d:%02d:%02d",
20655682Smarkm				tm->tm_year,
20755682Smarkm				tm->tm_mon + 1,
20855682Smarkm				tm->tm_mday,
20955682Smarkm				tm->tm_hour,
21055682Smarkm				tm->tm_min,
21155682Smarkm				tm->tm_sec);
21255682Smarkm		break;
21355682Smarkm	    case 'C' :
21455682Smarkm		ret = snprintf (buf, maxsize - n,
21555682Smarkm				"%02d", (tm->tm_year + 1900) / 100);
21655682Smarkm		break;
21755682Smarkm	    case 'd' :
21855682Smarkm		ret = snprintf (buf, maxsize - n,
21955682Smarkm				"%02d", tm->tm_mday);
22055682Smarkm		break;
22155682Smarkm	    case 'D' :
22255682Smarkm		ret = snprintf (buf, maxsize - n,
22355682Smarkm				"%02d/%02d/%02d",
22455682Smarkm				tm->tm_mon + 1,
22555682Smarkm				tm->tm_mday,
22655682Smarkm				(tm->tm_year + 1900) % 100);
22755682Smarkm		break;
22855682Smarkm	    case 'e' :
22955682Smarkm		ret = snprintf (buf, maxsize - n,
23055682Smarkm				"%2d", tm->tm_mday);
23155682Smarkm		break;
23255682Smarkm	    case 'F':
23355682Smarkm		ret = snprintf (buf, maxsize - n,
23455682Smarkm				"%04d-%02d-%02d", tm->tm_year + 1900,
23555682Smarkm				tm->tm_mon + 1, tm->tm_mday);
23655682Smarkm		break;
23755682Smarkm	    case 'g':
23855682Smarkm		/* last two digits of week-based year */
23955682Smarkm		abort();
24055682Smarkm	    case 'G':
24155682Smarkm		/* week-based year */
24255682Smarkm		abort();
24355682Smarkm	    case 'H' :
24455682Smarkm		ret = snprintf (buf, maxsize - n,
24555682Smarkm				"%02d", tm->tm_hour);
24655682Smarkm		break;
24755682Smarkm	    case 'I' :
24855682Smarkm		ret = snprintf (buf, maxsize - n,
24955682Smarkm				"%02d",
25055682Smarkm				hour_24to12 (tm->tm_hour));
25155682Smarkm		break;
25255682Smarkm	    case 'j' :
25355682Smarkm		ret = snprintf (buf, maxsize - n,
25455682Smarkm				"%03d", tm->tm_yday + 1);
25555682Smarkm		break;
25655682Smarkm	    case 'k' :
25755682Smarkm		ret = snprintf (buf, maxsize - n,
25855682Smarkm				"%2d", tm->tm_hour);
25955682Smarkm		break;
26055682Smarkm	    case 'l' :
26155682Smarkm		ret = snprintf (buf, maxsize - n,
26255682Smarkm				"%2d",
26355682Smarkm				hour_24to12 (tm->tm_hour));
26455682Smarkm		break;
26555682Smarkm	    case 'm' :
26655682Smarkm		ret = snprintf (buf, maxsize - n,
26755682Smarkm				"%02d", tm->tm_mon + 1);
26855682Smarkm		break;
26955682Smarkm	    case 'M' :
27055682Smarkm		ret = snprintf (buf, maxsize - n,
27155682Smarkm				"%02d", tm->tm_min);
27255682Smarkm		break;
27355682Smarkm	    case 'n' :
27455682Smarkm		ret = snprintf (buf, maxsize - n, "\n");
27555682Smarkm		break;
27655682Smarkm	    case 'p' :
27755682Smarkm		ret = snprintf (buf, maxsize - n, "%s",
27855682Smarkm				hour_to_ampm (tm->tm_hour));
27955682Smarkm		break;
28055682Smarkm	    case 'r' :
28155682Smarkm		ret = snprintf (buf, maxsize - n,
28255682Smarkm				"%02d:%02d:%02d %s",
28355682Smarkm				hour_24to12 (tm->tm_hour),
28455682Smarkm				tm->tm_min,
28555682Smarkm				tm->tm_sec,
28655682Smarkm				hour_to_ampm (tm->tm_hour));
28755682Smarkm		break;
28855682Smarkm	    case 'R' :
28955682Smarkm		ret = snprintf (buf, maxsize - n,
29055682Smarkm				"%02d:%02d",
29155682Smarkm				tm->tm_hour,
29255682Smarkm				tm->tm_min);
29355682Smarkm
29455682Smarkm	    case 's' :
29555682Smarkm		ret = snprintf (buf, maxsize - n,
296178825Sdfr				"%d", (int)mktime(rk_UNCONST(tm)));
29755682Smarkm		break;
29855682Smarkm	    case 'S' :
29955682Smarkm		ret = snprintf (buf, maxsize - n,
30055682Smarkm				"%02d", tm->tm_sec);
30155682Smarkm		break;
30255682Smarkm	    case 't' :
30355682Smarkm		ret = snprintf (buf, maxsize - n, "\t");
30455682Smarkm		break;
30555682Smarkm	    case 'T' :
30655682Smarkm	    case 'X' :
30755682Smarkm		ret = snprintf (buf, maxsize - n,
30855682Smarkm				"%02d:%02d:%02d",
30955682Smarkm				tm->tm_hour,
31055682Smarkm				tm->tm_min,
31155682Smarkm				tm->tm_sec);
31255682Smarkm		break;
31355682Smarkm	    case 'u' :
31455682Smarkm		ret = snprintf (buf, maxsize - n,
31555682Smarkm				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
31655682Smarkm		break;
31755682Smarkm	    case 'U' :
31855682Smarkm		ret = snprintf (buf, maxsize - n,
31955682Smarkm				"%02d", week_number_sun (tm));
32055682Smarkm		break;
32155682Smarkm	    case 'V' :
32255682Smarkm		ret = snprintf (buf, maxsize - n,
32355682Smarkm				"%02d", week_number_mon4 (tm));
32455682Smarkm		break;
32555682Smarkm	    case 'w' :
32655682Smarkm		ret = snprintf (buf, maxsize - n,
32755682Smarkm				"%d", tm->tm_wday);
32855682Smarkm		break;
32955682Smarkm	    case 'W' :
33055682Smarkm		ret = snprintf (buf, maxsize - n,
33155682Smarkm				"%02d", week_number_mon (tm));
33255682Smarkm		break;
33355682Smarkm	    case 'x' :
33455682Smarkm		ret = snprintf (buf, maxsize - n,
33555682Smarkm				"%d:%02d:%02d",
33655682Smarkm				tm->tm_year,
33755682Smarkm				tm->tm_mon + 1,
33855682Smarkm				tm->tm_mday);
33955682Smarkm		break;
34055682Smarkm	    case 'y' :
34155682Smarkm		ret = snprintf (buf, maxsize - n,
34255682Smarkm				"%02d", (tm->tm_year + 1900) % 100);
34355682Smarkm		break;
34455682Smarkm	    case 'Y' :
34555682Smarkm		ret = snprintf (buf, maxsize - n,
34655682Smarkm				"%d", tm->tm_year + 1900);
34755682Smarkm		break;
34855682Smarkm	    case 'z':
34955682Smarkm		ret = snprintf (buf, maxsize - n,
35055682Smarkm				"%ld",
35155682Smarkm#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
35255682Smarkm				(long)tm->tm_gmtoff
35355682Smarkm#elif defined(HAVE_TIMEZONE)
354102644Snectar#ifdef HAVE_ALTZONE
35555682Smarkm				tm->tm_isdst ?
35655682Smarkm				(long)altzone :
357102644Snectar#endif
35855682Smarkm				(long)timezone
35955682Smarkm#else
36055682Smarkm#error Where in timezone chaos are you?
36155682Smarkm#endif
36255682Smarkm				);
36355682Smarkm		break;
36455682Smarkm	    case 'Z' :
36555682Smarkm		ret = snprintf (buf, maxsize - n,
36655682Smarkm				"%s",
36755682Smarkm
36855682Smarkm#if defined(HAVE_STRUCT_TM_TM_ZONE)
36955682Smarkm				tm->tm_zone
37055682Smarkm#elif defined(HAVE_TIMEZONE)
37155682Smarkm				tzname[tm->tm_isdst]
37255682Smarkm#else
37355682Smarkm#error what?
37455682Smarkm#endif
37555682Smarkm		    );
37655682Smarkm		break;
37755682Smarkm	    case '\0' :
37855682Smarkm		--format;
37955682Smarkm		/* FALLTHROUGH */
38055682Smarkm	    case '%' :
38155682Smarkm		ret = snprintf (buf, maxsize - n,
38255682Smarkm				"%%");
38355682Smarkm		break;
38455682Smarkm	    default :
38555682Smarkm		ret = snprintf (buf, maxsize - n,
38655682Smarkm				"%%%c", *format);
38755682Smarkm		break;
38855682Smarkm	    }
38990926Snectar	    if (ret < 0 || ret >= maxsize - n)
39055682Smarkm		return 0;
39155682Smarkm	    n   += ret;
39255682Smarkm	    buf += ret;
39355682Smarkm	    ++format;
39455682Smarkm	} else {
39555682Smarkm	    *buf++ = *format++;
39655682Smarkm	    ++n;
39755682Smarkm	}
39855682Smarkm    }
39955682Smarkm    *buf++ = '\0';
40055682Smarkm    return n;
40155682Smarkm}
402