From 3ac4d68498dd378e2b3acd2bb86f4700263532d0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 9 Sep 2025 08:31:22 +0900 Subject: [PATCH] musl: time-util: make parse_gmtoff() accept extended timezone offset format musl v1.2.5 does not support %z specifier in strptime(). Since https://github.com/kraj/musl/commit/fced99e93daeefb0192fd16304f978d4401d1d77 %z is supported, but it only supports strict RFC-822/ISO 8601 format, that is, 4 digits with sign (e.g. +0900 or -1400), but does not support extended format: 2 digits or colon separated 4 digits (e.g. +09 or -14:00). Let's add fallback logic to make it support the extended timezone spec. --- src/basic/time-util.c | 77 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/src/basic/time-util.c b/src/basic/time-util.c index e54c829623..c09c9fd6d0 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -13,6 +13,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "io-util.h" #include "log.h" #include "parse-util.h" @@ -625,15 +626,85 @@ char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { } int parse_gmtoff(const char *t, long *ret) { + int r; + assert(t); struct tm tm; const char *k = strptime(t, "%z", &tm); - if (!k || *k != '\0') + if (k && *k == '\0') { + /* Success! */ + if (ret) + *ret = tm.tm_gmtoff; + return 0; + } + + /* musl v1.2.5 does not support %z specifier in strptime(). Since + * https://github.com/kraj/musl/commit/fced99e93daeefb0192fd16304f978d4401d1d77 + * %z is supported, but it only supports strict RFC-822/ISO 8601 format, that is, 4 digits with sign + * (e.g. +0900 or -1400), but does not support extended format: 2 digits or colon separated 4 digits + * (e.g. +09 or -14:00). Let's add fallback logic to make it support the extended timezone spec. */ + + bool positive; + switch (*t) { + case '+': + positive = true; + break; + case '-': + positive = false; + break; + default: + return -EINVAL; + } + + t++; + r = undecchar(*t); + if (r < 0) + return r; + + usec_t u = r * 10 * USEC_PER_HOUR; + + t++; + r = undecchar(*t); + if (r < 0) + return r; + u += r * USEC_PER_HOUR; + + t++; + if (*t == '\0') /* 2 digits case */ + goto finalize; + + if (*t == ':') /* skip colon */ + t++; + + r = undecchar(*t); + if (r < 0) + return r; + if (r >= 6) /* refuse minutes equal to or larger than 60 */ return -EINVAL; - if (ret) - *ret = tm.tm_gmtoff; + u += r * 10 * USEC_PER_MINUTE; + + t++; + r = undecchar(*t); + if (r < 0) + return r; + + u += r * USEC_PER_MINUTE; + + t++; + if (*t != '\0') + return -EINVAL; + +finalize: + if (u > USEC_PER_DAY) /* refuse larger than one day */ + return -EINVAL; + + if (ret) { + long gmtoff = u / USEC_PER_SEC; + *ret = positive ? gmtoff : -gmtoff; + } + return 0; }