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