1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2000 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
37 #include <libedataserver/e-time-utils.h>
39 #include "broken-date-parser.h"
43 #define NUMERIC_CHARS "1234567890"
44 #define WEEKDAY_CHARS "SundayMondayTuesdayWednesdayThursdayFridaySaturday"
45 #define MONTH_CHARS "JanuaryFebruaryMarchAprilMayJuneJulyAugustSeptemberOctoberNovemberDecember"
46 #define TIMEZONE_ALPHA_CHARS "UTCGMTESTEDTCSTCDTMSTPSTPDTZAMNY()"
47 #define TIMEZONE_NUMERIC_CHARS "-+1234567890"
48 #define TIME_CHARS "1234567890:"
50 #define DATE_TOKEN_NON_NUMERIC (1 << 0)
51 #define DATE_TOKEN_NON_WEEKDAY (1 << 1)
52 #define DATE_TOKEN_NON_MONTH (1 << 2)
53 #define DATE_TOKEN_NON_TIME (1 << 3)
54 #define DATE_TOKEN_HAS_COLON (1 << 4)
55 #define DATE_TOKEN_NON_TIMEZONE_ALPHA (1 << 5)
56 #define DATE_TOKEN_NON_TIMEZONE_NUMERIC (1 << 6)
57 #define DATE_TOKEN_HAS_SIGN (1 << 7)
59 static const unsigned char datetok_table[256] = {
60 128,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
61 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
62 111,111,111,111,111,111,111,111, 79, 79,111,175,111,175,111,111,
63 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,119,111,111,111,111,111,
64 111, 75,111, 79, 75, 79,105, 79,111,111,107,111,111, 73, 75,107,
65 79,111,111, 73, 77, 79,111,109,111, 79, 79,111,111,111,111,111,
66 111,105,107,107,109,105,111,107,105,105,111,111,107,107,105,105,
67 107,111,105,105,105,105,107,111,111,105,111,111,111,111,111,111,
68 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
69 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
70 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
71 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
72 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
73 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
74 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
75 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
78 /* hrm, is there a library for this shit? */
85 { "EST", -500 }, /* these are all US timezones. bloody yanks */
100 static const char tm_months[][4] = {
101 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
102 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
105 static const char tm_days[][4] = {
106 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
111 struct _date_token *next;
112 const unsigned char *start;
117 /* This is where it gets ugly... */
118 static struct _date_token *
119 datetok (const char *date)
121 struct _date_token *tokens = NULL, *token, *tail = (struct _date_token *) &tokens;
122 const unsigned char *start, *end;
127 /* kill leading whitespace */
128 while (*start && isspace ((int) *start))
134 mask = datetok_table[*start];
136 /* find the end of this token */
138 while (*end && !strchr ("-/,\t\r\n ", *end))
139 mask |= datetok_table[*end++];
142 token = g_malloc (sizeof (struct _date_token));
144 token->start = start;
145 token->len = end - start;
162 decode_int (const unsigned char *in, unsigned int inlen)
164 register const unsigned char *inptr;
165 const unsigned char *inend;
166 int sign = 1, val = 0;
174 } else if (*inptr == '+')
177 for ( ; inptr < inend; inptr++) {
178 if (!isdigit ((int) *inptr))
181 val = (val * 10) + (*inptr - '0');
190 get_wday (const unsigned char *in, unsigned int inlen)
197 for (wday = 0; wday < 7; wday++)
198 if (!g_ascii_strncasecmp (in, tm_days[wday], 3))
201 return -1; /* unknown week day */
205 get_mday (const unsigned char *in, unsigned int inlen)
209 mday = decode_int (in, inlen);
211 if (mday < 0 || mday > 31)
218 get_month (const unsigned char *in, unsigned int inlen)
225 for (i = 0; i < 12; i++)
226 if (!g_ascii_strncasecmp (in, tm_months[i], 3))
229 return -1; /* unknown month */
233 get_year (const unsigned char *in, unsigned int inlen)
237 year = decode_int (in, inlen);
242 year += (year < 70) ? 2000 : 1900;
251 get_time (const unsigned char *in, unsigned int inlen, int *hour, int *min, int *sec)
253 register const unsigned char *inptr;
254 const unsigned char *inend;
255 int *val, colons = 0;
257 *hour = *min = *sec = 0;
261 for (inptr = in; inptr < inend; inptr++) {
274 } else if (!isdigit ((int) *inptr))
277 *val = (*val * 10) + (*inptr - '0');
284 get_tzone (struct _date_token **token)
286 const unsigned char *inptr, *inend;
290 for (i = 0; *token && i < 2; *token = (*token)->next, i++) {
291 inptr = (*token)->start;
292 inlen = (*token)->len;
293 inend = inptr + inlen;
295 if (*inptr == '+' || *inptr == '-') {
296 t = decode_int (inptr, inlen);
297 if (t < -1200 || t > 1400)
304 if (*(inend - 1) == ')')
310 for (t = 0; t < 15; t++) {
311 unsigned int len = strlen (tz_offsets[t].name);
316 if (!strncmp (inptr, tz_offsets[t].name, len))
317 return tz_offsets[t].offset;
325 /* This is where things get interesting... ;-) */
327 #define date_token_mask(t) (((struct _date_token *) t)->mask)
328 #define is_numeric(t) ((date_token_mask (t) & DATE_TOKEN_NON_NUMERIC) == 0)
329 #define is_weekday(t) ((date_token_mask (t) & DATE_TOKEN_NON_WEEKDAY) == 0)
330 #define is_month(t) ((date_token_mask (t) & DATE_TOKEN_NON_MONTH) == 0)
331 #define is_time(t) (((date_token_mask (t) & DATE_TOKEN_NON_TIME) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_COLON))
332 #define is_tzone_alpha(t) ((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_ALPHA) == 0)
333 #define is_tzone_numeric(t) (((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_NUMERIC) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_SIGN))
334 #define is_tzone(t) (is_tzone_alpha (t) || is_tzone_numeric (t))
337 decode_broken_date (struct _date_token *tokens, int *tzone)
339 gboolean got_wday, got_month, got_tzone;
340 int hour, min, sec, offset, n;
341 struct _date_token *token;
345 memset ((void *) &tm, 0, sizeof (struct tm));
346 got_wday = got_month = got_tzone = FALSE;
351 if (is_weekday (token) && !got_wday) {
352 if ((n = get_wday (token->start, token->len)) != -1) {
353 d(printf ("weekday; "));
360 if (is_month (token) && !got_month) {
361 if ((n = get_month (token->start, token->len)) != -1) {
362 d(printf ("month; "));
369 if (is_time (token) && !tm.tm_hour && !tm.tm_min && !tm.tm_sec) {
370 if (get_time (token->start, token->len, &hour, &min, &sec)) {
371 d(printf ("time; "));
379 if (is_tzone (token) && !got_tzone) {
380 struct _date_token *t = token;
382 if ((n = get_tzone (&t)) != -1) {
383 d(printf ("tzone; "));
390 if (is_numeric (token)) {
391 if (token->len == 4 && !tm.tm_year) {
392 if ((n = get_year (token->start, token->len)) != -1) {
393 d(printf ("year; "));
394 tm.tm_year = n - 1900;
398 if (!got_month && !got_wday && token->next && is_numeric (token->next)) {
400 n = decode_int (token->start, token->len);
404 } else if (!tm.tm_mday && (n = get_mday (token->start, token->len)) != -1) {
405 d(printf ("mday; "));
408 } else if (!tm.tm_year) {
409 d(printf ("2-digit year; "));
410 n = get_year (token->start, token->len);
411 tm.tm_year = n - 1900;
426 time = e_mktime_utc (&tm);
428 /* time is now GMT of the time we want, but not offset by the timezone ... */
430 /* this should convert the time to the GMT equiv time */
431 time -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
442 * @datestr: input date string
445 * Decodes the rfc822/broken date string and saves the GMT offset into
446 * @saveoffset if non-NULL.
448 * Returns the time_t representation of the date string specified by
449 * @in. If 'saveoffset' is non-NULL, the value of the timezone offset
453 parse_broken_date (const char *datestr, int *saveoffset)
455 struct _date_token *token, *tokens;
458 tokens = datetok (datestr);
460 date = decode_broken_date (tokens, saveoffset);
465 tokens = tokens->next;
476 #ifdef DATETOK_STANDALONE
483 memset (datetok_table, 0, sizeof (datetok_table));
485 for (i = 0; i < 256; i++) {
486 if (!strchr (NUMERIC_CHARS, i))
487 datetok_table[i] |= DATE_TOKEN_NON_NUMERIC;
489 if (!strchr (WEEKDAY_CHARS, i))
490 datetok_table[i] |= DATE_TOKEN_NON_WEEKDAY;
492 if (!strchr (MONTH_CHARS, i))
493 datetok_table[i] |= DATE_TOKEN_NON_MONTH;
495 if (!strchr (TIME_CHARS, i))
496 datetok_table[i] |= DATE_TOKEN_NON_TIME;
498 if (!strchr (TIMEZONE_ALPHA_CHARS, i))
499 datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_ALPHA;
501 if (!strchr (TIMEZONE_NUMERIC_CHARS, i))
502 datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_NUMERIC;
504 if (((char) i) == ':')
505 datetok_table[i] |= DATE_TOKEN_HAS_COLON;
507 if (strchr ("+-", i))
508 datetok_table[i] |= DATE_TOKEN_HAS_SIGN;
511 printf ("static const unsigned int datetok_table[256] = {");
512 for (i = 0; i < 256; i++) {
515 printf ("%3d,", datetok_table[i]);
521 int main (int argc, char **argv)
528 date = parse_broken_date (argv[1], &offset);
529 printf ("%d; %d\n", date, offset);
534 #endif /* DATETOK_STANDALONE */