From: Zbigniew Jędrzejewski-Szmek Date: Wed, 17 May 2017 09:40:49 +0000 (-0400) Subject: calendarspec: parse unix timestamps (@...) (#5947) X-Git-Tag: v234~198 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d80e5b74e82fb4403dfa1be9d0eb7cb121e9181b;p=thirdparty%2Fsystemd.git calendarspec: parse unix timestamps (@...) (#5947) Fixes #5810. --- diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 2323eb85556..204120ee0e9 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -487,22 +487,33 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { } } +static int parse_one_number(const char *p, const char **e, unsigned long *ret) { + char *ee = NULL; + unsigned long value; + + errno = 0; + value = strtoul(p, &ee, 10); + if (errno > 0) + return -errno; + if (ee == p) + return -EINVAL; + + *ret = value; + *e = ee; + return 0; +} + static int parse_component_decimal(const char **p, bool usec, int *res) { unsigned long value; const char *e = NULL; - char *ee = NULL; int r; if (!isdigit(**p)) return -EINVAL; - errno = 0; - value = strtoul(*p, &ee, 10); - if (errno > 0) - return -errno; - if (ee == *p) - return -EINVAL; - e = ee; + r = parse_one_number(*p, &e, &value); + if (r < 0) + return r; if (usec) { if (value * USEC_PER_SEC / USEC_PER_SEC != value) @@ -553,6 +564,47 @@ static int const_chain(int value, CalendarComponent **c) { return 0; } +static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { + struct tm tm; + CalendarComponent *year = NULL, *month = NULL, *day = NULL, *hour = NULL, *minute = NULL, *us = NULL; + int r; + + assert_se(gmtime_r(&time, &tm)); + + r = const_chain(tm.tm_year + 1900, &year); + if (r < 0) + return r; + + r = const_chain(tm.tm_mon + 1, &month); + if (r < 0) + return r; + + r = const_chain(tm.tm_mday, &day); + if (r < 0) + return r; + + r = const_chain(tm.tm_hour, &hour); + if (r < 0) + return r; + + r = const_chain(tm.tm_min, &minute); + if (r < 0) + return r; + + r = const_chain(tm.tm_sec * USEC_PER_SEC, &us); + if (r < 0) + return r; + + c->utc = true; + c->year = year; + c->month = month; + c->day = day; + c->hour = hour; + c->minute = minute; + c->microsecond = us; + return 0; +} + static int prepend_component(const char **p, bool usec, CalendarComponent **c) { int r, start, stop = -1, repeat = 0; CalendarComponent *cc; @@ -657,6 +709,27 @@ static int parse_date(const char **p, CalendarSpec *c) { if (*t == 0) return 0; + /* @TIMESTAMP — UNIX time in seconds since the epoch */ + if (*t == '@') { + unsigned long value; + time_t time; + + r = parse_one_number(t + 1, &t, &value); + if (r < 0) + return r; + + time = value; + if ((unsigned long) time != value) + return -ERANGE; + + r = calendarspec_from_time_t(c, time); + if (r < 0) + return r; + + *p = t; + return 1; /* finito, don't parse H:M:S after that */ + } + r = parse_chain(&t, false, &first); if (r < 0) return r; @@ -832,7 +905,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { continue; e = endswith_no_case(p, tzname[j]); - if(!e) + if (!e) continue; if (e == p) continue; @@ -986,9 +1059,11 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (r < 0) goto fail; - r = parse_calendar_time(&p, c); - if (r < 0) - goto fail; + if (r == 0) { + r = parse_calendar_time(&p, c); + if (r < 0) + goto fail; + } if (*p != 0) { r = -EINVAL; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index f90b73aeaf3..a026ce4ef12 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -193,6 +193,11 @@ int main(int argc, char* argv[]) { test_one("*:20..39/5", "*-*-* *:20..35/5:00"); test_one("00:00:20..40/1", "*-*-* 00:00:20..40"); test_one("*~03/1,03..05", "*-*~03/1,03..05 00:00:00"); + /* UNIX timestamps are always UTC */ + test_one("@1493187147", "2017-04-26 06:12:27 UTC"); + test_one("@1493187147 UTC", "2017-04-26 06:12:27 UTC"); + test_one("@0", "1970-01-01 00:00:00 UTC"); + test_one("@0 UTC", "1970-01-01 00:00:00 UTC"); test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); diff --git a/src/test/test-date.c b/src/test/test-date.c index b77598c81dd..a952f779dc7 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -29,7 +29,7 @@ static void test_should_pass(const char *p) { assert_se(parse_timestamp(p, &t) >= 0); format_timestamp_us(buf, sizeof(buf), t); - log_info("%s", buf); + log_info("\"%s\" → \"%s\"", p, buf); /* Chop off timezone */ sp = strrchr(buf, ' ');