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 <locale.h>
     42 #include <string.h>
     43 #include <time.h>
     44 #include "tzfile.h"
     45 
     46 static const struct {
     47     const char *abday[7];
     48     const char *day[7];
     49     const char *abmon[12];
     50     const char *mon[12];
     51     const char *am_pm[2];
     52     const char *d_t_fmt;
     53     const char *d_fmt;
     54     const char *t_fmt;
     55     const char *t_fmt_ampm;
     56 } _DefaultTimeLocale = {
     57     {
     58         "Sun","Mon","Tue","Wed","Thu","Fri","Sat",
     59     },
     60     {
     61         "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
     62         "Friday", "Saturday"
     63     },
     64     {
     65         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     66         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     67     },
     68     {
     69         "January", "February", "March", "April", "May", "June", "July",
     70         "August", "September", "October", "November", "December"
     71     },
     72     {
     73         "AM", "PM"
     74     },
     75     "%a %b %d %H:%M:%S %Y",
     76     "%m/%d/%y",
     77     "%H:%M:%S",
     78     "%I:%M:%S %p"
     79 };
     80 
     81 #define _ctloc(x) (_DefaultTimeLocale.x)
     82 
     83 /*
     84  * We do not implement alternate representations. However, we always
     85  * check whether a given modifier is allowed for a certain conversion.
     86  */
     87 #define _ALT_E          0x01
     88 #define _ALT_O          0x02
     89 #define _LEGAL_ALT(x)       { if (alt_format & ~(x)) return (0); }
     90 
     91 
     92 struct century_relyear {
     93     int century;
     94     int relyear;
     95 };
     96 static  int _conv_num(const unsigned char **, int *, int, int);
     97 static  unsigned char *_strptime(const unsigned char *, const char *, struct tm *,
     98         struct century_relyear *);
     99 
    100 
    101 char *
    102 strptime(const char *buf, const char *fmt, struct tm *tm)
    103 {
    104     struct century_relyear cr;
    105     cr.century = TM_YEAR_BASE;
    106     cr.relyear = -1;
    107     return (char*)(_strptime((const unsigned char*)buf, fmt, tm, &cr));
    108 }
    109 
    110 static unsigned char *
    111 _strptime(const unsigned char *buf, const char *fmt, struct tm *tm, struct century_relyear *cr)
    112 {
    113     unsigned char c;
    114     const unsigned char *bp;
    115     size_t len = 0;
    116     int alt_format, i;
    117 
    118     bp = (unsigned char *)buf;
    119     while ((c = *fmt) != '\0') {
    120         /* Clear `alternate' modifier prior to new conversion. */
    121         alt_format = 0;
    122 
    123         /* Eat up white-space. */
    124         if (isspace(c)) {
    125             while (isspace(*bp))
    126                 bp++;
    127 
    128             fmt++;
    129             continue;
    130         }
    131 
    132         if ((c = *fmt++) != '%')
    133             goto literal;
    134 
    135 
    136 again:      switch (c = *fmt++) {
    137         case '%':   /* "%%" is converted to "%". */
    138 literal:
    139         if (c != *bp++)
    140             return (NULL);
    141 
    142         break;
    143 
    144         /*
    145          * "Alternative" modifiers. Just set the appropriate flag
    146          * and start over again.
    147          */
    148         case 'E':   /* "%E?" alternative conversion modifier. */
    149             _LEGAL_ALT(0);
    150             alt_format |= _ALT_E;
    151             goto again;
    152 
    153         case 'O':   /* "%O?" alternative conversion modifier. */
    154             _LEGAL_ALT(0);
    155             alt_format |= _ALT_O;
    156             goto again;
    157 
    158         /*
    159          * "Complex" conversion rules, implemented through recursion.
    160          */
    161         case 'c':   /* Date and time, using the locale's format. */
    162             _LEGAL_ALT(_ALT_E);
    163             if (!(bp = _strptime(bp, _ctloc(d_t_fmt), tm, cr)))
    164                 return (NULL);
    165             break;
    166 
    167         case 'D':   /* The date as "%m/%d/%y". */
    168             _LEGAL_ALT(0);
    169             if (!(bp = _strptime(bp, "%m/%d/%y", tm, cr)))
    170                 return (NULL);
    171             break;
    172 
    173         case 'R':   /* The time as "%H:%M". */
    174             _LEGAL_ALT(0);
    175             if (!(bp = _strptime(bp, "%H:%M", tm, cr)))
    176                 return (NULL);
    177             break;
    178 
    179         case 'r':   /* The time as "%I:%M:%S %p". */
    180             _LEGAL_ALT(0);
    181             if (!(bp = _strptime(bp, "%I:%M:%S %p", tm, cr)))
    182                 return (NULL);
    183             break;
    184 
    185         case 'T':   /* The time as "%H:%M:%S". */
    186             _LEGAL_ALT(0);
    187             if (!(bp = _strptime(bp, "%H:%M:%S", tm, cr)))
    188                 return (NULL);
    189             break;
    190 
    191         case 'X':   /* The time, using the locale's format. */
    192             _LEGAL_ALT(_ALT_E);
    193             if (!(bp = _strptime(bp, _ctloc(t_fmt), tm, cr)))
    194                 return (NULL);
    195             break;
    196 
    197         case 'x':   /* The date, using the locale's format. */
    198             _LEGAL_ALT(_ALT_E);
    199             if (!(bp = _strptime(bp, _ctloc(d_fmt), tm, cr)))
    200                 return (NULL);
    201             break;
    202 
    203         /*
    204          * "Elementary" conversion rules.
    205          */
    206         case 'A':   /* The day of week, using the locale's form. */
    207         case 'a':
    208             _LEGAL_ALT(0);
    209             for (i = 0; i < 7; i++) {
    210                 /* Full name. */
    211                 len = strlen(_ctloc(day[i]));
    212                 if (strncasecmp(_ctloc(day[i]), (const char*)bp, len) == 0)
    213                     break;
    214 
    215                 /* Abbreviated name. */
    216                 len = strlen(_ctloc(abday[i]));
    217                 if (strncasecmp(_ctloc(abday[i]), (const char*)bp, len) == 0)
    218                     break;
    219             }
    220 
    221             /* Nothing matched. */
    222             if (i == 7)
    223                 return (NULL);
    224 
    225             tm->tm_wday = i;
    226             bp += len;
    227             break;
    228 
    229         case 'B':   /* The month, using the locale's form. */
    230         case 'b':
    231         case 'h':
    232             _LEGAL_ALT(0);
    233             for (i = 0; i < 12; i++) {
    234                 /* Full name. */
    235                 len = strlen(_ctloc(mon[i]));
    236                 if (strncasecmp(_ctloc(mon[i]), (const char*)bp, len) == 0)
    237                     break;
    238 
    239                 /* Abbreviated name. */
    240                 len = strlen(_ctloc(abmon[i]));
    241                 if (strncasecmp(_ctloc(abmon[i]), (const char*)bp, len) == 0)
    242                     break;
    243             }
    244 
    245             /* Nothing matched. */
    246             if (i == 12)
    247                 return (NULL);
    248 
    249             tm->tm_mon = i;
    250             bp += len;
    251             break;
    252 
    253         case 'C':   /* The century number. */
    254             _LEGAL_ALT(_ALT_E);
    255             if (!(_conv_num(&bp, &i, 0, 99)))
    256                 return (NULL);
    257 
    258             cr->century = i * 100;
    259             break;
    260 
    261         case 'd':   /* The day of month. */
    262         case 'e':
    263             _LEGAL_ALT(_ALT_O);
    264             if (!(_conv_num(&bp, &tm->tm_mday, 1, 31)))
    265                 return (NULL);
    266             break;
    267 
    268         case 'k':   /* The hour (24-hour clock representation). */
    269             _LEGAL_ALT(0);
    270             /* FALLTHROUGH */
    271         case 'H':
    272             _LEGAL_ALT(_ALT_O);
    273             if (!(_conv_num(&bp, &tm->tm_hour, 0, 23)))
    274                 return (NULL);
    275             break;
    276 
    277         case 'l':   /* The hour (12-hour clock representation). */
    278             _LEGAL_ALT(0);
    279             /* FALLTHROUGH */
    280         case 'I':
    281             _LEGAL_ALT(_ALT_O);
    282             if (!(_conv_num(&bp, &tm->tm_hour, 1, 12)))
    283                 return (NULL);
    284             break;
    285 
    286         case 'j':   /* The day of year. */
    287             _LEGAL_ALT(0);
    288             if (!(_conv_num(&bp, &tm->tm_yday, 1, 366)))
    289                 return (NULL);
    290             tm->tm_yday--;
    291             break;
    292 
    293         case 'M':   /* The minute. */
    294             _LEGAL_ALT(_ALT_O);
    295             if (!(_conv_num(&bp, &tm->tm_min, 0, 59)))
    296                 return (NULL);
    297             break;
    298 
    299         case 'm':   /* The month. */
    300             _LEGAL_ALT(_ALT_O);
    301             if (!(_conv_num(&bp, &tm->tm_mon, 1, 12)))
    302                 return (NULL);
    303             tm->tm_mon--;
    304             break;
    305 
    306         case 'p':   /* The locale's equivalent of AM/PM. */
    307             _LEGAL_ALT(0);
    308             /* AM? */
    309             len = strlen(_ctloc(am_pm[0]));
    310             if (strncasecmp(_ctloc(am_pm[0]), (const char*)bp, len) == 0) {
    311                 if (tm->tm_hour > 12)   /* i.e., 13:00 AM ?! */
    312                     return (NULL);
    313                 else if (tm->tm_hour == 12)
    314                     tm->tm_hour = 0;
    315 
    316                 bp += len;
    317                 break;
    318             }
    319             /* PM? */
    320             len = strlen(_ctloc(am_pm[1]));
    321             if (strncasecmp(_ctloc(am_pm[1]), (const char*)bp, len) == 0) {
    322                 if (tm->tm_hour > 12)   /* i.e., 13:00 PM ?! */
    323                     return (NULL);
    324                 else if (tm->tm_hour < 12)
    325                     tm->tm_hour += 12;
    326 
    327                 bp += len;
    328                 break;
    329             }
    330 
    331             /* Nothing matched. */
    332             return (NULL);
    333 
    334         case 'S':   /* The seconds. */
    335             _LEGAL_ALT(_ALT_O);
    336             if (!(_conv_num(&bp, &tm->tm_sec, 0, 61)))
    337                 return (NULL);
    338             break;
    339 
    340         case 'U':   /* The week of year, beginning on sunday. */
    341         case 'W':   /* The week of year, beginning on monday. */
    342             _LEGAL_ALT(_ALT_O);
    343             /*
    344              * XXX This is bogus, as we can not assume any valid
    345              * information present in the tm structure at this
    346              * point to calculate a real value, so just check the
    347              * range for now.
    348              */
    349              if (!(_conv_num(&bp, &i, 0, 53)))
    350                 return (NULL);
    351              break;
    352 
    353         case 'w':   /* The day of week, beginning on sunday. */
    354             _LEGAL_ALT(_ALT_O);
    355             if (!(_conv_num(&bp, &tm->tm_wday, 0, 6)))
    356                 return (NULL);
    357             break;
    358 
    359         case 'Y':   /* The year. */
    360             _LEGAL_ALT(_ALT_E);
    361             if (!(_conv_num(&bp, &i, 0, 9999)))
    362                 return (NULL);
    363 
    364             cr->relyear = -1;
    365             tm->tm_year = i - TM_YEAR_BASE;
    366             break;
    367 
    368         case 'y':   /* The year within the century (2 digits). */
    369             _LEGAL_ALT(_ALT_E | _ALT_O);
    370             if (!(_conv_num(&bp, &cr->relyear, 0, 99)))
    371                 return (NULL);
    372             break;
    373 
    374         /*
    375          * Miscellaneous conversions.
    376          */
    377         case 'n':   /* Any kind of white-space. */
    378         case 't':
    379             _LEGAL_ALT(0);
    380             while (isspace(*bp))
    381                 bp++;
    382             break;
    383 
    384 
    385         default:    /* Unknown/unsupported conversion. */
    386             return (NULL);
    387         }
    388 
    389 
    390     }
    391 
    392     /*
    393      * We need to evaluate the two digit year spec (%y)
    394      * last as we can get a century spec (%C) at any time.
    395      */
    396     if (cr->relyear != -1) {
    397         if (cr->century == TM_YEAR_BASE) {
    398             if (cr->relyear <= 68)
    399                 tm->tm_year = cr->relyear + 2000 - TM_YEAR_BASE;
    400             else
    401                 tm->tm_year = cr->relyear + 1900 - TM_YEAR_BASE;
    402         } else {
    403             tm->tm_year = cr->relyear + cr->century - TM_YEAR_BASE;
    404         }
    405     }
    406 
    407     return (unsigned char*)bp;
    408 }
    409 
    410 
    411 static int
    412 _conv_num(const unsigned char **buf, int *dest, int llim, int ulim)
    413 {
    414     int result = 0;
    415     int rulim = ulim;
    416 
    417     if (**buf < '0' || **buf > '9')
    418         return (0);
    419 
    420     /* we use rulim to break out of the loop when we run out of digits */
    421     do {
    422         result *= 10;
    423         result += *(*buf)++ - '0';
    424         rulim /= 10;
    425     } while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9');
    426 
    427     if (result < llim || result > ulim)
    428         return (0);
    429 
    430     *dest = result;
    431     return (1);
    432 }
    433