calendarspec: parse unix timestamps (@...) (#5947)
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 17 May 2017 09:40:49 +0000 (05:40 -0400)
committerLennart Poettering <lennart@poettering.net>
Wed, 17 May 2017 09:40:49 +0000 (11:40 +0200)
Fixes #5810.

src/basic/calendarspec.c
src/test/test-calendarspec.c
src/test/test-date.c

index 2323eb8..204120e 100644 (file)
@@ -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;
index f90b73a..a026ce4 100644 (file)
@@ -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);
index b77598c..a952f77 100644 (file)
@@ -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, ' ');