root/tdate_parse.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pound_case
  2. strlong_compare
  3. strlong_search
  4. scan_wday
  5. scan_mon
  6. is_leap
  7. tm_to_time
  8. tdate_parse

   1 /* tdate_parse - parse string dates into internal form, stripped-down version
   2 **
   3 ** Copyright (c) 1995 by Jef Poskanzer <jef@mail.acme.com>.
   4 ** Copyright (c) 2023 by Amelia Zabardast Ziabari <ame@psianesia.org>.
   5 ** All rights reserved.
   6 **
   7 ** Redistribution and use in source and binary forms, with or without
   8 ** modification, are permitted provided that the following conditions
   9 ** are met:
  10 ** 1. Redistributions of source code must retain the above copyright
  11 **    notice, this list of conditions and the following disclaimer.
  12 ** 2. Redistributions in binary form must reproduce the above copyright
  13 **    notice, this list of conditions and the following disclaimer in the
  14 **    documentation and/or other materials provided with the distribution.
  15 **
  16 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26 ** SUCH DAMAGE.
  27 */
  28 
  29 /* This is a stripped-down version of date_parse.c, available at
  30 ** http://www.acme.com/software/date_parse/
  31 */
  32 
  33 #include <sys/types.h>
  34 
  35 #include <ctype.h>
  36 #ifdef HAVE_MEMORY_H
  37 #include <memory.h>
  38 #endif
  39 #include <stdio.h>
  40 #include <stdlib.h>
  41 #include <string.h>
  42 #include <time.h>
  43 
  44 #include "tdate_parse.h"
  45 
  46 
  47 struct strlong {
  48     char* s;
  49     long l;
  50     };
  51 
  52 
  53 static void
  54 pound_case( char* str )
  55     {
  56     for ( ; *str != '\0'; ++str )
  57         {
  58         if ( isupper( (int) *str ) )
  59             *str = tolower( (int) *str );
  60         }
  61     }
  62 
  63 
  64 static int
  65 strlong_compare( const void* v1, const void* v2 )
  66     {
  67     const struct strlong* s1 = (const struct strlong*) v1;
  68     const struct strlong* s2 = (const struct strlong*) v2;
  69     return strcmp( s1->s, s2->s );
  70     }
  71 
  72 
  73 static int
  74 strlong_search( char* str, struct strlong* tab, int n, long* lP )
  75     {
  76     int i, h, l, r;
  77 
  78     l = 0;
  79     h = n - 1;
  80     for (;;)
  81         {
  82         i = ( h + l ) / 2;
  83         r = strcmp( str, tab[i].s );
  84         if ( r < 0 )
  85             h = i - 1;
  86         else if ( r > 0 )
  87             l = i + 1;
  88         else
  89             {
  90             *lP = tab[i].l;
  91             return 1;
  92             }
  93         if ( h < l )
  94             return 0;
  95         }
  96     }
  97 
  98 
  99 static int
 100 scan_wday( char* str_wday, long* tm_wdayP )
 101     {
 102     static struct strlong wday_tab[] = {
 103         { "sun", 0 }, { "sunday", 0 },
 104         { "mon", 1 }, { "monday", 1 },
 105         { "tue", 2 }, { "tuesday", 2 },
 106         { "wed", 3 }, { "wednesday", 3 },
 107         { "thu", 4 }, { "thursday", 4 },
 108         { "fri", 5 }, { "friday", 5 },
 109         { "sat", 6 }, { "saturday", 6 },
 110         };
 111     static int sorted = 0;
 112 
 113     if ( ! sorted )
 114         {
 115         (void) qsort(
 116             wday_tab, sizeof(wday_tab)/sizeof(struct strlong),
 117             sizeof(struct strlong), strlong_compare );
 118         sorted = 1;
 119         }
 120     pound_case( str_wday );
 121     return strlong_search(
 122         str_wday, wday_tab,
 123         sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP );
 124     }
 125 
 126 
 127 static int
 128 scan_mon( char* str_mon, long* tm_monP )
 129     {
 130     static struct strlong mon_tab[] = {
 131         { "jan", 0 }, { "january", 0 },
 132         { "feb", 1 }, { "february", 1 },
 133         { "mar", 2 }, { "march", 2 },
 134         { "apr", 3 }, { "april", 3 },
 135         { "may", 4 },
 136         { "jun", 5 }, { "june", 5 },
 137         { "jul", 6 }, { "july", 6 },
 138         { "aug", 7 }, { "august", 7 },
 139         { "sep", 8 }, { "september", 8 },
 140         { "oct", 9 }, { "october", 9 },
 141         { "nov", 10 }, { "november", 10 },
 142         { "dec", 11 }, { "december", 11 },
 143         };
 144     static int sorted = 0;
 145 
 146     if ( ! sorted )
 147         {
 148         (void) qsort(
 149             mon_tab, sizeof(mon_tab)/sizeof(struct strlong),
 150             sizeof(struct strlong), strlong_compare );
 151         sorted = 1;
 152         }
 153     pound_case( str_mon );
 154     return strlong_search(
 155         str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP );
 156     }
 157 
 158 
 159 static int
 160 is_leap( int year )
 161     {
 162     return (year % 400) ? ( (year % 100) ? ( (year % 4) ? 0 : 1 ) : 0 ) : 1;
 163     }
 164 
 165 
 166 /* Basically the same as mktime(). */
 167 static time_t
 168 tm_to_time( struct tm* tmP )
 169     {
 170     time_t t;
 171     static int monthtab[12] = {
 172         0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
 173 
 174     /* Years since epoch, converted to days. */
 175     t = ( tmP->tm_year - 70 ) * 365;
 176     /* Leap days for previous years - this will break in 2100! */
 177     t += ( tmP->tm_year - 69 ) / 4;
 178     /* Days for the beginning of this month. */
 179     t += monthtab[tmP->tm_mon];
 180     /* Leap day for this year. */
 181     if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) )
 182         ++t;
 183     /* Days since the beginning of this month. */
 184     t += tmP->tm_mday - 1;      /* 1-based field */
 185     /* Hours, minutes, and seconds. */
 186     t = t * 24 + tmP->tm_hour;
 187     t = t * 60 + tmP->tm_min;
 188     t = t * 60 + tmP->tm_sec;
 189 
 190     return t;
 191     }
 192 
 193 
 194 time_t
 195 tdate_parse( char* str )
 196     {
 197     struct tm tm;
 198     char* cp;
 199     char str_mon[500], str_wday[500];
 200     int tm_sec, tm_min, tm_hour, tm_mday, tm_year;
 201     long tm_mon, tm_wday;
 202     time_t t;
 203 
 204     /* Initialize. */
 205     (void) memset( (char*) &tm, 0, sizeof(struct tm) );
 206 
 207     /* Skip initial whitespace ourselves - sscanf is clumsy at this. */
 208     for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp )
 209         continue;
 210 
 211     /* And do the sscanfs.  WARNING: you can add more formats here,
 212     ** but be careful!  You can easily screw up the parsing of existing
 213     ** formats when you add new ones.  The order is important.
 214     */
 215 
 216     /* DD-mth-YY HH:MM:SS GMT */
 217     if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT",
 218                 &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
 219                 &tm_sec ) == 6 &&
 220             scan_mon( str_mon, &tm_mon ) )
 221         {
 222         tm.tm_mday = tm_mday;
 223         tm.tm_mon = tm_mon;
 224         tm.tm_year = tm_year;
 225         tm.tm_hour = tm_hour;
 226         tm.tm_min = tm_min;
 227         tm.tm_sec = tm_sec;
 228         }
 229 
 230     /* DD mth YY HH:MM:SS GMT */
 231     else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT",
 232                 &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
 233                 &tm_sec) == 6 &&
 234             scan_mon( str_mon, &tm_mon ) )
 235         {
 236         tm.tm_mday = tm_mday;
 237         tm.tm_mon = tm_mon;
 238         tm.tm_year = tm_year;
 239         tm.tm_hour = tm_hour;
 240         tm.tm_min = tm_min;
 241         tm.tm_sec = tm_sec;
 242         }
 243 
 244     /* HH:MM:SS GMT DD-mth-YY */
 245     else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d",
 246                 &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
 247                 &tm_year ) == 6 &&
 248             scan_mon( str_mon, &tm_mon ) )
 249         {
 250         tm.tm_hour = tm_hour;
 251         tm.tm_min = tm_min;
 252         tm.tm_sec = tm_sec;
 253         tm.tm_mday = tm_mday;
 254         tm.tm_mon = tm_mon;
 255         tm.tm_year = tm_year;
 256         }
 257 
 258     /* HH:MM:SS GMT DD mth YY */
 259     else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d",
 260                 &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
 261                 &tm_year ) == 6 &&
 262             scan_mon( str_mon, &tm_mon ) )
 263         {
 264         tm.tm_hour = tm_hour;
 265         tm.tm_min = tm_min;
 266         tm.tm_sec = tm_sec;
 267         tm.tm_mday = tm_mday;
 268         tm.tm_mon = tm_mon;
 269         tm.tm_year = tm_year;
 270         }
 271 
 272     /* wdy, DD-mth-YY HH:MM:SS GMT */
 273     else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT",
 274                 str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
 275                 &tm_sec ) == 7 &&
 276             scan_wday( str_wday, &tm_wday ) &&
 277             scan_mon( str_mon, &tm_mon ) )
 278         {
 279         tm.tm_wday = tm_wday;
 280         tm.tm_mday = tm_mday;
 281         tm.tm_mon = tm_mon;
 282         tm.tm_year = tm_year;
 283         tm.tm_hour = tm_hour;
 284         tm.tm_min = tm_min;
 285         tm.tm_sec = tm_sec;
 286         }
 287 
 288     /* wdy, DD mth YY HH:MM:SS GMT */
 289     else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT",
 290                 str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
 291                 &tm_sec ) == 7 &&
 292             scan_wday( str_wday, &tm_wday ) &&
 293             scan_mon( str_mon, &tm_mon ) )
 294         {
 295         tm.tm_wday = tm_wday;
 296         tm.tm_mday = tm_mday;
 297         tm.tm_mon = tm_mon;
 298         tm.tm_year = tm_year;
 299         tm.tm_hour = tm_hour;
 300         tm.tm_min = tm_min;
 301         tm.tm_sec = tm_sec;
 302         }
 303 
 304     /* wdy mth DD HH:MM:SS GMT YY */
 305     else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d",
 306                 str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec,
 307                 &tm_year ) == 7 &&
 308             scan_wday( str_wday, &tm_wday ) &&
 309             scan_mon( str_mon, &tm_mon ) )
 310         {
 311         tm.tm_wday = tm_wday;
 312         tm.tm_mon = tm_mon;
 313         tm.tm_mday = tm_mday;
 314         tm.tm_hour = tm_hour;
 315         tm.tm_min = tm_min;
 316         tm.tm_sec = tm_sec;
 317         tm.tm_year = tm_year;
 318         }
 319     else
 320         return (time_t) -1;
 321 
 322     if ( tm.tm_year > 1900 )
 323         tm.tm_year -= 1900;
 324     else if ( tm.tm_year < 70 )
 325         tm.tm_year += 100;
 326 
 327     t = tm_to_time( &tm );
 328 
 329     return t;
 330     }

/* [previous][next][first][last][top][bottom][index][help] */