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