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