X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/9514ca81bd12a47df977822efc14ff285bd5bc10..4d5d51e903a42c23800540930244448c4aacae16:/sbr/datetime.c diff --git a/sbr/datetime.c b/sbr/datetime.c index 48f02d45..cdcb8991 100644 --- a/sbr/datetime.c +++ b/sbr/datetime.c @@ -6,10 +6,14 @@ */ #include "h/mh.h" +#include "dtime.h" +#include "error.h" #include "h/icalendar.h" -#include +#include "datetime.h" +#include "h/fmt_scan.h" #include "h/tws.h" #include "h/utils.h" +#include "unquote.h" /* * This doesn't try to support all of the myriad date-time formats @@ -57,13 +61,14 @@ struct tzdesc { * struct tws. */ static int -parse_datetime (const char *datetime, const char *zone, int dst, - struct tws *tws) { +parse_datetime (const char *datetime, const char *zone, bool dst, + struct tws *tws) +{ char utc_indicator; - int form_1 = 0; + bool form_1; int items_matched; - memset(tws, 0, sizeof *tws); + ZERO(tws); items_matched = sscanf (datetime, "%4d%2d%2dT%2d%2d%2d%c", &tws->tw_year, &tws->tw_mon, &tws->tw_mday, @@ -71,23 +76,22 @@ parse_datetime (const char *datetime, const char *zone, int dst, &utc_indicator); tws->tw_flags = TW_NULL; + form_1 = false; if (items_matched == 7) { /* The 'Z' must be capital according to RFC 5545 Sec. 3.3.5. */ if (utc_indicator != 'Z') { - inform("%s has invalid timezone indicator of 0x%x", + inform("%s has invalid timezone-indicator byte: %#x", datetime, utc_indicator); return NOTOK; } } else if (zone == NULL) { - form_1 = 1; + form_1 = true; } /* 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; @@ -95,7 +99,8 @@ parse_datetime (const char *datetime, const char *zone, int dst, 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. */ - tws->tw_flags &= ~TW_SDAY, tws->tw_flags |= TW_SEXP; + tws->tw_flags &= ~TW_SDAY; + tws->tw_flags |= TW_SEXP; /* For the call to dmktime(): - don't need tw_yday @@ -103,7 +108,11 @@ parse_datetime (const char *datetime, const char *zone, int dst, - the only flag in tw_flags used is TW_DST */ tws->tw_yday = tws->tw_clock = 0; - tws->tw_zone = 60 * (offset / 100) + offset % 100; + if (zone) { + int offset = atoi(zone); + tws->tw_zone = 60 * (offset / 100) + offset % 100; + } else + tws->tw_zone = 0; if (dst) { tws->tw_zone -= 60; /* per dlocaltime() */ tws->tw_flags |= TW_DST; @@ -148,14 +157,15 @@ parse_datetime (const char *datetime, const char *zone, int dst, } tzdesc_t -load_timezones (const contentline *clines) { +load_timezones (const contentline *clines) +{ tzdesc_t timezones = NULL, timezone = NULL; - int in_vtimezone, in_standard, in_daylight; + bool in_vtimezone, in_standard, in_daylight; tzparams *params = NULL; const contentline *node; /* Interpret each VTIMEZONE section. */ - in_vtimezone = in_standard = in_daylight = 0; + in_vtimezone = in_standard = in_daylight = false; for (node = clines; node; node = node->next) { /* node->name will be NULL if the line was "deleted". */ if (! node->name) { continue; } @@ -166,21 +176,21 @@ load_timezones (const contentline *clines) { (in_daylight && ! strcasecmp ("DAYLIGHT", node->value)))) { struct tws tws; - if (in_standard) { in_standard = 0; } - else if (in_daylight) { in_daylight = 0; } - if (parse_datetime (params->dtstart, params->offsetfrom, - in_daylight ? 1 : 0, - &tws) == OK) { - if (tws.tw_year >= 1970) { - /* dmktime() falls apart for, e.g., the year 1601. */ - params->start_dt = tws.tw_clock; - } - } else { + if (in_standard) { in_standard = false; } + else if (in_daylight) { in_daylight = false; } + + if (parse_datetime(params->dtstart, params->offsetfrom, + in_daylight, &tws) != OK) { inform("failed to parse start time %s for %s", - params->dtstart, - in_standard ? "standard" : "daylight"); + params->dtstart, + in_daylight ? "daylight" : "standard"); return NULL; } + + if (tws.tw_year >= 1970) { + /* dmktime() falls apart for, e.g., the year 1601. */ + params->start_dt = tws.tw_clock; + } params = NULL; } else if (! strcasecmp ("DTSTART", node->name)) { /* Save DTSTART for use after getting TZOFFSETFROM. */ @@ -195,14 +205,14 @@ load_timezones (const contentline *clines) { } else if (in_vtimezone) { if (! strcasecmp ("END", node->name) && ! strcasecmp ("VTIMEZONE", node->value)) { - in_vtimezone = 0; + in_vtimezone = false; } else if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("STANDARD", node->value)) { - in_standard = 1; + in_standard = true; params = &timezone->standard_params; } else if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("DAYLIGHT", node->value)) { - in_daylight = 1; + in_daylight = true; params = &timezone->daylight_params; } else if (! strcasecmp ("TZID", node->name)) { /* See comment below in format_datetime() about removing any enclosing quotes from a @@ -215,7 +225,7 @@ load_timezones (const contentline *clines) { if (! strcasecmp ("BEGIN", node->name) && ! strcasecmp ("VTIMEZONE", node->value)) { - in_vtimezone = 1; + in_vtimezone = true; NEW0(timezone); if (timezones) { tzdesc_t t; @@ -235,7 +245,8 @@ load_timezones (const contentline *clines) { } void -free_timezones (tzdesc_t timezone) { +free_timezones (tzdesc_t timezone) +{ tzdesc_t next; for ( ; timezone; timezone = next) { @@ -263,9 +274,10 @@ free_timezones (tzdesc_t timezone) { * Given a recurrence rule and year, calculate its time in seconds * from 01 January UTC of the year. */ -time_t +static time_t rrule_clock (const char *rrule, const char *starttime, const char *zone, - unsigned int year) { + unsigned int year) +{ time_t clock = 0; if (nmh_strcasestr (rrule, "FREQ=YEARLY;INTERVAL=1") || @@ -276,14 +288,15 @@ rrule_clock (const char *rrule, const char *starttime, const char *zone, int specific_day = 1; /* BYDAY integer (prefix) */ char buf[32]; int day; + int end_of_week; if ((cp = nmh_strcasestr (rrule, "BYDAY="))) { cp += 6; /* BYDAY integers must be ASCII. */ - if (*cp == '+') { ++cp; } /* +n specific day; don't support '-' */ - else if (*cp == '-') { goto fail; } + if (*cp == '+') { ++cp; } /* +n specific day */ + else if (*cp == '-') { ++cp; specific_day = -1; } - if (isdigit ((unsigned char) *cp)) { specific_day = *cp++ - 0x30; } + if (isdigit ((unsigned char) *cp)) { specific_day *= *cp++ - 0x30; } if (! strncasecmp (cp, "SU", 2)) { wday = 0; } else if (! strncasecmp (cp, "MO", 2)) { wday = 1; } @@ -297,12 +310,13 @@ rrule_clock (const char *rrule, const char *starttime, const char *zone, month = atoi (cp + 8); } - for (day = 1; day <= 7; ++day) { + end_of_week = specific_day > 0 ? 7 * specific_day : + dmlastday(year, month) + 7 * (1 + specific_day); + for (day = end_of_week - 6; day <= end_of_week; ++day) { /* E.g, 11-01-2014 02:00:00-0400 */ snprintf (buf, sizeof buf, "%02d-%02d-%04u %.2s:%.2s:%.2s%s", - month, day + 7 * (specific_day-1), year, - starttime, starttime + 2, starttime + 4, - zone ? zone : "0000"); + month, day, year, starttime, starttime + 2, + starttime + 4, zone ? zone : "0000"); if ((tws = dparsetime (buf))) { if (! (tws->tw_flags & (TW_SEXP|TW_SIMP))) { set_dotw (tws); } @@ -313,22 +327,22 @@ rrule_clock (const char *rrule, const char *starttime, const char *zone, } } - if (day <= 7) { + if (day <= end_of_week) { clock = tws->tw_clock; } } -fail: if (clock == 0) { inform("Unsupported RRULE format: %s, assume local timezone, continuing...", - rrule); + rrule); } return clock; } char * -format_datetime (tzdesc_t timezones, const contentline *node) { +format_datetime (tzdesc_t timezones, const contentline *node) +{ param_list *p; char *dt_timezone = NULL; int dst = 0; @@ -355,11 +369,11 @@ format_datetime (tzdesc_t timezones, const contentline *node) { if (! dt_timezone) { /* Form #1: DATE WITH LOCAL TIME, i.e., no time zone, or Form #2: DATE WITH UTC TIME */ - if (parse_datetime (node->value, NULL, 0, &tws[0]) == OK) { - return strdup (dasctime (&tws[0], 0)); + if (parse_datetime(node->value, NULL, false, &tws[0]) != OK) { + inform("unable to parse datetime %s", node->value); + return NULL; } - inform("unable to parse datetime %s", node->value); - return NULL; + return strdup (dasctime (&tws[0], 0)); } /* @@ -377,13 +391,12 @@ format_datetime (tzdesc_t timezones, const contentline *node) { if (tz->tzid && ! strcasecmp (dt_timezone, tz->tzid)) { break; } } - if (tz) { - free(dt_timezone); - } else { + if (!tz) { inform("did not find VTIMEZONE section for %s", dt_timezone); free(dt_timezone); return NULL; } + free(dt_timezone); /* Determine if it's Daylight Saving. */ tp_std = strchr (tz->standard_params.dtstart, 'T'); @@ -428,29 +441,24 @@ format_datetime (tzdesc_t timezones, const contentline *node) { return NULL; } - if (parse_datetime (node->value, tz->standard_params.offsetto, - 0, &tws[0]) == OK) { - dt[0] = tws[0].tw_clock; - } else { + if (parse_datetime(node->value, tz->standard_params.offsetto, + false, &tws[0]) != OK) { inform("unable to parse datetime %s", node->value); return NULL; } + dt[0] = tws[0].tw_clock; if (tp_dst) { if (dt[0] < transition[1]) { dst = 0; } else { - if (parse_datetime (node->value, - tz->daylight_params.offsetto, 1, - &tws[1]) == OK) { - dt[1] = tws[1].tw_clock; - } else { - inform("unable to parse datetime %s", - node->value); + if (parse_datetime(node->value, + tz->daylight_params.offsetto, true, &tws[1]) != OK) { + inform("unable to parse datetime %s", node->value); return NULL; } - - dst = dt[1] > transition[0] ? 0 : 1; + dt[1] = tws[1].tw_clock; + dst = dt[1] <= transition[0]; } }