From: Thomas Weißschuh Date: Tue, 4 Jul 2023 07:45:55 +0000 (+0200) Subject: timeutils: add strtimespec_relative X-Git-Tag: v2.40-rc1~346^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=00ddcaf19c60471b9390a12f65a56c5c0f6877cf;p=thirdparty%2Futil-linux.git timeutils: add strtimespec_relative Signed-off-by: Thomas Weißschuh --- diff --git a/include/timeutils.h b/include/timeutils.h index 4257bd1eda..27261abd87 100644 --- a/include/timeutils.h +++ b/include/timeutils.h @@ -78,6 +78,7 @@ int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz) int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz); int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz); int strtimespec_iso(const struct timespec *t, int flags, char *buf, size_t bufsz); +int strtimespec_relative(const struct timespec *ts, char *buf, size_t bufsz); #define UL_SHORTTIME_THISYEAR_HHMM (1 << 1) diff --git a/lib/timeutils.c b/lib/timeutils.c index c1ffc547c0..8f9c8e9be8 100644 --- a/lib/timeutils.c +++ b/lib/timeutils.c @@ -617,6 +617,63 @@ int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, si return rc <= 0 ? -1 : 0; } +int strtimespec_relative(const struct timespec *ts, char *buf, size_t bufsz) +{ + time_t secs = ts->tv_sec; + size_t i, parts = 0; + int rc; + + if (bufsz) + buf[0] = '\0'; + + static const struct { + const char * const suffix; + int width; + int64_t secs; + } table[] = { + { "y", 4, NSEC_PER_YEAR / NSEC_PER_SEC }, + { "d", 3, NSEC_PER_DAY / NSEC_PER_SEC }, + { "h", 2, NSEC_PER_HOUR / NSEC_PER_SEC }, + { "m", 2, NSEC_PER_MINUTE / NSEC_PER_SEC }, + { "s", 2, NSEC_PER_SEC / NSEC_PER_SEC }, + }; + + for (i = 0; i < ARRAY_SIZE(table); i++) { + if (secs >= table[i].secs) { + rc = snprintf(buf, bufsz, + "%*"PRId64"%s%s", + parts ? table[i].width : 0, + secs / table[i].secs, table[i].suffix, + secs % table[i].secs ? " " : ""); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + parts++; + buf += rc; + bufsz -= rc; + secs %= table[i].secs; + } + } + + if (ts->tv_nsec) { + if (ts->tv_nsec % NSEC_PER_MSEC) { + rc = snprintf(buf, bufsz, "%*luns", + parts ? 10 : 0, ts->tv_nsec); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + } else { + rc = snprintf(buf, bufsz, "%*llums", + parts ? 4 : 0, ts->tv_nsec / NSEC_PER_MSEC); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + } + } + + return 0; + err: + warnx(_("format_reltime: buffer overflow.")); + return -1; +} + #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { @@ -727,6 +784,53 @@ static int run_unittest_format(void) return rc; } +static int run_unittest_format_relative(void) +{ + int rc = EXIT_SUCCESS; + char buf[FORMAT_TIMESTAMP_MAX]; + static const struct testcase { + struct timespec ts; + const char * const expected; + } testcases[] = { + {{}, "" }, + {{ 1 }, "1s" }, + {{ 10 }, "10s" }, + {{ 100 }, "1m 40s" }, + {{ 1000 }, "16m 40s" }, + {{ 10000 }, "2h 46m 40s" }, + {{ 100000 }, "1d 3h 46m 40s" }, + {{ 1000000 }, "11d 13h 46m 40s" }, + {{ 10000000 }, "115d 17h 46m 40s" }, + {{ 100000000 }, "3y 61d 15h 46m 40s" }, + {{ 60 }, "1m" }, + {{ 3600 }, "1h" }, + + {{ 1, 1 }, "1s 1ns" }, + {{ 0, 1 }, "1ns" }, + {{ 0, 1000000 }, "1ms" }, + {{ 0, 1000001 }, "1000001ns" }, + }; + + setenv("TZ", "GMT", 1); + tzset(); + + for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { + struct testcase t = testcases[i]; + int r = strtimespec_relative(&t.ts, buf, sizeof(buf)); + if (r) { + fprintf(stderr, "Could not format '%s'\n", t.expected); + rc = EXIT_FAILURE; + } + + if (strcmp(buf, t.expected)) { + fprintf(stderr, "#%02zu '%-20s' != '%-20s'\n", i, buf, t.expected); + rc = EXIT_FAILURE; + } + } + + return rc; +} + int main(int argc, char *argv[]) { struct timespec ts = { 0 }; @@ -741,6 +845,8 @@ int main(int argc, char *argv[]) return run_unittest_timestamp(); else if (strcmp(argv[1], "--unittest-format") == 0) return run_unittest_format(); + else if (strcmp(argv[1], "--unittest-format-relative") == 0) + return run_unittest_format_relative(); if (strcmp(argv[1], "--timestamp") == 0) { usec_t usec = 0; @@ -768,6 +874,9 @@ int main(int argc, char *argv[]) strtimespec_iso(&ts, ISO_TIMESTAMP_DOT, buf, sizeof(buf)); printf("Zone: '%s'\n", buf); + strtimespec_relative(&ts, buf, sizeof(buf)); + printf("Rel: '%s'\n", buf); + return EXIT_SUCCESS; } diff --git a/tests/ts/lib/timeutils b/tests/ts/lib/timeutils index 74bb17b8e1..043b0c0e13 100755 --- a/tests/ts/lib/timeutils +++ b/tests/ts/lib/timeutils @@ -23,4 +23,8 @@ ts_init_subtest "format" "$TS_HELPER_TIMEUTILS" --unittest-format 2> "$TS_ERRLOG" || ts_die "test failed" ts_finalize_subtest +ts_init_subtest "format-relative" +"$TS_HELPER_TIMEUTILS" --unittest-format-relative 2> "$TS_ERRLOG" || ts_die "test failed" +ts_finalize_subtest + ts_finalize