diff options
-rw-r--r-- | tests/date.test | 10 | ||||
-rw-r--r-- | toys/posix/date.c | 22 |
2 files changed, 30 insertions, 2 deletions
diff --git a/tests/date.test b/tests/date.test new file mode 100644 index 00000000..d72e50cf --- /dev/null +++ b/tests/date.test @@ -0,0 +1,10 @@ +#!/bin/bash + +[ -f testing.sh ] && . testing.sh + +#testing "name" "command" "result" "infile" "stdin" + +# Accidentally given a Unix time, we should trivially reject that. +testing "date Unix time" "date 1438053157 2>&1" "date: bad date '1438053157'\n" "" "" +# But some invalid dates are more subtle, like Febuary 29th in a non-leap year. +testing "date Feb 29th" "date 022900001975 2>&1" "date: bad date diff --git a/toys/posix/date.c b/toys/posix/date.c index 3093f815..84e04fcc 100644 --- a/toys/posix/date.c +++ b/toys/posix/date.c @@ -51,6 +51,24 @@ GLOBALS( char *showdate; ) +// mktime(3) normalizes the struct tm fields, but date(1) shouldn't. +// If we round trip via localtime_r(3) and get back where we started, +// we know 'tm' is already in normal form. +static time_t non_normalizing_mktime(struct tm *tm) +{ + struct tm tm0 = *tm; + time_t t = mktime(tm); + struct tm tm1; + + if (t == -1 || !localtime_r(&t, &tm1) || + tm0.tm_sec != tm1.tm_sec || tm0.tm_min != tm1.tm_min || + tm0.tm_hour != tm1.tm_hour || tm0.tm_mday != tm1.tm_mday || + tm0.tm_mon != tm1.tm_mon) + return -1; + + return t; +} + // Handle default posix date format: mmddhhmm[[cc]yy] // returns 0 success, nonzero for error static int parse_posixdate(char *str, struct tm *tm) @@ -145,13 +163,13 @@ void date_main(void) // We can't just pass a timezone to mktime because posix. setenv("TZ", "UTC", 1); tzset(); - tv.tv_sec = mktime(&tm); + tv.tv_sec = non_normalizing_mktime(&tm); if (CFG_TOYBOX_FREE) { if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); } - } else tv.tv_sec = mktime(&tm); + } else tv.tv_sec = non_normalizing_mktime(&tm); if (tv.tv_sec == (time_t)-1) goto bad_date; tv.tv_usec = 0; |