diff options
| author | Elliott Hughes <enh@google.com> | 2015-08-11 16:06:06 -0500 | 
|---|---|---|
| committer | Rob Landley <rob@landley.net> | 2015-08-11 16:06:06 -0500 | 
| commit | 05499787ca89fa4b017c2441e89020799f02e4c1 (patch) | |
| tree | c8e48c77d96b47fd710238f82240126e71798981 | |
| parent | 7f6bb3dae7ffe5ffdf10cc2e73a803ff820ebca8 (diff) | |
| download | toybox-05499787ca89fa4b017c2441e89020799f02e4c1.tar.gz | |
Fix more date bugs.
Correctly and portably check for non-normal dates, and explicitly show
the "before" and "after" dates (in the format of the user's choosing).
Clear the struct tm in date_main rather than parse_default because on
one path the struct tm is actually initialized. Explicitly clear the
tm_sec field in parse_default because -- experiment shows -- that
should not be preserved. Only do the "what does this 2-digit year
mean?" dance if we actually parsed a 2-digit year. Show the right
string in the error message if strptime fails.
Also add more tests, and use UTC in the tests to avoid flakiness.
| -rw-r--r-- | tests/date.test | 16 | ||||
| -rw-r--r-- | toys/posix/date.c | 51 | 
2 files changed, 43 insertions, 24 deletions
diff --git a/tests/date.test b/tests/date.test index d72e50cf..94a41579 100644 --- a/tests/date.test +++ b/tests/date.test @@ -4,7 +4,19 @@  #testing "name" "command" "result" "infile" "stdin" +# Test Unix date parsing. +testing "date -d @0" "TZ=UTC date -d @0 2>&1" "Thu Jan  1 00:00:00 GMT 1970\n" "" "" +testing "date -d @0x123" "TZ=UTC date -d @0x123 2>&1" "date: bad date '@0x123'\n" "" "" + +# Test basic date parsing. +# Note that toybox's -d format is not the same as coreutils'. +testing "date -d 06021234" "TZ=UTC date -d 06021234 2>&1" "Sun Jun  2 12:34:00 UTC 1900\n" "" "" +testing "date -d 060212341982" "TZ=UTC date -d 060212341982 2>&1" "Sun Jun  2 12:34:00 UTC 1982\n" "" "" +testing "date -d 123" "TZ=UTC date -d 123 2>&1" "date: bad date '123'\n" "" "" +  # Accidentally given a Unix time, we should trivially reject that. -testing "date Unix time" "date 1438053157 2>&1" "date: bad date '1438053157'\n" "" "" +testing "date Unix time missing @" "TZ=UTC date 1438053157 2>&1" \ +  "date: bad date '1438053157'; Tue February 38 05:31:00 UTC 2057 != Sun Mar 10 05:31:00 UTC 2058\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 +testing "date Feb 29th" "TZ=UTC date 022900001975 2>&1" \ +  "date: bad date '022900001975'; Tue Feb 29 00:00:00 UTC 2075 != Fri Mar  1 00:00:00 UTC 2075\n" "" "" diff --git a/toys/posix/date.c b/toys/posix/date.c index 909ca5a9..a42de502 100644 --- a/toys/posix/date.c +++ b/toys/posix/date.c @@ -56,18 +56,24 @@ GLOBALS(  )  // mktime(3) normalizes the struct tm fields, but date(1) shouldn't. -static time_t chkmktime(struct tm *tm) +static time_t chkmktime(struct tm *tm, const char *str, const char* fmt)  { -  struct tm tm2; +  struct tm tm0 = *tm; +  struct tm tm1;    time_t t = mktime(tm); -  int *tt1 = (void *)tm, *tt2=(void *)&tm2, i; -  if (t != -1 && localtime_r(&t, &tm2)) { -    for (i=0; i<6; i++) if (tt1[i] != tt2[i]) break; -    if (i == 5) return t; -  } +  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) { +    int len; -  return -1; +    strftime(toybuf, sizeof(toybuf), fmt, &tm0); +    len = strlen(toybuf) + 1; +    strftime(toybuf + len, sizeof(toybuf) - len, fmt, &tm1); +    error_exit("bad date '%s'; %s != %s", str, toybuf, toybuf + len); +  } +  return t;  }  static void utzset(void) @@ -92,8 +98,6 @@ static int parse_default(char *str, struct tm *tm)  {    int len = 0; -  memset(tm, 0, sizeof(struct tm)); -    // Parse @UNIXTIME[.FRACTION]    if (*str == '@') {      long long ll; @@ -139,16 +143,18 @@ static int parse_default(char *str, struct tm *tm)      // 2 digit years, next 50 years are "future", last 50 years are "past".      // A "future" date in past is a century ahead.      // A non-future date in the future is a century behind. -    if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) { -      if (year < r1) year += 100; -    } else if (year > r1) year -= 100; +    if (len == 2) { +      if ((r1 < r2) ? (r1 < year && year < r2) : (year < r1 || year > r2)) { +        if (year < r1) year += 100; +      } else if (year > r1) year -= 100; +    }      tm->tm_year = year + century;    }    if (*str == '.') {      len = 0;      sscanf(str, ".%u%n", &tm->tm_sec, &len);      str += len; -  } +  } else tm->tm_sec = 0;    return *str;  } @@ -158,6 +164,8 @@ void date_main(void)    char *setdate = *toys.optargs, *format_string = "%a %b %e %H:%M:%S %Z %Y";    struct tm tm; +  memset(&tm, 0, sizeof(struct tm)); +    // We can't just pass a timezone to mktime because posix.    if (toys.optflags & FLAG_u) utzset(); @@ -165,8 +173,8 @@ void date_main(void)      if (TT.setfmt) {        char *s = strptime(TT.showdate, TT.setfmt+(*TT.setfmt=='+'), &tm); -      if (!s || *s) goto bad_date; -    } else if (parse_default(TT.showdate, &tm)) goto bad_date; +      if (!s || *s) goto bad_showdate; +    } else if (parse_default(TT.showdate, &tm)) goto bad_showdate;    } else {      time_t now; @@ -191,15 +199,14 @@ void date_main(void)    } else if (setdate) {      struct timeval tv; -    if (parse_default(setdate, &tm)) goto bad_date; +    if (parse_default(setdate, &tm)) error_exit("bad date '%s'", setdate);      if (toys.optflags & FLAG_u) {        // We can't just pass a timezone to mktime because posix.        utzset(); -      tv.tv_sec = chkmktime(&tm); +      tv.tv_sec = chkmktime(&tm, setdate, format_string);        utzreset(); -    } else tv.tv_sec = chkmktime(&tm); -    if (tv.tv_sec == (time_t)-1) goto bad_date; +    } else tv.tv_sec = chkmktime(&tm, setdate, format_string);      tv.tv_usec = TT.nano/1000;      if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date"); @@ -212,6 +219,6 @@ void date_main(void)    return; -bad_date: -  error_exit("bad date '%s'", setdate); +bad_showdate: +  error_exit("bad date '%s'", TT.showdate);  }  | 
