1 /* 2 * Copyright (c) 1999 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 <ctype.h> 37 #include "roken.h" 38 39 RCSID("$Id: strptime.c,v 1.2 1999/11/12 15:29:55 assar Exp $"); 40 41 static const char *abb_weekdays[] = { 42 "Sun", 43 "Mon", 44 "Tue", 45 "Wed", 46 "Thu", 47 "Fri", 48 "Sat", 49 NULL 50 }; 51 52 static const char *full_weekdays[] = { 53 "Sunday", 54 "Monday", 55 "Tuesday", 56 "Wednesday", 57 "Thursday", 58 "Friday", 59 "Saturday", 60 NULL 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 NULL 77 }; 78 79 static const char *full_month[] = { 80 "January", 81 "February", 82 "Mars", 83 "April", 84 "May", 85 "June", 86 "July", 87 "August", 88 "September", 89 "October", 90 "November", 91 "December", 92 NULL, 93 }; 94 95 static const char *ampm[] = { 96 "am", 97 "pm", 98 NULL 99 }; 100 101 /* 102 * Try to match `*buf' to one of the strings in `strs'. Return the 103 * index of the matching string (or -1 if none). Also advance buf. 104 */ 105 106 static int 107 match_string (const char **buf, const char **strs) 108 { 109 int i = 0; 110 111 for (i = 0; strs[i] != NULL; ++i) { 112 int len = strlen (strs[i]); 113 114 if (strncasecmp (*buf, strs[i], len) == 0) { 115 *buf += len; 116 return i; 117 } 118 } 119 return -1; 120 } 121 122 /* 123 * tm_year is relative this year */ 124 125 const int tm_year_base = 1900; 126 127 /* 128 * Return TRUE iff `year' was a leap year. 129 */ 130 131 static int 132 is_leap_year (int year) 133 { 134 return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); 135 } 136 137 /* 138 * Return the weekday [0,6] (0 = Sunday) of the first day of `year' 139 */ 140 141 static int 142 first_day (int year) 143 { 144 int ret = 4; 145 146 for (; year > 1970; --year) 147 ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7; 148 return ret; 149 } 150 151 /* 152 * Set `timeptr' given `wnum' (week number [0, 53]) 153 */ 154 155 static void 156 set_week_number_sun (struct tm *timeptr, int wnum) 157 { 158 int fday = first_day (timeptr->tm_year + tm_year_base); 159 160 timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday; 161 if (timeptr->tm_yday < 0) { 162 timeptr->tm_wday = fday; 163 timeptr->tm_yday = 0; 164 } 165 } 166 167 /* 168 * Set `timeptr' given `wnum' (week number [0, 53]) 169 */ 170 171 static void 172 set_week_number_mon (struct tm *timeptr, int wnum) 173 { 174 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; 175 176 timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday; 177 if (timeptr->tm_yday < 0) { 178 timeptr->tm_wday = (fday + 1) % 7; 179 timeptr->tm_yday = 0; 180 } 181 } 182 183 /* 184 * Set `timeptr' given `wnum' (week number [0, 53]) 185 */ 186 187 static void 188 set_week_number_mon4 (struct tm *timeptr, int wnum) 189 { 190 int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; 191 int offset = 0; 192 193 if (fday < 4) 194 offset += 7; 195 196 timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday; 197 if (timeptr->tm_yday < 0) { 198 timeptr->tm_wday = fday; 199 timeptr->tm_yday = 0; 200 } 201 } 202 203 /* 204 * 205 */ 206 207 char * 208 strptime (const char *buf, const char *format, struct tm *timeptr) 209 { 210 char c; 211 212 for (; (c = *format) != '\0'; ++format) { 213 char *s; 214 int ret; 215 216 if (isspace (c)) { 217 while (isspace (*buf)) 218 ++buf; 219 } else if (c == '%' && format[1] != '\0') { 220 c = *++format; 221 if (c == 'E' || c == 'O') 222 c = *++format; 223 switch (c) { 224 case 'A' : 225 ret = match_string (&buf, full_weekdays); 226 if (ret < 0) 227 return NULL; 228 timeptr->tm_wday = ret; 229 break; 230 case 'a' : 231 ret = match_string (&buf, abb_weekdays); 232 if (ret < 0) 233 return NULL; 234 timeptr->tm_wday = ret; 235 break; 236 case 'B' : 237 ret = match_string (&buf, full_month); 238 if (ret < 0) 239 return NULL; 240 timeptr->tm_mon = ret; 241 break; 242 case 'b' : 243 case 'h' : 244 ret = match_string (&buf, abb_month); 245 if (ret < 0) 246 return NULL; 247 timeptr->tm_mon = ret; 248 break; 249 case 'C' : 250 ret = strtol (buf, &s, 10); 251 if (s == buf) 252 return NULL; 253 timeptr->tm_year = (ret * 100) - tm_year_base; 254 buf = s; 255 break; 256 case 'c' : 257 abort (); 258 case 'D' : /* %m/%d/%y */ 259 s = strptime (buf, "%m/%d/%y", timeptr); 260 if (s == NULL) 261 return NULL; 262 buf = s; 263 break; 264 case 'd' : 265 case 'e' : 266 ret = strtol (buf, &s, 10); 267 if (s == buf) 268 return NULL; 269 timeptr->tm_mday = ret; 270 buf = s; 271 break; 272 case 'H' : 273 case 'k' : 274 ret = strtol (buf, &s, 10); 275 if (s == buf) 276 return NULL; 277 timeptr->tm_hour = ret; 278 buf = s; 279 break; 280 case 'I' : 281 case 'l' : 282 ret = strtol (buf, &s, 10); 283 if (s == buf) 284 return NULL; 285 if (ret == 12) 286 timeptr->tm_hour = 0; 287 else 288 timeptr->tm_hour = ret; 289 buf = s; 290 break; 291 case 'j' : 292 ret = strtol (buf, &s, 10); 293 if (s == buf) 294 return NULL; 295 timeptr->tm_yday = ret - 1; 296 buf = s; 297 break; 298 case 'm' : 299 ret = strtol (buf, &s, 10); 300 if (s == buf) 301 return NULL; 302 timeptr->tm_mon = ret - 1; 303 buf = s; 304 break; 305 case 'M' : 306 ret = strtol (buf, &s, 10); 307 if (s == buf) 308 return NULL; 309 timeptr->tm_min = ret; 310 buf = s; 311 break; 312 case 'n' : 313 if (*buf == '\n') 314 ++buf; 315 else 316 return NULL; 317 break; 318 case 'p' : 319 ret = match_string (&buf, ampm); 320 if (ret < 0) 321 return NULL; 322 if (timeptr->tm_hour == 0) { 323 if (ret == 1) 324 timeptr->tm_hour = 12; 325 } else 326 timeptr->tm_hour += 12; 327 break; 328 case 'r' : /* %I:%M:%S %p */ 329 s = strptime (buf, "%I:%M:%S %p", timeptr); 330 if (s == NULL) 331 return NULL; 332 buf = s; 333 break; 334 case 'R' : /* %H:%M */ 335 s = strptime (buf, "%H:%M", timeptr); 336 if (s == NULL) 337 return NULL; 338 buf = s; 339 break; 340 case 'S' : 341 ret = strtol (buf, &s, 10); 342 if (s == buf) 343 return NULL; 344 timeptr->tm_sec = ret; 345 buf = s; 346 break; 347 case 't' : 348 if (*buf == '\t') 349 ++buf; 350 else 351 return NULL; 352 break; 353 case 'T' : /* %H:%M:%S */ 354 case 'X' : 355 s = strptime (buf, "%H:%M:%S", timeptr); 356 if (s == NULL) 357 return NULL; 358 buf = s; 359 break; 360 case 'u' : 361 ret = strtol (buf, &s, 10); 362 if (s == buf) 363 return NULL; 364 timeptr->tm_wday = ret - 1; 365 buf = s; 366 break; 367 case 'w' : 368 ret = strtol (buf, &s, 10); 369 if (s == buf) 370 return NULL; 371 timeptr->tm_wday = ret; 372 buf = s; 373 break; 374 case 'U' : 375 ret = strtol (buf, &s, 10); 376 if (s == buf) 377 return NULL; 378 set_week_number_sun (timeptr, ret); 379 buf = s; 380 break; 381 case 'V' : 382 ret = strtol (buf, &s, 10); 383 if (s == buf) 384 return NULL; 385 set_week_number_mon4 (timeptr, ret); 386 buf = s; 387 break; 388 case 'W' : 389 ret = strtol (buf, &s, 10); 390 if (s == buf) 391 return NULL; 392 set_week_number_mon (timeptr, ret); 393 buf = s; 394 break; 395 case 'x' : 396 s = strptime (buf, "%Y:%m:%d", timeptr); 397 if (s == NULL) 398 return NULL; 399 buf = s; 400 break; 401 case 'y' : 402 ret = strtol (buf, &s, 10); 403 if (s == buf) 404 return NULL; 405 if (ret < 70) 406 timeptr->tm_year = 100 + ret; 407 else 408 timeptr->tm_year = ret; 409 buf = s; 410 break; 411 case 'Y' : 412 ret = strtol (buf, &s, 10); 413 if (s == buf) 414 return NULL; 415 timeptr->tm_year = ret - tm_year_base; 416 buf = s; 417 break; 418 case 'Z' : 419 abort (); 420 case '\0' : 421 --format; 422 /* FALLTHROUGH */ 423 case '%' : 424 if (*buf == '%') 425 ++buf; 426 else 427 return NULL; 428 break; 429 default : 430 if (*buf == '%' || *++buf == c) 431 ++buf; 432 else 433 return NULL; 434 break; 435 } 436 } else { 437 if (*buf == c) 438 ++buf; 439 else 440 return NULL; 441 } 442 } 443 return (char *)buf; 444 } 445