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