1b528cefcSMark Murray /* 28373020dSJacques Vidrine * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan 3b528cefcSMark Murray * (Royal Institute of Technology, Stockholm, Sweden). 4b528cefcSMark Murray * All rights reserved. 5b528cefcSMark Murray * 6b528cefcSMark Murray * Redistribution and use in source and binary forms, with or without 7b528cefcSMark Murray * modification, are permitted provided that the following conditions 8b528cefcSMark Murray * are met: 9b528cefcSMark Murray * 10b528cefcSMark Murray * 1. Redistributions of source code must retain the above copyright 11b528cefcSMark Murray * notice, this list of conditions and the following disclaimer. 12b528cefcSMark Murray * 13b528cefcSMark Murray * 2. Redistributions in binary form must reproduce the above copyright 14b528cefcSMark Murray * notice, this list of conditions and the following disclaimer in the 15b528cefcSMark Murray * documentation and/or other materials provided with the distribution. 16b528cefcSMark Murray * 17b528cefcSMark Murray * 3. Neither the name of KTH nor the names of its contributors may be 18b528cefcSMark Murray * used to endorse or promote products derived from this software without 19b528cefcSMark Murray * specific prior written permission. 20b528cefcSMark Murray * 21b528cefcSMark Murray * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 22b528cefcSMark Murray * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23b528cefcSMark Murray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24b528cefcSMark Murray * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 25b528cefcSMark Murray * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26b528cefcSMark Murray * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27b528cefcSMark Murray * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28b528cefcSMark Murray * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29b528cefcSMark Murray * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30b528cefcSMark Murray * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31b528cefcSMark Murray * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 32b528cefcSMark Murray 33b528cefcSMark Murray #ifdef HAVE_CONFIG_H 34b528cefcSMark Murray #include <config.h> 35b528cefcSMark Murray #endif 36b528cefcSMark Murray #include "roken.h" 37b528cefcSMark Murray 388373020dSJacques Vidrine RCSID("$Id: strftime.c,v 1.13 2002/08/20 12:42:37 joda Exp $"); 39b528cefcSMark Murray 40b528cefcSMark Murray static const char *abb_weekdays[] = { 41b528cefcSMark Murray "Sun", 42b528cefcSMark Murray "Mon", 43b528cefcSMark Murray "Tue", 44b528cefcSMark Murray "Wed", 45b528cefcSMark Murray "Thu", 46b528cefcSMark Murray "Fri", 47b528cefcSMark Murray "Sat", 48b528cefcSMark Murray }; 49b528cefcSMark Murray 50b528cefcSMark Murray static const char *full_weekdays[] = { 51b528cefcSMark Murray "Sunday", 52b528cefcSMark Murray "Monday", 53b528cefcSMark Murray "Tuesday", 54b528cefcSMark Murray "Wednesday", 55b528cefcSMark Murray "Thursday", 56b528cefcSMark Murray "Friday", 57b528cefcSMark Murray "Saturday", 58b528cefcSMark Murray }; 59b528cefcSMark Murray 60b528cefcSMark Murray static const char *abb_month[] = { 61b528cefcSMark Murray "Jan", 62b528cefcSMark Murray "Feb", 63b528cefcSMark Murray "Mar", 64b528cefcSMark Murray "Apr", 65b528cefcSMark Murray "May", 66b528cefcSMark Murray "Jun", 67b528cefcSMark Murray "Jul", 68b528cefcSMark Murray "Aug", 69b528cefcSMark Murray "Sep", 70b528cefcSMark Murray "Oct", 71b528cefcSMark Murray "Nov", 72b528cefcSMark Murray "Dec" 73b528cefcSMark Murray }; 74b528cefcSMark Murray 75b528cefcSMark Murray static const char *full_month[] = { 76b528cefcSMark Murray "January", 77b528cefcSMark Murray "February", 78b528cefcSMark Murray "Mars", 79b528cefcSMark Murray "April", 80b528cefcSMark Murray "May", 81b528cefcSMark Murray "June", 82b528cefcSMark Murray "July", 83b528cefcSMark Murray "August", 84b528cefcSMark Murray "September", 85b528cefcSMark Murray "October", 86b528cefcSMark Murray "November", 87b528cefcSMark Murray "December" 88b528cefcSMark Murray }; 89b528cefcSMark Murray 90b528cefcSMark Murray static const char *ampm[] = { 91b528cefcSMark Murray "AM", 92b528cefcSMark Murray "PM" 93b528cefcSMark Murray }; 94b528cefcSMark Murray 95b528cefcSMark Murray /* 96b528cefcSMark Murray * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12] 97b528cefcSMark Murray */ 98b528cefcSMark Murray 99b528cefcSMark Murray static int 100b528cefcSMark Murray hour_24to12 (int hour) 101b528cefcSMark Murray { 102b528cefcSMark Murray int ret = hour % 12; 103b528cefcSMark Murray 104b528cefcSMark Murray if (ret == 0) 105b528cefcSMark Murray ret = 12; 106b528cefcSMark Murray return ret; 107b528cefcSMark Murray } 108b528cefcSMark Murray 109b528cefcSMark Murray /* 110b528cefcSMark Murray * Return AM or PM for `hour' 111b528cefcSMark Murray */ 112b528cefcSMark Murray 113b528cefcSMark Murray static const char * 114b528cefcSMark Murray hour_to_ampm (int hour) 115b528cefcSMark Murray { 116b528cefcSMark Murray return ampm[hour / 12]; 117b528cefcSMark Murray } 118b528cefcSMark Murray 119b528cefcSMark Murray /* 120b528cefcSMark Murray * Return the week number of `tm' (Sunday being the first day of the week) 121b528cefcSMark Murray * as [0, 53] 122b528cefcSMark Murray */ 123b528cefcSMark Murray 124b528cefcSMark Murray static int 125b528cefcSMark Murray week_number_sun (const struct tm *tm) 126b528cefcSMark Murray { 127b528cefcSMark Murray return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7; 128b528cefcSMark Murray } 129b528cefcSMark Murray 130b528cefcSMark Murray /* 131b528cefcSMark Murray * Return the week number of `tm' (Monday being the first day of the week) 132b528cefcSMark Murray * as [0, 53] 133b528cefcSMark Murray */ 134b528cefcSMark Murray 135b528cefcSMark Murray static int 136b528cefcSMark Murray week_number_mon (const struct tm *tm) 137b528cefcSMark Murray { 138b528cefcSMark Murray int wday = (tm->tm_wday + 6) % 7; 139b528cefcSMark Murray 140b528cefcSMark Murray return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7; 141b528cefcSMark Murray } 142b528cefcSMark Murray 143b528cefcSMark Murray /* 144b528cefcSMark Murray * Return the week number of `tm' (Monday being the first day of the 145b528cefcSMark Murray * week) as [01, 53]. Week number one is the one that has four or more 146b528cefcSMark Murray * days in that year. 147b528cefcSMark Murray */ 148b528cefcSMark Murray 149b528cefcSMark Murray static int 150b528cefcSMark Murray week_number_mon4 (const struct tm *tm) 151b528cefcSMark Murray { 152b528cefcSMark Murray int wday = (tm->tm_wday + 6) % 7; 153b528cefcSMark Murray int w1day = (wday - tm->tm_yday % 7 + 7) % 7; 154b528cefcSMark Murray int ret; 155b528cefcSMark Murray 156b528cefcSMark Murray ret = (tm->tm_yday + w1day) / 7; 157b528cefcSMark Murray if (w1day >= 4) 158b528cefcSMark Murray --ret; 159b528cefcSMark Murray if (ret == -1) 160b528cefcSMark Murray ret = 53; 161b528cefcSMark Murray else 162b528cefcSMark Murray ++ret; 163b528cefcSMark Murray return ret; 164b528cefcSMark Murray } 165b528cefcSMark Murray 166b528cefcSMark Murray /* 167b528cefcSMark Murray * 168b528cefcSMark Murray */ 169b528cefcSMark Murray 170b528cefcSMark Murray size_t 171b528cefcSMark Murray strftime (char *buf, size_t maxsize, const char *format, 172b528cefcSMark Murray const struct tm *tm) 173b528cefcSMark Murray { 174b528cefcSMark Murray size_t n = 0; 1754137ff4cSJacques Vidrine int ret; 176b528cefcSMark Murray 177b528cefcSMark Murray while (*format != '\0' && n < maxsize) { 178b528cefcSMark Murray if (*format == '%') { 179b528cefcSMark Murray ++format; 180b528cefcSMark Murray if(*format == 'E' || *format == 'O') 181b528cefcSMark Murray ++format; 182b528cefcSMark Murray switch (*format) { 183b528cefcSMark Murray case 'a' : 184b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 185b528cefcSMark Murray "%s", abb_weekdays[tm->tm_wday]); 186b528cefcSMark Murray break; 187b528cefcSMark Murray case 'A' : 188b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 189b528cefcSMark Murray "%s", full_weekdays[tm->tm_wday]); 190b528cefcSMark Murray break; 191b528cefcSMark Murray case 'h' : 192b528cefcSMark Murray case 'b' : 193b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 194b528cefcSMark Murray "%s", abb_month[tm->tm_mon]); 195b528cefcSMark Murray break; 196b528cefcSMark Murray case 'B' : 197b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 198b528cefcSMark Murray "%s", full_month[tm->tm_mon]); 199b528cefcSMark Murray break; 200b528cefcSMark Murray case 'c' : 201b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 202b528cefcSMark Murray "%d:%02d:%02d %02d:%02d:%02d", 203b528cefcSMark Murray tm->tm_year, 204b528cefcSMark Murray tm->tm_mon + 1, 205b528cefcSMark Murray tm->tm_mday, 206b528cefcSMark Murray tm->tm_hour, 207b528cefcSMark Murray tm->tm_min, 208b528cefcSMark Murray tm->tm_sec); 209b528cefcSMark Murray break; 210b528cefcSMark Murray case 'C' : 211b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 212b528cefcSMark Murray "%02d", (tm->tm_year + 1900) / 100); 213b528cefcSMark Murray break; 214b528cefcSMark Murray case 'd' : 215b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 216b528cefcSMark Murray "%02d", tm->tm_mday); 217b528cefcSMark Murray break; 218b528cefcSMark Murray case 'D' : 219b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 220b528cefcSMark Murray "%02d/%02d/%02d", 221b528cefcSMark Murray tm->tm_mon + 1, 222b528cefcSMark Murray tm->tm_mday, 223b528cefcSMark Murray (tm->tm_year + 1900) % 100); 224b528cefcSMark Murray break; 225b528cefcSMark Murray case 'e' : 226b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 227b528cefcSMark Murray "%2d", tm->tm_mday); 228b528cefcSMark Murray break; 229b528cefcSMark Murray case 'F': 230b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 231b528cefcSMark Murray "%04d-%02d-%02d", tm->tm_year + 1900, 232b528cefcSMark Murray tm->tm_mon + 1, tm->tm_mday); 233b528cefcSMark Murray break; 234b528cefcSMark Murray case 'g': 235b528cefcSMark Murray /* last two digits of week-based year */ 236b528cefcSMark Murray abort(); 237b528cefcSMark Murray case 'G': 238b528cefcSMark Murray /* week-based year */ 239b528cefcSMark Murray abort(); 240b528cefcSMark Murray case 'H' : 241b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 242b528cefcSMark Murray "%02d", tm->tm_hour); 243b528cefcSMark Murray break; 244b528cefcSMark Murray case 'I' : 245b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 246b528cefcSMark Murray "%02d", 247b528cefcSMark Murray hour_24to12 (tm->tm_hour)); 248b528cefcSMark Murray break; 249b528cefcSMark Murray case 'j' : 250b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 251b528cefcSMark Murray "%03d", tm->tm_yday + 1); 252b528cefcSMark Murray break; 253b528cefcSMark Murray case 'k' : 254b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 255b528cefcSMark Murray "%2d", tm->tm_hour); 256b528cefcSMark Murray break; 257b528cefcSMark Murray case 'l' : 258b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 259b528cefcSMark Murray "%2d", 260b528cefcSMark Murray hour_24to12 (tm->tm_hour)); 261b528cefcSMark Murray break; 262b528cefcSMark Murray case 'm' : 263b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 264b528cefcSMark Murray "%02d", tm->tm_mon + 1); 265b528cefcSMark Murray break; 266b528cefcSMark Murray case 'M' : 267b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 268b528cefcSMark Murray "%02d", tm->tm_min); 269b528cefcSMark Murray break; 270b528cefcSMark Murray case 'n' : 271b528cefcSMark Murray ret = snprintf (buf, maxsize - n, "\n"); 272b528cefcSMark Murray break; 273b528cefcSMark Murray case 'p' : 274b528cefcSMark Murray ret = snprintf (buf, maxsize - n, "%s", 275b528cefcSMark Murray hour_to_ampm (tm->tm_hour)); 276b528cefcSMark Murray break; 277b528cefcSMark Murray case 'r' : 278b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 279b528cefcSMark Murray "%02d:%02d:%02d %s", 280b528cefcSMark Murray hour_24to12 (tm->tm_hour), 281b528cefcSMark Murray tm->tm_min, 282b528cefcSMark Murray tm->tm_sec, 283b528cefcSMark Murray hour_to_ampm (tm->tm_hour)); 284b528cefcSMark Murray break; 285b528cefcSMark Murray case 'R' : 286b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 287b528cefcSMark Murray "%02d:%02d", 288b528cefcSMark Murray tm->tm_hour, 289b528cefcSMark Murray tm->tm_min); 290b528cefcSMark Murray 291b528cefcSMark Murray case 's' : 292b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 293b528cefcSMark Murray "%d", (int)mktime((struct tm *)tm)); 294b528cefcSMark Murray break; 295b528cefcSMark Murray case 'S' : 296b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 297b528cefcSMark Murray "%02d", tm->tm_sec); 298b528cefcSMark Murray break; 299b528cefcSMark Murray case 't' : 300b528cefcSMark Murray ret = snprintf (buf, maxsize - n, "\t"); 301b528cefcSMark Murray break; 302b528cefcSMark Murray case 'T' : 303b528cefcSMark Murray case 'X' : 304b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 305b528cefcSMark Murray "%02d:%02d:%02d", 306b528cefcSMark Murray tm->tm_hour, 307b528cefcSMark Murray tm->tm_min, 308b528cefcSMark Murray tm->tm_sec); 309b528cefcSMark Murray break; 310b528cefcSMark Murray case 'u' : 311b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 312b528cefcSMark Murray "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); 313b528cefcSMark Murray break; 314b528cefcSMark Murray case 'U' : 315b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 316b528cefcSMark Murray "%02d", week_number_sun (tm)); 317b528cefcSMark Murray break; 318b528cefcSMark Murray case 'V' : 319b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 320b528cefcSMark Murray "%02d", week_number_mon4 (tm)); 321b528cefcSMark Murray break; 322b528cefcSMark Murray case 'w' : 323b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 324b528cefcSMark Murray "%d", tm->tm_wday); 325b528cefcSMark Murray break; 326b528cefcSMark Murray case 'W' : 327b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 328b528cefcSMark Murray "%02d", week_number_mon (tm)); 329b528cefcSMark Murray break; 330b528cefcSMark Murray case 'x' : 331b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 332b528cefcSMark Murray "%d:%02d:%02d", 333b528cefcSMark Murray tm->tm_year, 334b528cefcSMark Murray tm->tm_mon + 1, 335b528cefcSMark Murray tm->tm_mday); 336b528cefcSMark Murray break; 337b528cefcSMark Murray case 'y' : 338b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 339b528cefcSMark Murray "%02d", (tm->tm_year + 1900) % 100); 340b528cefcSMark Murray break; 341b528cefcSMark Murray case 'Y' : 342b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 343b528cefcSMark Murray "%d", tm->tm_year + 1900); 344b528cefcSMark Murray break; 345b528cefcSMark Murray case 'z': 346b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 347b528cefcSMark Murray "%ld", 348b528cefcSMark Murray #if defined(HAVE_STRUCT_TM_TM_GMTOFF) 349b528cefcSMark Murray (long)tm->tm_gmtoff 350b528cefcSMark Murray #elif defined(HAVE_TIMEZONE) 3518373020dSJacques Vidrine #ifdef HAVE_ALTZONE 352b528cefcSMark Murray tm->tm_isdst ? 353b528cefcSMark Murray (long)altzone : 3548373020dSJacques Vidrine #endif 355b528cefcSMark Murray (long)timezone 356b528cefcSMark Murray #else 357b528cefcSMark Murray #error Where in timezone chaos are you? 358b528cefcSMark Murray #endif 359b528cefcSMark Murray ); 360b528cefcSMark Murray break; 361b528cefcSMark Murray case 'Z' : 362b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 363b528cefcSMark Murray "%s", 364b528cefcSMark Murray 365b528cefcSMark Murray #if defined(HAVE_STRUCT_TM_TM_ZONE) 366b528cefcSMark Murray tm->tm_zone 367b528cefcSMark Murray #elif defined(HAVE_TIMEZONE) 368b528cefcSMark Murray tzname[tm->tm_isdst] 369b528cefcSMark Murray #else 370b528cefcSMark Murray #error what? 371b528cefcSMark Murray #endif 372b528cefcSMark Murray ); 373b528cefcSMark Murray break; 374b528cefcSMark Murray case '\0' : 375b528cefcSMark Murray --format; 376b528cefcSMark Murray /* FALLTHROUGH */ 377b528cefcSMark Murray case '%' : 378b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 379b528cefcSMark Murray "%%"); 380b528cefcSMark Murray break; 381b528cefcSMark Murray default : 382b528cefcSMark Murray ret = snprintf (buf, maxsize - n, 383b528cefcSMark Murray "%%%c", *format); 384b528cefcSMark Murray break; 385b528cefcSMark Murray } 3864137ff4cSJacques Vidrine if (ret < 0 || ret >= maxsize - n) 387b528cefcSMark Murray return 0; 388b528cefcSMark Murray n += ret; 389b528cefcSMark Murray buf += ret; 390b528cefcSMark Murray ++format; 391b528cefcSMark Murray } else { 392b528cefcSMark Murray *buf++ = *format++; 393b528cefcSMark Murray ++n; 394b528cefcSMark Murray } 395b528cefcSMark Murray } 396b528cefcSMark Murray *buf++ = '\0'; 397b528cefcSMark Murray return n; 398b528cefcSMark Murray } 399