1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-08 08:58:27 +03:00

calendarspec: parse unix timestamps (@...) (#5947)

Fixes #5810.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2017-05-17 05:40:49 -04:00 committed by Lennart Poettering
parent a8b7e8a742
commit d80e5b74e8
3 changed files with 93 additions and 13 deletions

View 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;

View 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);

View 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, ' ');