X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Ftime-util.c;h=9ee711a49e58e0c1a7c276201897f52d0f305a36;hp=3d00b20eec84069d75a2168542ff8d4fb1448a55;hb=2f79c10e9aef916efbcf29315eea8c25d0a50ac4;hpb=bbb8486e1709ee297a1eb803f88041e1da1fa436 diff --git a/src/shared/time-util.c b/src/shared/time-util.c index 3d00b20ee..9ee711a49 100644 --- a/src/shared/time-util.c +++ b/src/shared/time-util.c @@ -46,6 +46,11 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { int64_t delta; assert(ts); + if (u == (usec_t) -1) { + ts->realtime = ts->monotonic = (usec_t) -1; + return ts; + } + ts->realtime = u; if (u == 0) @@ -64,6 +69,27 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { return ts; } +dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + assert(ts); + + if (u == (usec_t) -1) { + ts->realtime = ts->monotonic = (usec_t) -1; + return ts; + } + + ts->monotonic = u; + delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; + + ts->realtime = now(CLOCK_REALTIME); + if ((int64_t) ts->realtime > delta) + ts->realtime -= delta; + else + ts->realtime = 0; + + return ts; +} + usec_t timespec_load(const struct timespec *ts) { assert(ts); @@ -199,11 +225,13 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) { return buf; } -char *format_timespan(char *buf, size_t l, usec_t t) { +char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { static const struct { const char *suffix; usec_t usec; } table[] = { + { "y", USEC_PER_YEAR }, + { "month", USEC_PER_MONTH }, { "w", USEC_PER_WEEK }, { "d", USEC_PER_DAY }, { "h", USEC_PER_HOUR }, @@ -215,6 +243,7 @@ char *format_timespan(char *buf, size_t l, usec_t t) { unsigned i; char *p = buf; + bool something = false; assert(buf); assert(l > 0); @@ -222,17 +251,25 @@ char *format_timespan(char *buf, size_t l, usec_t t) { if (t == (usec_t) -1) return NULL; - if (t == 0) { + if (t <= 0) { snprintf(p, l, "0"); p[l-1] = 0; return p; } - /* The result of this function can be parsed with parse_usec */ + /* The result of this function can be parsed with parse_sec */ for (i = 0; i < ELEMENTSOF(table); i++) { int k; size_t n; + bool done = false; + usec_t a, b; + + if (t <= 0) + break; + + if (t < accuracy && something) + break; if (t < table[i].usec) continue; @@ -240,13 +277,54 @@ char *format_timespan(char *buf, size_t l, usec_t t) { if (l <= 1) break; - k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix); + a = t / table[i].usec; + b = t % table[i].usec; + + /* Let's see if we should shows this in dot notation */ + if (t < USEC_PER_MINUTE && b > 0) { + usec_t cc; + int j; + + j = 0; + for (cc = table[i].usec; cc > 1; cc /= 10) + j++; + + for (cc = accuracy; cc > 1; cc /= 10) { + b /= 10; + j--; + } + + if (j > 0) { + k = snprintf(p, l, + "%s%llu.%0*llu%s", + p > buf ? " " : "", + (unsigned long long) a, + j, + (unsigned long long) b, + table[i].suffix); + + t = 0; + done = true; + } + } + + /* No? Then let's show it normally */ + if (!done) { + k = snprintf(p, l, + "%s%llu%s", + p > buf ? " " : "", + (unsigned long long) a, + table[i].suffix); + + t = b; + } + n = MIN((size_t) k, l); l -= n; p += n; - t %= table[i].usec; + something = true; } *p = 0; @@ -284,11 +362,32 @@ void dual_timestamp_deserialize(const char *value, dual_timestamp *t) { } int parse_timestamp(const char *t, usec_t *usec) { + static const struct { + const char *name; + const int nr; + } day_nr[] = { + { "Sunday", 0 }, + { "Sun", 0 }, + { "Monday", 1 }, + { "Mon", 1 }, + { "Tuesday", 2 }, + { "Tue", 2 }, + { "Wednesday", 3 }, + { "Wed", 3 }, + { "Thursday", 4 }, + { "Thu", 4 }, + { "Friday", 5 }, + { "Fri", 5 }, + { "Saturday", 6 }, + { "Sat", 6 }, + }; + const char *k; struct tm tm, copy; time_t x; usec_t plus = 0, minus = 0, ret; - int r; + int r, weekday = -1; + unsigned i; /* * Allowed syntaxes: @@ -312,6 +411,7 @@ int parse_timestamp(const char *t, usec_t *usec) { x = time(NULL); assert_se(localtime_r(&x, &tm)); + tm.tm_isdst = -1; if (streq(t, "now")) goto finish; @@ -332,18 +432,46 @@ int parse_timestamp(const char *t, usec_t *usec) { } else if (t[0] == '+') { - r = parse_usec(t+1, &plus); + r = parse_sec(t+1, &plus); if (r < 0) return r; goto finish; } else if (t[0] == '-') { - r = parse_usec(t+1, &minus); + r = parse_sec(t+1, &minus); if (r < 0) return r; goto finish; + + } else if (endswith(t, " ago")) { + _cleanup_free_ char *z; + + z = strndup(t, strlen(t) - 4); + if (!z) + return -ENOMEM; + + r = parse_sec(z, &minus); + if (r < 0) + return r; + + goto finish; + } + + for (i = 0; i < ELEMENTSOF(day_nr); i++) { + size_t skip; + + if (!startswith_no_case(t, day_nr[i].name)) + continue; + + skip = strlen(day_nr[i].name); + if (t[skip] != ' ') + continue; + + weekday = day_nr[i].nr; + t += skip + 1; + break; } copy = tm; @@ -403,6 +531,9 @@ finish: if (x == (time_t) -1) return -EINVAL; + if (weekday >= 0 && tm.tm_wday != weekday) + return -EINVAL; + ret = (usec_t) x * USEC_PER_SEC; ret += plus; @@ -416,7 +547,7 @@ finish: return 0; } -int parse_usec(const char *t, usec_t *usec) { +int parse_sec(const char *t, usec_t *usec) { static const struct { const char *suffix; usec_t usec; @@ -453,41 +584,74 @@ int parse_usec(const char *t, usec_t *usec) { const char *p; usec_t r = 0; + bool something = false; assert(t); assert(usec); p = t; - do { - long long l; + for (;;) { + long long l, z = 0; char *e; - unsigned i; + unsigned i, n = 0; + + p += strspn(p, WHITESPACE); + + if (*p == 0) { + if (!something) + return -EINVAL; + + break; + } errno = 0; l = strtoll(p, &e, 10); - if (errno != 0) + if (errno > 0) return -errno; if (l < 0) return -ERANGE; - if (e == p) + if (*e == '.') { + char *b = e + 1; + + errno = 0; + z = strtoll(b, &e, 10); + if (errno > 0) + return -errno; + + if (z < 0) + return -ERANGE; + + if (e == b) + return -EINVAL; + + n = e - b; + + } else if (e == p) return -EINVAL; e += strspn(e, WHITESPACE); for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - r += (usec_t) l * table[i].usec; + usec_t k = (usec_t) z * table[i].usec; + + for (; n > 0; n--) + k /= 10; + + r += (usec_t) l * table[i].usec + k; p = e + strlen(table[i].suffix); + + something = true; break; } if (i >= ELEMENTSOF(table)) return -EINVAL; - } while (*p != 0); + } *usec = r; @@ -533,41 +697,74 @@ int parse_nsec(const char *t, nsec_t *nsec) { const char *p; nsec_t r = 0; + bool something = false; assert(t); assert(nsec); p = t; - do { - long long l; + for (;;) { + long long l, z = 0; char *e; - unsigned i; + unsigned i, n = 0; + + p += strspn(p, WHITESPACE); + + if (*p == 0) { + if (!something) + return -EINVAL; + + break; + } errno = 0; l = strtoll(p, &e, 10); - if (errno != 0) + if (errno > 0) return -errno; if (l < 0) return -ERANGE; - if (e == p) + if (*e == '.') { + char *b = e + 1; + + errno = 0; + z = strtoll(b, &e, 10); + if (errno > 0) + return -errno; + + if (z < 0) + return -ERANGE; + + if (e == b) + return -EINVAL; + + n = e - b; + + } else if (e == p) return -EINVAL; e += strspn(e, WHITESPACE); for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - r += (nsec_t) l * table[i].nsec; + nsec_t k = (nsec_t) z * table[i].nsec; + + for (; n > 0; n--) + k /= 10; + + r += (nsec_t) l * table[i].nsec + k; p = e + strlen(table[i].suffix); + + something = true; break; } if (i >= ELEMENTSOF(table)) return -EINVAL; - } while (*p != 0); + } *nsec = r;