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