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 #include <config.h> 34 #include "roken.h" 35 #ifdef TEST_STRPFTIME 36 #include "strpftime-test.h" 37 #endif 38 39 static const char *abb_weekdays[] = { 40 "Sun", 41 "Mon", 42 "Tue", 43 "Wed", 44 "Thu", 45 "Fri", 46 "Sat", 47 }; 48 49 static const char *full_weekdays[] = { 50 "Sunday", 51 "Monday", 52 "Tuesday", 53 "Wednesday", 54 "Thursday", 55 "Friday", 56 "Saturday", 57 }; 58 59 static const char *abb_month[] = { 60 "Jan", 61 "Feb", 62 "Mar", 63 "Apr", 64 "May", 65 "Jun", 66 "Jul", 67 "Aug", 68 "Sep", 69 "Oct", 70 "Nov", 71 "Dec" 72 }; 73 74 static const char *full_month[] = { 75 "January", 76 "February", 77 "Mars", 78 "April", 79 "May", 80 "June", 81 "July", 82 "August", 83 "September", 84 "October", 85 "November", 86 "December" 87 }; 88 89 static const char *ampm[] = { 90 "AM", 91 "PM" 92 }; 93 94 /* 95 * Convert hour in [0, 24] to [12 1 - 11 12 1 - 11 12] 96 */ 97 98 static int 99 hour_24to12 (int hour) 100 { 101 int ret = hour % 12; 102 103 if (ret == 0) 104 ret = 12; 105 return ret; 106 } 107 108 /* 109 * Return AM or PM for `hour' 110 */ 111 112 static const char * 113 hour_to_ampm (int hour) 114 { 115 return ampm[hour / 12]; 116 } 117 118 /* 119 * Return the week number of `tm' (Sunday being the first day of the week) 120 * as [0, 53] 121 */ 122 123 static int 124 week_number_sun (const struct tm *tm) 125 { 126 return (tm->tm_yday + 7 - (tm->tm_yday % 7 - tm->tm_wday + 7) % 7) / 7; 127 } 128 129 /* 130 * Return the week number of `tm' (Monday being the first day of the week) 131 * as [0, 53] 132 */ 133 134 static int 135 week_number_mon (const struct tm *tm) 136 { 137 int wday = (tm->tm_wday + 6) % 7; 138 139 return (tm->tm_yday + 7 - (tm->tm_yday % 7 - wday + 7) % 7) / 7; 140 } 141 142 /* 143 * Return the week number of `tm' (Monday being the first day of the 144 * week) as [01, 53]. Week number one is the one that has four or more 145 * days in that year. 146 */ 147 148 static int 149 week_number_mon4 (const struct tm *tm) 150 { 151 int wday = (tm->tm_wday + 6) % 7; 152 int w1day = (wday - tm->tm_yday % 7 + 7) % 7; 153 int ret; 154 155 ret = (tm->tm_yday + w1day) / 7; 156 if (w1day >= 4) 157 --ret; 158 if (ret == -1) 159 ret = 53; 160 else 161 ++ret; 162 return ret; 163 } 164 165 /* 166 * 167 */ 168 169 ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL 170 strftime (char *buf, size_t maxsize, const char *format, 171 const struct tm *tm) 172 { 173 size_t n = 0; 174 int ret; 175 176 while (*format != '\0' && n < maxsize) { 177 if (*format == '%') { 178 ++format; 179 if(*format == 'E' || *format == 'O') 180 ++format; 181 switch (*format) { 182 case 'a' : 183 ret = snprintf (buf, maxsize - n, 184 "%s", abb_weekdays[tm->tm_wday]); 185 break; 186 case 'A' : 187 ret = snprintf (buf, maxsize - n, 188 "%s", full_weekdays[tm->tm_wday]); 189 break; 190 case 'h' : 191 case 'b' : 192 ret = snprintf (buf, maxsize - n, 193 "%s", abb_month[tm->tm_mon]); 194 break; 195 case 'B' : 196 ret = snprintf (buf, maxsize - n, 197 "%s", full_month[tm->tm_mon]); 198 break; 199 case 'c' : 200 ret = snprintf (buf, maxsize - n, 201 "%d:%02d:%02d %02d:%02d:%02d", 202 tm->tm_year, 203 tm->tm_mon + 1, 204 tm->tm_mday, 205 tm->tm_hour, 206 tm->tm_min, 207 tm->tm_sec); 208 break; 209 case 'C' : 210 ret = snprintf (buf, maxsize - n, 211 "%02d", (tm->tm_year + 1900) / 100); 212 break; 213 case 'd' : 214 ret = snprintf (buf, maxsize - n, 215 "%02d", tm->tm_mday); 216 break; 217 case 'D' : 218 ret = snprintf (buf, maxsize - n, 219 "%02d/%02d/%02d", 220 tm->tm_mon + 1, 221 tm->tm_mday, 222 (tm->tm_year + 1900) % 100); 223 break; 224 case 'e' : 225 ret = snprintf (buf, maxsize - n, 226 "%2d", tm->tm_mday); 227 break; 228 case 'F': 229 ret = snprintf (buf, maxsize - n, 230 "%04d-%02d-%02d", tm->tm_year + 1900, 231 tm->tm_mon + 1, tm->tm_mday); 232 break; 233 case 'g': 234 /* last two digits of week-based year */ 235 abort(); 236 case 'G': 237 /* week-based year */ 238 abort(); 239 case 'H' : 240 ret = snprintf (buf, maxsize - n, 241 "%02d", tm->tm_hour); 242 break; 243 case 'I' : 244 ret = snprintf (buf, maxsize - n, 245 "%02d", 246 hour_24to12 (tm->tm_hour)); 247 break; 248 case 'j' : 249 ret = snprintf (buf, maxsize - n, 250 "%03d", tm->tm_yday + 1); 251 break; 252 case 'k' : 253 ret = snprintf (buf, maxsize - n, 254 "%2d", tm->tm_hour); 255 break; 256 case 'l' : 257 ret = snprintf (buf, maxsize - n, 258 "%2d", 259 hour_24to12 (tm->tm_hour)); 260 break; 261 case 'm' : 262 ret = snprintf (buf, maxsize - n, 263 "%02d", tm->tm_mon + 1); 264 break; 265 case 'M' : 266 ret = snprintf (buf, maxsize - n, 267 "%02d", tm->tm_min); 268 break; 269 case 'n' : 270 ret = snprintf (buf, maxsize - n, "\n"); 271 break; 272 case 'p' : 273 ret = snprintf (buf, maxsize - n, "%s", 274 hour_to_ampm (tm->tm_hour)); 275 break; 276 case 'r' : 277 ret = snprintf (buf, maxsize - n, 278 "%02d:%02d:%02d %s", 279 hour_24to12 (tm->tm_hour), 280 tm->tm_min, 281 tm->tm_sec, 282 hour_to_ampm (tm->tm_hour)); 283 break; 284 case 'R' : 285 ret = snprintf (buf, maxsize - n, 286 "%02d:%02d", 287 tm->tm_hour, 288 tm->tm_min); 289 break; 290 case 's' : 291 ret = snprintf (buf, maxsize - n, 292 "%d", (int)mktime(rk_UNCONST(tm))); 293 break; 294 case 'S' : 295 ret = snprintf (buf, maxsize - n, 296 "%02d", tm->tm_sec); 297 break; 298 case 't' : 299 ret = snprintf (buf, maxsize - n, "\t"); 300 break; 301 case 'T' : 302 case 'X' : 303 ret = snprintf (buf, maxsize - n, 304 "%02d:%02d:%02d", 305 tm->tm_hour, 306 tm->tm_min, 307 tm->tm_sec); 308 break; 309 case 'u' : 310 ret = snprintf (buf, maxsize - n, 311 "%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); 312 break; 313 case 'U' : 314 ret = snprintf (buf, maxsize - n, 315 "%02d", week_number_sun (tm)); 316 break; 317 case 'V' : 318 ret = snprintf (buf, maxsize - n, 319 "%02d", week_number_mon4 (tm)); 320 break; 321 case 'w' : 322 ret = snprintf (buf, maxsize - n, 323 "%d", tm->tm_wday); 324 break; 325 case 'W' : 326 ret = snprintf (buf, maxsize - n, 327 "%02d", week_number_mon (tm)); 328 break; 329 case 'x' : 330 ret = snprintf (buf, maxsize - n, 331 "%d:%02d:%02d", 332 tm->tm_year, 333 tm->tm_mon + 1, 334 tm->tm_mday); 335 break; 336 case 'y' : 337 ret = snprintf (buf, maxsize - n, 338 "%02d", (tm->tm_year + 1900) % 100); 339 break; 340 case 'Y' : 341 ret = snprintf (buf, maxsize - n, 342 "%d", tm->tm_year + 1900); 343 break; 344 case 'z': 345 ret = snprintf (buf, maxsize - n, 346 "%ld", 347 #if defined(HAVE_STRUCT_TM_TM_GMTOFF) 348 (long)tm->tm_gmtoff 349 #elif defined(HAVE_TIMEZONE) 350 #ifdef HAVE_ALTZONE 351 tm->tm_isdst ? 352 (long)altzone : 353 #endif 354 (long)timezone 355 #else 356 #error Where in timezone chaos are you? 357 #endif 358 ); 359 break; 360 case 'Z' : 361 ret = snprintf (buf, maxsize - n, 362 "%s", 363 364 #if defined(HAVE_STRUCT_TM_TM_ZONE) 365 tm->tm_zone 366 #elif defined(HAVE_TIMEZONE) 367 tzname[tm->tm_isdst] 368 #else 369 #error what? 370 #endif 371 ); 372 break; 373 case '\0' : 374 --format; 375 /* FALLTHROUGH */ 376 case '%' : 377 ret = snprintf (buf, maxsize - n, 378 "%%"); 379 break; 380 default : 381 ret = snprintf (buf, maxsize - n, 382 "%%%c", *format); 383 break; 384 } 385 if (ret < 0 || ret >= (int)(maxsize - n)) 386 return 0; 387 n += ret; 388 buf += ret; 389 ++format; 390 } else { 391 *buf++ = *format++; 392 ++n; 393 } 394 } 395 *buf = '\0'; 396 return n; 397 } 398