X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/aad5d20016b28bb8c28592c1ebe4213aafaa43c3..4db310433eb20ec95643299b0d14fefaea44b8bd:/sbr/datetime.c diff --git a/sbr/datetime.c b/sbr/datetime.c index 8b251362..20b4be66 100644 --- a/sbr/datetime.c +++ b/sbr/datetime.c @@ -1,5 +1,4 @@ -/* - * datetime.c -- functions for manipulating RFC 5545 date-time values +/* datetime.c -- functions for manipulating RFC 5545 date-time values * * This code is Copyright (c) 2014, by the authors of nmh. * See the COPYRIGHT file in the root directory of the nmh @@ -62,7 +61,10 @@ parse_datetime (const char *datetime, const char *zone, int dst, struct tws *tws) { char utc_indicator; int form_1 = 0; - int items_matched = + int items_matched; + + memset(tws, 0, sizeof *tws); + items_matched = sscanf (datetime, "%4d%2d%2dT%2d%2d%2d%c", &tws->tw_year, &tws->tw_mon, &tws->tw_mday, &tws->tw_hour, &tws->tw_min, &tws->tw_sec, @@ -72,7 +74,7 @@ parse_datetime (const char *datetime, const char *zone, int dst, if (items_matched == 7) { /* The 'Z' must be capital according to RFC 5545 Sec. 3.3.5. */ if (utc_indicator != 'Z') { - advise (NULL, "%s has invalid timezone indicator of 0x%x", + inform("%s has invalid timezone indicator of 0x%x", datetime, utc_indicator); return NOTOK; } @@ -80,12 +82,16 @@ parse_datetime (const char *datetime, const char *zone, int dst, form_1 = 1; } - if (items_matched >= 6) { + /* items_matched of 3 is for, e.g., 20151230. Assume that means + the entire day. The time fields of the tws struct were + initialized to 0 by the memset() above. */ + if (items_matched >= 6 || items_matched == 3) { int offset = atoi (zone ? zone : "0"); /* struct tws defines tw_mon over [0, 11]. */ --tws->tw_mon; + /* Fill out rest of tws, i.e., its tw_wday and tw_flags. */ set_dotw (tws); /* set_dotw() sets TW_SIMP. Replace that with TW_SEXP so that dasctime() outputs the dotw before the date instead of after. */ @@ -136,9 +142,9 @@ parse_datetime (const char *datetime, const char *zone, int dst, } return OK; - } else { - return NOTOK; } + + return NOTOK; } tzdesc_t @@ -170,7 +176,7 @@ load_timezones (const contentline *clines) { params->start_dt = tws.tw_clock; } } else { - advise (NULL, "failed to parse start time %s for %s", + inform("failed to parse start time %s for %s", params->dtstart, in_standard ? "standard" : "daylight"); return NULL; @@ -199,14 +205,18 @@ load_timezones (const contentline *clines) { in_daylight = 1; params = &timezone->daylight_params; } else if (! strcasecmp ("TZID", node->name)) { - timezone->tzid = strdup (node->value); + /* See comment below in format_datetime() about removing any enclosing quotes from a + timezone identifier. */ + char *buf = mh_xmalloc(strlen(node->value) + 1); + unquote_string(node->value, buf); + timezone->tzid = buf; } } else { if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("VTIMEZONE", node->value)) { in_vtimezone = 1; - timezone = mh_xcalloc (1, sizeof (struct tzdesc)); + NEW0(timezone); if (timezones) { tzdesc_t t; @@ -258,7 +268,8 @@ rrule_clock (const char *rrule, const char *starttime, const char *zone, unsigned int year) { time_t clock = 0; - if (nmh_strcasestr (rrule, "FREQ=YEARLY;INTERVAL=1")) { + if (nmh_strcasestr (rrule, "FREQ=YEARLY;INTERVAL=1") || + (nmh_strcasestr (rrule, "FREQ=YEARLY") && nmh_strcasestr(rrule, "INTERVAL") == NULL)) { struct tws *tws; const char *cp; int wday = -1, month = -1; @@ -309,9 +320,8 @@ rrule_clock (const char *rrule, const char *starttime, const char *zone, fail: if (clock == 0) { - admonish (NULL, - "Unsupported RRULE format: %s, assume local timezone", - rrule); + inform("Unsupported RRULE format: %s, assume local timezone, continuing...", + rrule); } return clock; @@ -329,7 +339,15 @@ format_datetime (tzdesc_t timezones, const contentline *node) { /* Extract the timezone, if specified (RFC 5545 Sec. 3.3.5 Form #3). */ for (p = node->params; p && p->param_name; p = p->next) { if (! strcasecmp (p->param_name, "TZID") && p->values) { - dt_timezone = p->values->value; + /* Remove any enclosing quotes from the timezone identifier. I don't believe that it's + legal for it to be quoted, according to RFC 5545 § 3.2.19: + tzidparam = "TZID" "=" [tzidprefix] paramtext + tzidprefix = "/" + where paramtext includes SAFE-CHAR, which specifically excludes DQUOTE. But we'll + be generous and strip quotes. */ + char *buf = mh_xmalloc(strlen(p->values->value) + 1); + unquote_string(p->values->value, buf); + dt_timezone = buf; break; } } @@ -339,10 +357,9 @@ format_datetime (tzdesc_t timezones, const contentline *node) { Form #2: DATE WITH UTC TIME */ if (parse_datetime (node->value, NULL, 0, &tws[0]) == OK) { return strdup (dasctime (&tws[0], 0)); - } else { - advise (NULL, "unable to parse datetime %s", node->value); - return NULL; } + inform("unable to parse datetime %s", node->value); + return NULL; } /* @@ -360,8 +377,11 @@ format_datetime (tzdesc_t timezones, const contentline *node) { if (tz->tzid && ! strcasecmp (dt_timezone, tz->tzid)) { break; } } - if (! tz) { - advise (NULL, "did not find VTIMEZONE section for %s", dt_timezone); + if (tz) { + free(dt_timezone); + } else { + inform("did not find VTIMEZONE section for %s", dt_timezone); + free(dt_timezone); return NULL; } @@ -402,7 +422,7 @@ format_datetime (tzdesc_t timezones, const contentline *node) { } if (transition[0] < transition[1]) { - advise (NULL, "format_datetime() requires that daylight " + inform("format_datetime() requires that daylight " "saving time transition precede standard time " "transition"); return NULL; @@ -412,7 +432,7 @@ format_datetime (tzdesc_t timezones, const contentline *node) { 0, &tws[0]) == OK) { dt[0] = tws[0].tw_clock; } else { - advise (NULL, "unable to parse datetime %s", node->value); + inform("unable to parse datetime %s", node->value); return NULL; } @@ -425,7 +445,7 @@ format_datetime (tzdesc_t timezones, const contentline *node) { &tws[1]) == OK) { dt[1] = tws[1].tw_clock; } else { - advise (NULL, "unable to parse datetime %s", + inform("unable to parse datetime %s", node->value); return NULL; } @@ -437,7 +457,7 @@ format_datetime (tzdesc_t timezones, const contentline *node) { if (dst) { if (tz->daylight_params.start_dt > 0 && dt[dst] < tz->daylight_params.start_dt) { - advise (NULL, "date-time of %s is before VTIMEZONE start " + inform("date-time of %s is before VTIMEZONE start " "of %s", node->value, tz->daylight_params.dtstart); return NULL; @@ -445,7 +465,7 @@ format_datetime (tzdesc_t timezones, const contentline *node) { } else { if (tz->standard_params.start_dt > 0 && dt[dst] < tz->standard_params.start_dt) { - advise (NULL, "date-time of %s is before VTIMEZONE start " + inform("date-time of %s is before VTIMEZONE start " "of %s", node->value, tz->standard_params.dtstart); return NULL; @@ -453,12 +473,12 @@ format_datetime (tzdesc_t timezones, const contentline *node) { } } else { if (! tp_std) { - advise (NULL, "unsupported date-time format: %s", + inform("unsupported date-time format: %s", tz->standard_params.dtstart); return NULL; } if (! tp_dt) { - advise (NULL, "unsupported date-time format: %s", node->value); + inform("unsupported date-time format: %s", node->value); return NULL; } }