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 46struct strlong { 47 char* s; 48 long l; 49 }; 50 51 52static void 53pound_case( char* str ) 54 { 55 for ( ; *str != '\0'; ++str ) 56 { 57 if ( isupper( (int) *str ) ) 58 *str = tolower( (int) *str ); 59 } 60 } 61 62 63//static int 64//strlong_compare( const void* v1, const void* v2 ) 65// { 66// const struct strlong* s1 = (const struct strlong*) v1; 67// const struct strlong* s2 = (const struct strlong*) v2; 68// return strcmp( s1->s, s2->s ); 69// } 70 71 72static int 73strlong_search( char* str, struct strlong* tab, int n, long* lP ) 74 { 75 int i, h, l, r; 76 77 l = 0; 78 h = n - 1; 79 for (;;) 80 { 81 i = ( h + l ) / 2; 82 r = strcmp( str, tab[i].s ); 83 if ( r < 0 ) 84 h = i - 1; 85 else if ( r > 0 ) 86 l = i + 1; 87 else 88 { 89 *lP = tab[i].l; 90 return 1; 91 } 92 if ( h < l ) 93 return 0; 94 } 95 } 96 97 98static int 99scan_wday( char* str_wday, long* tm_wdayP ) 100 { 101// static struct strlong wday_tab[] = { 102// { "sun", 0 }, { "sunday", 0 }, 103// { "mon", 1 }, { "monday", 1 }, 104// { "tue", 2 }, { "tuesday", 2 }, 105// { "wed", 3 }, { "wednesday", 3 }, 106// { "thu", 4 }, { "thursday", 4 }, 107// { "fri", 5 }, { "friday", 5 }, 108// { "sat", 6 }, { "saturday", 6 }, 109// }; 110// static int sorted = 0; 111// 112// if ( ! sorted ) 113// { 114// (void) qsort( 115// wday_tab, sizeof(wday_tab)/sizeof(struct strlong), 116// sizeof(struct strlong), strlong_compare ); 117// sorted = 1; 118// } 119 120 /*manually sorted wday_tab to avoid concurrent accessing problem*/ 121 static const struct strlong wday_tab[] = { 122 { "fri", 5 }, { "friday", 5 }, 123 { "mon", 1 }, { "monday", 1 }, 124 { "sat", 6 }, { "saturday", 6 }, 125 { "sun", 0 }, { "sunday", 0 }, 126 { "thu", 4 }, { "thursday", 4 }, 127 { "tue", 2 }, { "tuesday", 2 }, 128 { "wed", 3 }, { "wednesday", 3 } 129 }; 130 131 pound_case( str_wday ); 132 return strlong_search( 133 str_wday, wday_tab, sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP ); 134 } 135 136 137static int 138scan_mon( char* str_mon, long* tm_monP ) 139 { 140// static struct strlong mon_tab[] = { 141// { "jan", 0 }, { "january", 0 }, 142// { "feb", 1 }, { "february", 1 }, 143// { "mar", 2 }, { "march", 2 }, 144// { "apr", 3 }, { "april", 3 }, 145// { "may", 4 }, 146// { "jun", 5 }, { "june", 5 }, 147// { "jul", 6 }, { "july", 6 }, 148// { "aug", 7 }, { "august", 7 }, 149// { "sep", 8 }, { "september", 8 }, 150// { "oct", 9 }, { "october", 9 }, 151// { "nov", 10 }, { "november", 10 }, 152// { "dec", 11 }, { "december", 11 }, 153// }; 154// static int sorted = 0; 155// 156// if ( ! sorted ) 157// { 158// (void) qsort( 159// mon_tab, sizeof(mon_tab)/sizeof(struct strlong), 160// sizeof(struct strlong), strlong_compare ); 161// sorted = 1; 162// } 163 164 /*manually sorted mon_tab to avoid concurrent accessing problem*/ 165 static const struct strlong mon_tab[] = { 166 { "apr", 3 }, { "april", 3 }, 167 { "aug", 7 }, { "august", 7 }, 168 { "dec", 11}, { "december", 11 }, 169 { "feb", 1 }, { "february", 1 }, 170 { "jan", 0 }, { "january", 0 }, 171 { "jul", 6 }, { "july", 6 }, 172 { "jun", 5 }, { "june", 5 }, 173 { "mar", 2 }, { "march", 2 }, 174 { "may", 4 }, 175 { "nov", 10}, { "november", 10 }, 176 { "oct", 9 }, { "october", 9 }, 177 { "sep", 8 }, { "september", 8 } 178 }; 179 180 pound_case( str_mon ); 181 return strlong_search( 182 str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP ); 183 } 184 185 186static int 187is_leap( int year ) 188 { 189 return year % 400? ( year % 100 ? ( year % 4 ? 0 : 1 ) : 0 ) : 1; 190 } 191 192 193/* Basically the same as mktime(). */ 194static time_t 195tm_to_time( struct tm* tmP ) 196 { 197 time_t t; 198 static int monthtab[12] = { 199 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 200 201 /* Years since epoch, converted to days. */ 202 t = ( tmP->tm_year - 70 ) * 365; 203 /* Leap days for previous years - this will break in 2100! */ 204 t += ( tmP->tm_year - 69 ) / 4; 205 /* Days for the beginning of this month. */ 206 t += monthtab[tmP->tm_mon]; 207 /* Leap day for this year. */ 208 if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) ) 209 ++t; 210 /* Days since the beginning of this month. */ 211 t += tmP->tm_mday - 1; /* 1-based field */ 212 /* Hours, minutes, and seconds. */ 213 t = t * 24 + tmP->tm_hour; 214 t = t * 60 + tmP->tm_min; 215 t = t * 60 + tmP->tm_sec; 216 217 return t; 218 } 219 220 221time_t 222tdate_parse( char* str ) 223 { 224 struct tm tm; 225 char* cp; 226 char str_mon[500], str_wday[500]; 227 int tm_sec, tm_min, tm_hour, tm_mday, tm_year; 228 long tm_mon, tm_wday; 229 time_t t; 230 231 /* Initialize. */ 232 (void) memset( (char*) &tm, 0, sizeof(struct tm) ); 233 234 /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ 235 for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp ) 236 continue; 237 238 /* And do the sscanfs. WARNING: you can add more formats here, 239 ** but be careful! You can easily screw up the parsing of existing 240 ** formats when you add new ones. The order is important. 241 */ 242 243 /* DD-mth-YY HH:MM:SS GMT */ 244 if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", 245 &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 246 &tm_sec ) == 6 && 247 scan_mon( str_mon, &tm_mon ) ) 248 { 249 tm.tm_mday = tm_mday; 250 tm.tm_mon = tm_mon; 251 tm.tm_year = tm_year; 252 tm.tm_hour = tm_hour; 253 tm.tm_min = tm_min; 254 tm.tm_sec = tm_sec; 255 } 256 257 /* DD mth YY HH:MM:SS GMT */ 258 else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", 259 &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 260 &tm_sec) == 6 && 261 scan_mon( str_mon, &tm_mon ) ) 262 { 263 tm.tm_mday = tm_mday; 264 tm.tm_mon = tm_mon; 265 tm.tm_year = tm_year; 266 tm.tm_hour = tm_hour; 267 tm.tm_min = tm_min; 268 tm.tm_sec = tm_sec; 269 } 270 271 /* HH:MM:SS GMT DD-mth-YY */ 272 else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", 273 &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, 274 &tm_year ) == 6 && 275 scan_mon( str_mon, &tm_mon ) ) 276 { 277 tm.tm_hour = tm_hour; 278 tm.tm_min = tm_min; 279 tm.tm_sec = tm_sec; 280 tm.tm_mday = tm_mday; 281 tm.tm_mon = tm_mon; 282 tm.tm_year = tm_year; 283 } 284 285 /* HH:MM:SS GMT DD mth YY */ 286 else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", 287 &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, 288 &tm_year ) == 6 && 289 scan_mon( str_mon, &tm_mon ) ) 290 { 291 tm.tm_hour = tm_hour; 292 tm.tm_min = tm_min; 293 tm.tm_sec = tm_sec; 294 tm.tm_mday = tm_mday; 295 tm.tm_mon = tm_mon; 296 tm.tm_year = tm_year; 297 } 298 299 /* wdy, DD-mth-YY HH:MM:SS GMT */ 300 else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", 301 str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 302 &tm_sec ) == 7 && 303 scan_wday( str_wday, &tm_wday ) && 304 scan_mon( str_mon, &tm_mon ) ) 305 { 306 tm.tm_wday = tm_wday; 307 tm.tm_mday = tm_mday; 308 tm.tm_mon = tm_mon; 309 tm.tm_year = tm_year; 310 tm.tm_hour = tm_hour; 311 tm.tm_min = tm_min; 312 tm.tm_sec = tm_sec; 313 } 314 315 /* wdy, DD mth YY HH:MM:SS GMT */ 316 else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", 317 str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 318 &tm_sec ) == 7 && 319 scan_wday( str_wday, &tm_wday ) && 320 scan_mon( str_mon, &tm_mon ) ) 321 { 322 tm.tm_wday = tm_wday; 323 tm.tm_mday = tm_mday; 324 tm.tm_mon = tm_mon; 325 tm.tm_year = tm_year; 326 tm.tm_hour = tm_hour; 327 tm.tm_min = tm_min; 328 tm.tm_sec = tm_sec; 329 } 330 331 /* wdy mth DD HH:MM:SS GMT YY */ 332 else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", 333 str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, 334 &tm_year ) == 7 && 335 scan_wday( str_wday, &tm_wday ) && 336 scan_mon( str_mon, &tm_mon ) ) 337 { 338 tm.tm_wday = tm_wday; 339 tm.tm_mon = tm_mon; 340 tm.tm_mday = tm_mday; 341 tm.tm_hour = tm_hour; 342 tm.tm_min = tm_min; 343 tm.tm_sec = tm_sec; 344 tm.tm_year = tm_year; 345 } 346 else 347 return (time_t) -1; 348 349 if ( tm.tm_year > 1900 ) 350 tm.tm_year -= 1900; 351 else if ( tm.tm_year < 70 ) 352 tm.tm_year += 100; 353 354 t = tm_to_time( &tm ); 355 356 return t; 357 } 358