aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/date.test10
-rw-r--r--toys/posix/date.c22
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;