155682Smarkm/*
2233294Sstas * Copyright (c) 1999 - 2002 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    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#include <config.h>
34233294Sstas#include "roken.h"
35178825Sdfr#ifdef TEST_STRPFTIME
36178825Sdfr#include "strpftime-test.h"
37178825Sdfr#endif
3855682Smarkm
3955682Smarkmstatic const char *abb_weekdays[] = {
4055682Smarkm    "Sun",
4155682Smarkm    "Mon",
4255682Smarkm    "Tue",
4355682Smarkm    "Wed",
4455682Smarkm    "Thu",
4555682Smarkm    "Fri",
4655682Smarkm    "Sat",
4755682Smarkm};
4855682Smarkm
4955682Smarkmstatic const char *full_weekdays[] = {
5055682Smarkm    "Sunday",
5155682Smarkm    "Monday",
5255682Smarkm    "Tuesday",
5355682Smarkm    "Wednesday",
5455682Smarkm    "Thursday",
5555682Smarkm    "Friday",
5655682Smarkm    "Saturday",
5755682Smarkm};
5855682Smarkm
5955682Smarkmstatic const char *abb_month[] = {
6055682Smarkm    "Jan",
6155682Smarkm    "Feb",
6255682Smarkm    "Mar",
6355682Smarkm    "Apr",
6455682Smarkm    "May",
6555682Smarkm    "Jun",
6655682Smarkm    "Jul",
6755682Smarkm    "Aug",
6855682Smarkm    "Sep",
6955682Smarkm    "Oct",
7055682Smarkm    "Nov",
7155682Smarkm    "Dec"
7255682Smarkm};
7355682Smarkm
7455682Smarkmstatic const char *full_month[] = {
7555682Smarkm    "January",
7655682Smarkm    "February",
7755682Smarkm    "Mars",
7855682Smarkm    "April",
7955682Smarkm    "May",
8055682Smarkm    "June",
8155682Smarkm    "July",
8255682Smarkm    "August",
8355682Smarkm    "September",
8455682Smarkm    "October",
8555682Smarkm    "November",
8655682Smarkm    "December"
8755682Smarkm};
8855682Smarkm
8955682Smarkmstatic const char *ampm[] = {
9055682Smarkm    "AM",
9155682Smarkm    "PM"
9255682Smarkm};
9355682Smarkm
9455682Smarkm/*
9555682Smarkm * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12]
9655682Smarkm */
9755682Smarkm
9855682Smarkmstatic int
9955682Smarkmhour_24to12 (int hour)
10055682Smarkm{
10155682Smarkm    int ret = hour % 12;
10255682Smarkm
10355682Smarkm    if (ret == 0)
10455682Smarkm	ret = 12;
10555682Smarkm    return ret;
10655682Smarkm}
10755682Smarkm
10855682Smarkm/*
10955682Smarkm * Return AM or PM for `hour'
11055682Smarkm */
11155682Smarkm
11255682Smarkmstatic const char *
11355682Smarkmhour_to_ampm (int hour)
11455682Smarkm{
11555682Smarkm    return ampm[hour / 12];
11655682Smarkm}
11755682Smarkm
11855682Smarkm/*
11955682Smarkm * Return the week number of `tm' (Sunday being the first day of the week)
12055682Smarkm * as [0, 53]
12155682Smarkm */
12255682Smarkm
12355682Smarkmstatic int
12455682Smarkmweek_number_sun (const struct tm *tm)
12555682Smarkm{
12655682Smarkm    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7;
12755682Smarkm}
12855682Smarkm
12955682Smarkm/*
13055682Smarkm * Return the week number of `tm' (Monday being the first day of the week)
13155682Smarkm * as [0, 53]
13255682Smarkm */
13355682Smarkm
13455682Smarkmstatic int
13555682Smarkmweek_number_mon (const struct tm *tm)
13655682Smarkm{
13755682Smarkm    int wday = (tm->tm_wday + 6) % 7;
13855682Smarkm
13955682Smarkm    return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7;
14055682Smarkm}
14155682Smarkm
14255682Smarkm/*
14355682Smarkm * Return the week number of `tm' (Monday being the first day of the
14455682Smarkm * week) as [01, 53].  Week number one is the one that has four or more
14555682Smarkm * days in that year.
14655682Smarkm */
14755682Smarkm
14855682Smarkmstatic int
14955682Smarkmweek_number_mon4 (const struct tm *tm)
15055682Smarkm{
15155682Smarkm    int wday  = (tm->tm_wday + 6) % 7;
15255682Smarkm    int w1day = (wday - tm->tm_yday % 7 + 7) % 7;
15355682Smarkm    int ret;
154233294Sstas
15555682Smarkm    ret = (tm->tm_yday + w1day) / 7;
15655682Smarkm    if (w1day >= 4)
15755682Smarkm	--ret;
15855682Smarkm    if (ret == -1)
15955682Smarkm	ret = 53;
16055682Smarkm    else
16155682Smarkm	++ret;
16255682Smarkm    return ret;
16355682Smarkm}
16455682Smarkm
16555682Smarkm/*
16655682Smarkm *
16755682Smarkm */
16855682Smarkm
169233294SstasROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
17055682Smarkmstrftime (char *buf, size_t maxsize, const char *format,
17155682Smarkm	  const struct tm *tm)
17255682Smarkm{
17355682Smarkm    size_t n = 0;
17490926Snectar    int ret;
17555682Smarkm
17655682Smarkm    while (*format != '\0' && n < maxsize) {
17755682Smarkm	if (*format == '%') {
17855682Smarkm	    ++format;
17955682Smarkm	    if(*format == 'E' || *format == 'O')
18055682Smarkm		++format;
18155682Smarkm	    switch (*format) {
18255682Smarkm	    case 'a' :
18355682Smarkm		ret = snprintf (buf, maxsize - n,
18455682Smarkm				"%s", abb_weekdays[tm->tm_wday]);
18555682Smarkm		break;
18655682Smarkm	    case 'A' :
18755682Smarkm		ret = snprintf (buf, maxsize - n,
18855682Smarkm				"%s", full_weekdays[tm->tm_wday]);
18955682Smarkm		break;
19055682Smarkm	    case 'h' :
19155682Smarkm	    case 'b' :
19255682Smarkm		ret = snprintf (buf, maxsize - n,
19355682Smarkm				"%s", abb_month[tm->tm_mon]);
19455682Smarkm		break;
19555682Smarkm	    case 'B' :
19655682Smarkm		ret = snprintf (buf, maxsize - n,
19755682Smarkm				"%s", full_month[tm->tm_mon]);
19855682Smarkm		break;
19955682Smarkm	    case 'c' :
20055682Smarkm		ret = snprintf (buf, maxsize - n,
20155682Smarkm				"%d:%02d:%02d %02d:%02d:%02d",
20255682Smarkm				tm->tm_year,
20355682Smarkm				tm->tm_mon + 1,
20455682Smarkm				tm->tm_mday,
20555682Smarkm				tm->tm_hour,
20655682Smarkm				tm->tm_min,
20755682Smarkm				tm->tm_sec);
20855682Smarkm		break;
20955682Smarkm	    case 'C' :
21055682Smarkm		ret = snprintf (buf, maxsize - n,
21155682Smarkm				"%02d", (tm->tm_year + 1900) / 100);
21255682Smarkm		break;
21355682Smarkm	    case 'd' :
21455682Smarkm		ret = snprintf (buf, maxsize - n,
21555682Smarkm				"%02d", tm->tm_mday);
21655682Smarkm		break;
21755682Smarkm	    case 'D' :
21855682Smarkm		ret = snprintf (buf, maxsize - n,
21955682Smarkm				"%02d/%02d/%02d",
22055682Smarkm				tm->tm_mon + 1,
22155682Smarkm				tm->tm_mday,
22255682Smarkm				(tm->tm_year + 1900) % 100);
22355682Smarkm		break;
22455682Smarkm	    case 'e' :
22555682Smarkm		ret = snprintf (buf, maxsize - n,
22655682Smarkm				"%2d", tm->tm_mday);
22755682Smarkm		break;
22855682Smarkm	    case 'F':
22955682Smarkm		ret = snprintf (buf, maxsize - n,
23055682Smarkm				"%04d-%02d-%02d", tm->tm_year + 1900,
23155682Smarkm				tm->tm_mon + 1, tm->tm_mday);
23255682Smarkm		break;
23355682Smarkm	    case 'g':
23455682Smarkm		/* last two digits of week-based year */
23555682Smarkm		abort();
23655682Smarkm	    case 'G':
23755682Smarkm		/* week-based year */
23855682Smarkm		abort();
23955682Smarkm	    case 'H' :
24055682Smarkm		ret = snprintf (buf, maxsize - n,
24155682Smarkm				"%02d", tm->tm_hour);
24255682Smarkm		break;
24355682Smarkm	    case 'I' :
24455682Smarkm		ret = snprintf (buf, maxsize - n,
24555682Smarkm				"%02d",
24655682Smarkm				hour_24to12 (tm->tm_hour));
24755682Smarkm		break;
24855682Smarkm	    case 'j' :
24955682Smarkm		ret = snprintf (buf, maxsize - n,
25055682Smarkm				"%03d", tm->tm_yday + 1);
25155682Smarkm		break;
25255682Smarkm	    case 'k' :
25355682Smarkm		ret = snprintf (buf, maxsize - n,
25455682Smarkm				"%2d", tm->tm_hour);
25555682Smarkm		break;
25655682Smarkm	    case 'l' :
25755682Smarkm		ret = snprintf (buf, maxsize - n,
25855682Smarkm				"%2d",
25955682Smarkm				hour_24to12 (tm->tm_hour));
26055682Smarkm		break;
26155682Smarkm	    case 'm' :
26255682Smarkm		ret = snprintf (buf, maxsize - n,
26355682Smarkm				"%02d", tm->tm_mon + 1);
26455682Smarkm		break;
26555682Smarkm	    case 'M' :
26655682Smarkm		ret = snprintf (buf, maxsize - n,
26755682Smarkm				"%02d", tm->tm_min);
26855682Smarkm		break;
26955682Smarkm	    case 'n' :
27055682Smarkm		ret = snprintf (buf, maxsize - n, "\n");
27155682Smarkm		break;
27255682Smarkm	    case 'p' :
27355682Smarkm		ret = snprintf (buf, maxsize - n, "%s",
27455682Smarkm				hour_to_ampm (tm->tm_hour));
27555682Smarkm		break;
27655682Smarkm	    case 'r' :
27755682Smarkm		ret = snprintf (buf, maxsize - n,
27855682Smarkm				"%02d:%02d:%02d %s",
27955682Smarkm				hour_24to12 (tm->tm_hour),
28055682Smarkm				tm->tm_min,
28155682Smarkm				tm->tm_sec,
28255682Smarkm				hour_to_ampm (tm->tm_hour));
28355682Smarkm		break;
28455682Smarkm	    case 'R' :
28555682Smarkm		ret = snprintf (buf, maxsize - n,
28655682Smarkm				"%02d:%02d",
28755682Smarkm				tm->tm_hour,
28855682Smarkm				tm->tm_min);
289233294Sstas		break;
29055682Smarkm	    case 's' :
29155682Smarkm		ret = snprintf (buf, maxsize - n,
292178825Sdfr				"%d", (int)mktime(rk_UNCONST(tm)));
29355682Smarkm		break;
29455682Smarkm	    case 'S' :
29555682Smarkm		ret = snprintf (buf, maxsize - n,
29655682Smarkm				"%02d", tm->tm_sec);
29755682Smarkm		break;
29855682Smarkm	    case 't' :
29955682Smarkm		ret = snprintf (buf, maxsize - n, "\t");
30055682Smarkm		break;
30155682Smarkm	    case 'T' :
30255682Smarkm	    case 'X' :
30355682Smarkm		ret = snprintf (buf, maxsize - n,
30455682Smarkm				"%02d:%02d:%02d",
30555682Smarkm				tm->tm_hour,
30655682Smarkm				tm->tm_min,
30755682Smarkm				tm->tm_sec);
30855682Smarkm		break;
30955682Smarkm	    case 'u' :
31055682Smarkm		ret = snprintf (buf, maxsize - n,
31155682Smarkm				"%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
31255682Smarkm		break;
31355682Smarkm	    case 'U' :
31455682Smarkm		ret = snprintf (buf, maxsize - n,
31555682Smarkm				"%02d", week_number_sun (tm));
31655682Smarkm		break;
31755682Smarkm	    case 'V' :
31855682Smarkm		ret = snprintf (buf, maxsize - n,
31955682Smarkm				"%02d", week_number_mon4 (tm));
32055682Smarkm		break;
32155682Smarkm	    case 'w' :
32255682Smarkm		ret = snprintf (buf, maxsize - n,
32355682Smarkm				"%d", tm->tm_wday);
32455682Smarkm		break;
32555682Smarkm	    case 'W' :
32655682Smarkm		ret = snprintf (buf, maxsize - n,
32755682Smarkm				"%02d", week_number_mon (tm));
32855682Smarkm		break;
32955682Smarkm	    case 'x' :
33055682Smarkm		ret = snprintf (buf, maxsize - n,
33155682Smarkm				"%d:%02d:%02d",
33255682Smarkm				tm->tm_year,
33355682Smarkm				tm->tm_mon + 1,
33455682Smarkm				tm->tm_mday);
33555682Smarkm		break;
33655682Smarkm	    case 'y' :
33755682Smarkm		ret = snprintf (buf, maxsize - n,
33855682Smarkm				"%02d", (tm->tm_year + 1900) % 100);
33955682Smarkm		break;
34055682Smarkm	    case 'Y' :
34155682Smarkm		ret = snprintf (buf, maxsize - n,
34255682Smarkm				"%d", tm->tm_year + 1900);
34355682Smarkm		break;
34455682Smarkm	    case 'z':
34555682Smarkm		ret = snprintf (buf, maxsize - n,
34655682Smarkm				"%ld",
34755682Smarkm#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
34855682Smarkm				(long)tm->tm_gmtoff
34955682Smarkm#elif defined(HAVE_TIMEZONE)
350102644Snectar#ifdef HAVE_ALTZONE
35155682Smarkm				tm->tm_isdst ?
35255682Smarkm				(long)altzone :
353102644Snectar#endif
35455682Smarkm				(long)timezone
35555682Smarkm#else
35655682Smarkm#error Where in timezone chaos are you?
357233294Sstas#endif
35855682Smarkm				);
35955682Smarkm		break;
36055682Smarkm	    case 'Z' :
36155682Smarkm		ret = snprintf (buf, maxsize - n,
36255682Smarkm				"%s",
36355682Smarkm
36455682Smarkm#if defined(HAVE_STRUCT_TM_TM_ZONE)
36555682Smarkm				tm->tm_zone
36655682Smarkm#elif defined(HAVE_TIMEZONE)
36755682Smarkm				tzname[tm->tm_isdst]
36855682Smarkm#else
36955682Smarkm#error what?
37055682Smarkm#endif
37155682Smarkm		    );
37255682Smarkm		break;
37355682Smarkm	    case '\0' :
37455682Smarkm		--format;
37555682Smarkm		/* FALLTHROUGH */
37655682Smarkm	    case '%' :
37755682Smarkm		ret = snprintf (buf, maxsize - n,
37855682Smarkm				"%%");
37955682Smarkm		break;
38055682Smarkm	    default :
38155682Smarkm		ret = snprintf (buf, maxsize - n,
38255682Smarkm				"%%%c", *format);
38355682Smarkm		break;
38455682Smarkm	    }
385233294Sstas	    if (ret < 0 || ret >= (int)(maxsize - n))
38655682Smarkm		return 0;
38755682Smarkm	    n   += ret;
38855682Smarkm	    buf += ret;
38955682Smarkm	    ++format;
39055682Smarkm	} else {
39155682Smarkm	    *buf++ = *format++;
39255682Smarkm	    ++n;
39355682Smarkm	}
39455682Smarkm    }
395233294Sstas    *buf = '\0';
39655682Smarkm    return n;
39755682Smarkm}
398