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