Home | History | Annotate | Download | only in tzcode
      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 
     48 static 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 
     94 struct century_relyear {
     95     int century;
     96     int relyear;
     97 };
     98 static  int _conv_num(const unsigned char **, int *, int, int);
     99 static  unsigned char *_strptime(const unsigned char *, const char *, struct tm *,
    100         struct century_relyear *);
    101 
    102 
    103 char *
    104 strptime(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 
    112 static 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 
    138 again:      switch (c = *fmt++) {
    139         case '%':   /* "%%" is converted to "%". */
    140 literal:
    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 'F':  /* The date as "%Y-%m-%d". */
    176             _LEGAL_ALT(0);
    177             if (!(bp = _strptime(bp, "%Y-%m-%d", tm, cr)))
    178                 return (NULL);
    179             continue;
    180 
    181         case 'R':   /* The time as "%H:%M". */
    182             _LEGAL_ALT(0);
    183             if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
    184                 return (NULL);
    185             break;
    186 
    187         case 'r':   /* The time as "%I:%M:%S %p". */
    188             _LEGAL_ALT(0);
    189             if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
    190                 return (NULL);
    191             break;
    192 
    193         case 'T':   /* The time as "%H:%M:%S". */
    194             _LEGAL_ALT(0);
    195             if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
    196                 return (NULL);
    197             break;
    198 
    199         case 'v':  /* The date as "%e-%b-%Y". */
    200             _LEGAL_ALT(0);
    201             if (!(bp = _strptime(bp, "%e-%b-%Y", tm, cr)))
    202                 return (NULL);
    203             break;
    204 
    205         case 'X':   /* The time, using the locale's format. */
    206             _LEGAL_ALT(_ALT_E);
    207             if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
    208                 return (NULL);
    209             break;
    210 
    211         case 'x':   /* The date, using the locale's format. */
    212             _LEGAL_ALT(_ALT_E);
    213             if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
    214                 return (NULL);
    215             break;
    216 
    217         /*
    218          * "Elementary" conversion rules.
    219          */
    220         case 'A':   /* The day of week, using the locale's form. */
    221         case 'a':
    222             _LEGAL_ALT(0);
    223             for (i = 0; i < 7; i++) {
    224                 /* Full name. */
    225                 len = strlen(_ctloc(day[i]));
    226                 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
    227                     break;
    228 
    229                 /* Abbreviated name. */
    230                 len = strlen(_ctloc(abday[i]));
    231                 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
    232                     break;
    233             }
    234 
    235             /* Nothing matched. */
    236             if (i == 7)
    237                 return (NULL);
    238 
    239             tm->tm_wday = i;
    240             bp += len;
    241             break;
    242 
    243         case 'B':   /* The month, using the locale's form. */
    244         case 'b':
    245         case 'h':
    246             _LEGAL_ALT(0);
    247             for (i = 0; i < 12; i++) {
    248                 /* Full name. */
    249                 len = strlen(_ctloc(mon[i]));
    250                 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
    251                     break;
    252 
    253                 /* Abbreviated name. */
    254                 len = strlen(_ctloc(abmon[i]));
    255                 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
    256                     break;
    257             }
    258 
    259             /* Nothing matched. */
    260             if (i == 12)
    261                 return (NULL);
    262 
    263             tm->tm_mon = i;
    264             bp += len;
    265             break;
    266 
    267         case 'C':   /* The century number. */
    268             _LEGAL_ALT(_ALT_E);
    269             if (!(_conv_num(&bp, &i, 0, 99)))
    270                 return (NULL);
    271 
    272             cr->century = i * 100;
    273             break;
    274 
    275         case 'd':   /* The day of month. */
    276         case 'e':
    277             _LEGAL_ALT(_ALT_O);
    278             if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
    279                 return (NULL);
    280             break;
    281 
    282         case 'k':   /* The hour (24-hour clock representation). */
    283             _LEGAL_ALT(0);
    284             /* FALLTHROUGH */
    285         case 'H':
    286             _LEGAL_ALT(_ALT_O);
    287             if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
    288                 return (NULL);
    289             break;
    290 
    291         case 'l':   /* The hour (12-hour clock representation). */
    292             _LEGAL_ALT(0);
    293             /* FALLTHROUGH */
    294         case 'I':
    295             _LEGAL_ALT(_ALT_O);
    296             if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
    297                 return (NULL);
    298             break;
    299 
    300         case 'j':   /* The day of year. */
    301             _LEGAL_ALT(0);
    302             if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
    303                 return (NULL);
    304             tm->tm_yday--;
    305             break;
    306 
    307         case 'M':   /* The minute. */
    308             _LEGAL_ALT(_ALT_O);
    309             if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
    310                 return (NULL);
    311             break;
    312 
    313         case 'm':   /* The month. */
    314             _LEGAL_ALT(_ALT_O);
    315             if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
    316                 return (NULL);
    317             tm->tm_mon--;
    318             break;
    319 
    320         case 'P':
    321         case 'p':   /* The locale's equivalent of AM/PM. */
    322             _LEGAL_ALT(0);
    323             /* AM? */
    324             len = strlen(_ctloc(am_pm[0]));
    325             if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
    326                 if (tm->tm_hour > 12)   /* i.e., 13:00 AM ?! */
    327                     return (NULL);
    328                 else if (tm->tm_hour == 12)
    329                     tm->tm_hour = 0;
    330 
    331                 bp += len;
    332                 break;
    333             }
    334             /* PM? */
    335             len = strlen(_ctloc(am_pm[1]));
    336             if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
    337                 if (tm->tm_hour > 12)   /* i.e., 13:00 PM ?! */
    338                     return (NULL);
    339                 else if (tm->tm_hour < 12)
    340                     tm->tm_hour += 12;
    341 
    342                 bp += len;
    343                 break;
    344             }
    345 
    346             /* Nothing matched. */
    347             return (NULL);
    348 
    349         case 'S':   /* The seconds. */
    350             _LEGAL_ALT(_ALT_O);
    351             if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
    352                 return (NULL);
    353             break;
    354 
    355         case 's':
    356             {
    357                 // Android addition, based on FreeBSD's implementation.
    358                 int saved_errno = errno;
    359                 errno = 0;
    360                 const unsigned char* old_bp = bp;
    361                 long n = strtol((const char*) bp, (char**) &bp, 10);
    362                 time_t t = n;
    363                 if (bp == old_bp || errno == ERANGE || ((long) t) != n) {
    364                     errno = saved_errno;
    365                     return NULL;
    366                 }
    367                 errno = saved_errno;
    368 
    369                 if (localtime_r(&t, tm) == NULL) return NULL;
    370             }
    371             break;
    372 
    373 
    374         case 'U':   /* The week of year, beginning on sunday. */
    375         case 'W':   /* The week of year, beginning on monday. */
    376             _LEGAL_ALT(_ALT_O);
    377             /*
    378              * XXX This is bogus, as we can not assume any valid
    379              * information present in the tm structure at this
    380              * point to calculate a real value, so just check the
    381              * range for now.
    382              */
    383              if (!(_conv_num(&bp, &i, 0, 53)))
    384                 return (NULL);
    385              break;
    386 
    387         case 'w':   /* The day of week, beginning on sunday. */
    388             _LEGAL_ALT(_ALT_O);
    389             if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
    390                 return (NULL);
    391             break;
    392 
    393         case 'u':  /* The day of week, monday = 1. */
    394             _LEGAL_ALT(_ALT_O);
    395             if (!(_conv_num(&bp, &i, 1, 7)))
    396                 return (NULL);
    397             tm->tm_wday = i % 7;
    398             continue;
    399 
    400         case 'g':  /* The year corresponding to the ISO week
    401                     * number but without the century.
    402                     */
    403             if (!(_conv_num(&bp, &i, 0, 99)))
    404                 return (NULL);
    405             continue;
    406 
    407         case 'G':  /* The year corresponding to the ISO week
    408                     * number with century.
    409                     */
    410             do
    411                 bp++;
    412             while (isdigit(*bp));
    413             continue;
    414 
    415         case 'V':  /* The ISO 8601:1988 week number as decimal */
    416             if (!(_conv_num(&bp, &i, 0, 53)))
    417                 return (NULL);
    418             continue;
    419 
    420         case 'Y':   /* The year. */
    421             _LEGAL_ALT(_ALT_E);
    422             if (!(_conv_num(&bp, &i, 0, 9999)))
    423                 return (NULL);
    424 
    425             cr->relyear = -1;
    426             tm->tm_year = i - TM_YEAR_BASE;
    427             break;
    428 
    429         case 'y':   /* The year within the century (2 digits). */
    430             _LEGAL_ALT(_ALT_E | _ALT_O);
    431             if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
    432                 return (NULL);
    433             break;
    434 
    435         /*
    436          * Miscellaneous conversions.
    437          */
    438         case 'n':   /* Any kind of white-space. */
    439         case 't':
    440             _LEGAL_ALT(0);
    441             while (isspace(*bp))
    442                 bp++;
    443             break;
    444 
    445 
    446         default:    /* Unknown/unsupported conversion. */
    447             return (NULL);
    448         }
    449 
    450 
    451     }
    452 
    453     /*
    454      * We need to evaluate the two digit year spec (%y)
    455      * last as we can get a century spec (%C) at any time.
    456      */
    457     if (cr->relyear != -1) {
    458         if (cr->century == TM_YEAR_BASE) {
    459             if (cr->relyear <= 68)
    460                 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
    461             else
    462                 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
    463         } else {
    464             tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
    465         }
    466     }
    467 
    468     return (unsigned char*)bp;
    469 }
    470 
    471 
    472 static int
    473 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
    474 {
    475     int result = 0;
    476     int rulim = ulim;
    477 
    478     if (**buf < '0' || **buf > '9')
    479         return (0);
    480 
    481     /* we use rulim to break out of the loop when we run out of digits */
    482     do {
    483         result *= 10;
    484         result += *(*buf)++ - '0';
    485         rulim /= 10;
    486     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
    487 
    488     if (result < llim || result > ulim)
    489         return (0);
    490 
    491     *dest = result;
    492     return (1);
    493 }
    494 
    495 char* strptime_l(const char* buf, const char* fmt, struct tm* tm, locale_t l) {
    496   return strptime(buf, fmt, tm);
    497 }
    498