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