diff --git a/lib/timeutils.c b/lib/timeutils.c index 79b674907..f61a77ab9 100644 --- a/lib/timeutils.c +++ b/lib/timeutils.c @@ -147,6 +147,26 @@ static int parse_sec(const char *t, usec_t *usec) return 0; } +static int parse_subseconds(const char *t, usec_t *usec) +{ + usec_t ret = 0; + int factor = USEC_PER_SEC / 10; + + if (*t != '.' && *t != ',') + return -1; + + while (*(++t)) { + if (!isdigit(*t) || factor < 1) + return -1; + + ret += ((usec_t) *t - '0') * factor; + factor /= 10; + } + + *usec = ret; + return 0; +} + static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) { static const struct { @@ -171,27 +191,35 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) const char *k; struct tm tm, copy; - usec_t plus = 0, minus = 0, ret; + usec_t plus = 0, minus = 0, ret = 0; int r, weekday = -1; unsigned i; /* * Allowed syntaxes: * - * 2012-09-22 16:34:22 - * 2012-09-22T16:34:22 - * @1348331662 (seconds since the Epoch (1970-01-01 00:00 UTC)) - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) + * 2012-09-22 16:34:22 ! + * 2012-09-22T16:34:22 ! + * 20120922163422 ! + * @1348331662 ! (seconds since the Epoch (1970-01-01 00:00 UTC)) + * 2012-09-22 16:34 (seconds will be set to 0) + * 2012-09-22 (time will be set to 00:00:00) + * 16:34:22 ! (date will be set to today) + * 16:34 (date will be set to today, seconds to 0) * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) + * yesterday (time is set to 00:00:00) + * today (time is set to 00:00:00) + * tomorrow (time is set to 00:00:00) * +5min * -5days * + * Syntaxes marked with '!' also optionally allow up to six digits of + * subsecond granularity, separated by '.' or ',': + * + * 2012-09-22 16:34:22.12 + * 2012-09-22 16:34:22.123456 + * + * */ assert(t); @@ -235,6 +263,8 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) k = strptime(t + 1, "%s", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; return -EINVAL; } else if (endswith(t, " ago")) { @@ -271,16 +301,22 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; tm = copy; k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; tm = copy; k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); @@ -314,6 +350,8 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) k = strptime(t, "%H:%M:%S", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; tm = copy; k = strptime(t, "%H:%M", &tm); @@ -326,6 +364,8 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) k = strptime(t, "%Y%m%d%H%M%S", &tm); if (k && *k == 0) goto finish; + else if (k && parse_subseconds(k, &ret) == 0) + goto finish; return -EINVAL; @@ -337,7 +377,7 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - ret = (usec_t) x *USEC_PER_SEC; + ret += (usec_t) x * USEC_PER_SEC; ret += plus; if (ret > minus) @@ -586,19 +626,24 @@ static int run_unittest_timestamp(void) const char * const input; usec_t expected; } testcases[] = { - { "2012-09-22 16:34:22", 1348331662000000 }, - { "@1348331662" , 1348331662000000 }, - { "2012-09-22 16:34" , 1348331640000000 }, - { "2012-09-22" , 1348272000000000 }, - { "16:34:22" , 1674232462000000 }, - { "16:34" , 1674232440000000 }, - { "now" , 1674180427000000 }, - { "yesterday" , 1674086400000000 }, - { "today" , 1674172800000000 }, - { "tomorrow" , 1674259200000000 }, - { "+5min" , 1674180727000000 }, - { "-5days" , 1673748427000000 }, - { "20120922163422" , 1348331662000000 }, + { "2012-09-22 16:34:22" , 1348331662000000 }, + { "2012-09-22 16:34:22,012", 1348331662012000 }, + { "2012-09-22 16:34:22.012", 1348331662012000 }, + { "@1348331662" , 1348331662000000 }, + { "@1348331662.234567" , 1348331662234567 }, + { "2012-09-22 16:34" , 1348331640000000 }, + { "2012-09-22" , 1348272000000000 }, + { "16:34:22" , 1674232462000000 }, + { "16:34:22,123456" , 1674232462123456 }, + { "16:34:22.123456" , 1674232462123456 }, + { "16:34" , 1674232440000000 }, + { "now" , 1674180427000000 }, + { "yesterday" , 1674086400000000 }, + { "today" , 1674172800000000 }, + { "tomorrow" , 1674259200000000 }, + { "+5min" , 1674180727000000 }, + { "-5days" , 1673748427000000 }, + { "20120922163422" , 1348331662000000 }, }; if (unsetenv("TZ"))