aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2020-08-03 16:59:29 -0700
committerRob Landley <rob@landley.net>2020-08-04 03:13:14 -0500
commitbabcb4bcf4c5c37e545f174db9ba99c62ebb877a (patch)
treec57469ee0dbfb12f4f85ed65bd5cdbbb28796cce /lib
parentdc7654192c4b0c599cf5f742673a0829d6eddcd9 (diff)
downloadtoybox-babcb4bcf4c5c37e545f174db9ba99c62ebb877a.tar.gz
xparsedate: support UTC offsets.
Requested in https://github.com/landley/toybox/issues/130, quoting an old version of the toybox help. This is also supported by coreutils. Set $LANG to C in the date tests so that they pass with TEST_HOST=1 (they were already failing for me, presumably related to a newer glibc).
Diffstat (limited to 'lib')
-rw-r--r--lib/xwrap.c48
1 files changed, 38 insertions, 10 deletions
diff --git a/lib/xwrap.c b/lib/xwrap.c
index abf0d4da..886878cd 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -964,6 +964,35 @@ time_t xvali_date(struct tm *tm, char *str)
error_exit("bad date %s", str);
}
+// Turn a timezone specified as +HH[:MM] or Z into a value for $TZ.
+static int convert_tz(char *tz, char **pp)
+{
+ char sign, *p = *pp;
+ unsigned h_off, m_off, len;
+
+ if (*p == 'Z') {
+ strcpy(tz, "UTC0");
+ *pp = p+1;
+ return 1;
+ }
+
+ sign = *p++;
+ if (sign != '+' && sign != '-') return 0;
+
+ if (sscanf(p, "%2u%n", &h_off, &len) != 1) return 0;
+ p += len;
+
+ if (*p == ':') p++;
+
+ if (sscanf(p, "%2u%n", &m_off, &len) != 1) return 0;
+ p += len;
+
+ // We have to flip the sign because POSIX UTC offsets are backwards!
+ sprintf(tz, "UTC%c%02d:%02d", sign == '-' ? '+' : '-', h_off, m_off);
+ *pp = p;
+ return 1;
+}
+
// Parse date string (relative to current *t). Sets time_t and nanoseconds.
void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
{
@@ -975,7 +1004,7 @@ void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
char *s = str, *p, *oldtz = 0, *formats[] = {"%Y-%m-%d %T", "%Y-%m-%dT%T",
"%H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%H:%M", "%m%d%H%M",
endian ? "%m%d%H%M%y" : "%y%m%d%H%M",
- endian ? "%m%d%H%M%C%y" : "%C%y%m%d%H%M"};
+ endian ? "%m%d%H%M%C%y" : "%C%y%m%d%H%M"}, tz[10];
*nano = 0;
@@ -998,15 +1027,6 @@ void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
xvali_date(0, str);
}
- // Trailing Z means UTC timezone, don't expect libc to know this.
- // (Trimming it off here means it won't show up in error messages.)
- if ((i = strlen(str)) && toupper(str[i-1])=='Z') {
- str[--i] = 0;
- oldtz = getenv("TZ");
- if (oldtz) oldtz = xstrdup(oldtz);
- setenv("TZ", "UTC0", 1);
- }
-
// Try each format
for (i = 0; i<ARRAY_LEN(formats); i++) {
localtime_r(&now, &tm);
@@ -1014,6 +1034,7 @@ void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
tm.tm_isdst = -endian;
if ((p = strptime(s, formats[i], &tm))) {
+ // Handle optional fractional seconds.
if (*p == '.') {
p++;
// If format didn't already specify seconds, grab seconds
@@ -1029,6 +1050,13 @@ void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
}
}
+ // Handle optional timezone.
+ if (convert_tz(tz, &p)) {
+ oldtz = getenv("TZ");
+ if (oldtz) oldtz = xstrdup(oldtz);
+ setenv("TZ", tz, 1);
+ }
+
if (!*p) break;
}
}