strptime.c revision 81baaf272cb79c69ca9b0969466f5f946c923a2b
1/* $OpenBSD: strptime.c,v 1.11 2005/08/08 08:05:38 espie Exp $ */ 2/* $NetBSD: strptime.c,v 1.12 1998/01/20 21:39:40 mycroft Exp $ */ 3 4/*- 5 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code was contributed to The NetBSD Foundation by Klaus Klein. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39//#include <sys/localedef.h> 40#include <ctype.h> 41#include <errno.h> 42#include <locale.h> 43#include <stdlib.h> 44#include <string.h> 45#include <time.h> 46#include "tzfile.h" 47 48static const struct { 49 const char *abday[7]; 50 const char *day[7]; 51 const char *abmon[12]; 52 const char *mon[12]; 53 const char *am_pm[2]; 54 const char *d_t_fmt; 55 const char *d_fmt; 56 const char *t_fmt; 57 const char *t_fmt_ampm; 58} _DefaultTimeLocale = { 59 { 60 "Sun","Mon","Tue","Wed","Thu","Fri","Sat", 61 }, 62 { 63 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", 64 "Friday", "Saturday" 65 }, 66 { 67 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 69 }, 70 { 71 "January", "February", "March", "April", "May", "June", "July", 72 "August", "September", "October", "November", "December" 73 }, 74 { 75 "AM", "PM" 76 }, 77 "%a %b %d %H:%M:%S %Y", 78 "%m/%d/%y", 79 "%H:%M:%S", 80 "%I:%M:%S %p" 81}; 82 83#define _ctloc(x) (_DefaultTimeLocale.x) 84 85/* 86 * We do not implement alternate representations. However, we always 87 * check whether a given modifier is allowed for a certain conversion. 88 */ 89#define _ALT_E 0x01 90#define _ALT_O 0x02 91#define _LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } 92 93 94struct century_relyear { 95 int century; 96 int relyear; 97}; 98static int _conv_num(const unsigned char **, int *, int, int); 99static unsigned char *_strptime(const unsigned char *, const char *, struct tm *, 100 struct century_relyear *); 101 102 103char * 104strptime(const char *buf, const char *fmt, struct tm *tm) 105{ 106 struct century_relyear cr; 107 cr.century = TM_YEAR_BASE; 108 cr.relyear = -1; 109 return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr)); 110} 111 112static unsigned char * 113_strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr) 114{ 115 unsigned char c; 116 const unsigned char *bp; 117 size_t len = 0; 118 int alt_format, i; 119 120 bp = (unsigned char *)buf; 121 while ((c = *fmt) != '\0') { 122 /* Clear `alternate' modifier prior to new conversion. */ 123 alt_format = 0; 124 125 /* Eat up white-space. */ 126 if (isspace(c)) { 127 while (isspace(*bp)) 128 bp++; 129 130 fmt++; 131 continue; 132 } 133 134 if ((c = *fmt++) != '%') 135 goto literal; 136 137 138again: switch (c = *fmt++) { 139 case '%': /* "%%" is converted to "%". */ 140literal: 141 if (c != *bp++) 142 return (NULL); 143 144 break; 145 146 /* 147 * "Alternative" modifiers. Just set the appropriate flag 148 * and start over again. 149 */ 150 case 'E': /* "%E?" alternative conversion modifier. */ 151 _LEGAL_ALT(0); 152 alt_format |= _ALT_E; 153 goto again; 154 155 case 'O': /* "%O?" alternative conversion modifier. */ 156 _LEGAL_ALT(0); 157 alt_format |= _ALT_O; 158 goto again; 159 160 /* 161 * "Complex" conversion rules, implemented through recursion. 162 */ 163 case 'c': /* Date and time, using the locale's format. */ 164 _LEGAL_ALT(_ALT_E); 165 if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr))) 166 return (NULL); 167 break; 168 169 case 'D': /* The date as "%m/%d/%y". */ 170 _LEGAL_ALT(0); 171 if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr))) 172 return (NULL); 173 break; 174 175 case 'R': /* The time as "%H:%M". */ 176 _LEGAL_ALT(0); 177 if (!(bp = _strptime(bp, "%H:%M", tm, cr))) 178 return (NULL); 179 break; 180 181 case 'r': /* The time as "%I:%M:%S %p". */ 182 _LEGAL_ALT(0); 183 if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr))) 184 return (NULL); 185 break; 186 187 case 'T': /* The time as "%H:%M:%S". */ 188 _LEGAL_ALT(0); 189 if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr))) 190 return (NULL); 191 break; 192 193 case 'X': /* The time, using the locale's format. */ 194 _LEGAL_ALT(_ALT_E); 195 if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr))) 196 return (NULL); 197 break; 198 199 case 'x': /* The date, using the locale's format. */ 200 _LEGAL_ALT(_ALT_E); 201 if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr))) 202 return (NULL); 203 break; 204 205 /* 206 * "Elementary" conversion rules. 207 */ 208 case 'A': /* The day of week, using the locale's form. */ 209 case 'a': 210 _LEGAL_ALT(0); 211 for (i = 0; i < 7; i++) { 212 /* Full name. */ 213 len = strlen(_ctloc(day[i])); 214 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0) 215 break; 216 217 /* Abbreviated name. */ 218 len = strlen(_ctloc(abday[i])); 219 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0) 220 break; 221 } 222 223 /* Nothing matched. */ 224 if (i == 7) 225 return (NULL); 226 227 tm->tm_wday = i; 228 bp += len; 229 break; 230 231 case 'B': /* The month, using the locale's form. */ 232 case 'b': 233 case 'h': 234 _LEGAL_ALT(0); 235 for (i = 0; i < 12; i++) { 236 /* Full name. */ 237 len = strlen(_ctloc(mon[i])); 238 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0) 239 break; 240 241 /* Abbreviated name. */ 242 len = strlen(_ctloc(abmon[i])); 243 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0) 244 break; 245 } 246 247 /* Nothing matched. */ 248 if (i == 12) 249 return (NULL); 250 251 tm->tm_mon = i; 252 bp += len; 253 break; 254 255 case 'C': /* The century number. */ 256 _LEGAL_ALT(_ALT_E); 257 if (!(_conv_num(&bp, &i, 0, 99))) 258 return (NULL); 259 260 cr->century = i * 100; 261 break; 262 263 case 'd': /* The day of month. */ 264 case 'e': 265 _LEGAL_ALT(_ALT_O); 266 if (!(_conv_num(&bp, &tm->tm_mday, 1, 31))) 267 return (NULL); 268 break; 269 270 case 'k': /* The hour (24-hour clock representation). */ 271 _LEGAL_ALT(0); 272 /* FALLTHROUGH */ 273 case 'H': 274 _LEGAL_ALT(_ALT_O); 275 if (!(_conv_num(&bp, &tm->tm_hour, 0, 23))) 276 return (NULL); 277 break; 278 279 case 'l': /* The hour (12-hour clock representation). */ 280 _LEGAL_ALT(0); 281 /* FALLTHROUGH */ 282 case 'I': 283 _LEGAL_ALT(_ALT_O); 284 if (!(_conv_num(&bp, &tm->tm_hour, 1, 12))) 285 return (NULL); 286 break; 287 288 case 'j': /* The day of year. */ 289 _LEGAL_ALT(0); 290 if (!(_conv_num(&bp, &tm->tm_yday, 1, 366))) 291 return (NULL); 292 tm->tm_yday--; 293 break; 294 295 case 'M': /* The minute. */ 296 _LEGAL_ALT(_ALT_O); 297 if (!(_conv_num(&bp, &tm->tm_min, 0, 59))) 298 return (NULL); 299 break; 300 301 case 'm': /* The month. */ 302 _LEGAL_ALT(_ALT_O); 303 if (!(_conv_num(&bp, &tm->tm_mon, 1, 12))) 304 return (NULL); 305 tm->tm_mon--; 306 break; 307 308 case 'p': /* The locale's equivalent of AM/PM. */ 309 _LEGAL_ALT(0); 310 /* AM? */ 311 len = strlen(_ctloc(am_pm[0])); 312 if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) { 313 if (tm->tm_hour > 12) /* i.e., 13:00 AM ?! */ 314 return (NULL); 315 else if (tm->tm_hour == 12) 316 tm->tm_hour = 0; 317 318 bp += len; 319 break; 320 } 321 /* PM? */ 322 len = strlen(_ctloc(am_pm[1])); 323 if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) { 324 if (tm->tm_hour > 12) /* i.e., 13:00 PM ?! */ 325 return (NULL); 326 else if (tm->tm_hour < 12) 327 tm->tm_hour += 12; 328 329 bp += len; 330 break; 331 } 332 333 /* Nothing matched. */ 334 return (NULL); 335 336 case 'S': /* The seconds. */ 337 _LEGAL_ALT(_ALT_O); 338 if (!(_conv_num(&bp, &tm->tm_sec, 0, 61))) 339 return (NULL); 340 break; 341 342 case 's': 343 { 344 // Android addition, based on FreeBSD's implementation. 345 int saved_errno = errno; 346 errno = 0; 347 const unsigned char* old_bp = bp; 348 long n = strtol((const char*) bp, (char**) &bp, 10); 349 time_t t = n; 350 if (bp == old_bp || errno == ERANGE || ((long) t) != n) { 351 errno = saved_errno; 352 return NULL; 353 } 354 errno = saved_errno; 355 356 if (localtime_r(&t, tm) == NULL) return NULL; 357 } 358 break; 359 360 361 case 'U': /* The week of year, beginning on sunday. */ 362 case 'W': /* The week of year, beginning on monday. */ 363 _LEGAL_ALT(_ALT_O); 364 /* 365 * XXX This is bogus, as we can not assume any valid 366 * information present in the tm structure at this 367 * point to calculate a real value, so just check the 368 * range for now. 369 */ 370 if (!(_conv_num(&bp, &i, 0, 53))) 371 return (NULL); 372 break; 373 374 case 'w': /* The day of week, beginning on sunday. */ 375 _LEGAL_ALT(_ALT_O); 376 if (!(_conv_num(&bp, &tm->tm_wday, 0, 6))) 377 return (NULL); 378 break; 379 380 case 'Y': /* The year. */ 381 _LEGAL_ALT(_ALT_E); 382 if (!(_conv_num(&bp, &i, 0, 9999))) 383 return (NULL); 384 385 cr->relyear = -1; 386 tm->tm_year = i - TM_YEAR_BASE; 387 break; 388 389 case 'y': /* The year within the century (2 digits). */ 390 _LEGAL_ALT(_ALT_E | _ALT_O); 391 if (!(_conv_num(&bp, &cr->relyear, 0, 99))) 392 return (NULL); 393 break; 394 395 /* 396 * Miscellaneous conversions. 397 */ 398 case 'n': /* Any kind of white-space. */ 399 case 't': 400 _LEGAL_ALT(0); 401 while (isspace(*bp)) 402 bp++; 403 break; 404 405 406 default: /* Unknown/unsupported conversion. */ 407 return (NULL); 408 } 409 410 411 } 412 413 /* 414 * We need to evaluate the two digit year spec (%y) 415 * last as we can get a century spec (%C) at any time. 416 */ 417 if (cr->relyear != -1) { 418 if (cr->century == TM_YEAR_BASE) { 419 if (cr->relyear <= 68) 420 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE; 421 else 422 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE; 423 } else { 424 tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE; 425 } 426 } 427 428 return (unsigned char*)bp; 429} 430 431 432static int 433_conv_num(const unsigned char **buf, int *dest, int llim, int ulim) 434{ 435 int result = 0; 436 int rulim = ulim; 437 438 if (**buf < '0' || **buf > '9') 439 return (0); 440 441 /* we use rulim to break out of the loop when we run out of digits */ 442 do { 443 result *= 10; 444 result += *(*buf)++ - '0'; 445 rulim /= 10; 446 } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); 447 448 if (result < llim || result > ulim) 449 return (0); 450 451 *dest = result; 452 return (1); 453} 454 455char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) { 456 return strptime(buf, fmt, tm); 457} 458