From: Yu Watanabe Date: Mon, 8 Sep 2025 23:31:22 +0000 (+0900) Subject: musl: time-util: make parse_gmtoff() accept extended timezone offset format X-Git-Tag: v259-rc1~81^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3ac4d68498dd378e2b3acd2bb86f4700263532d0;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/basic/time-util.c b/src/basic/time-util.c index e54c829623e..c09c9fd6d02 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; + + 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; + } - if (ret) - *ret = tm.tm_gmtoff; return 0; }