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