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