aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/lib.h2
-rw-r--r--lib/xwrap.c100
-rw-r--r--tests/date.test16
-rw-r--r--tests/touch.test23
-rw-r--r--toys/posix/date.c2
-rw-r--r--toys/posix/touch.c62
6 files changed, 67 insertions, 138 deletions
diff --git a/lib/lib.h b/lib/lib.h
index 174c1fd6..8e6514a8 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -182,7 +182,7 @@ char *xtzset(char *new);
void xsignal_flags(int signal, void *handler, int flags);
void xsignal(int signal, void *handler);
time_t xvali_date(struct tm *tm, char *str);
-void xparsedate(char *str, time_t *t, unsigned *nano);
+void xparsedate(char *str, time_t *t, unsigned *nano, int endian);
// lib.c
void verror_msg(char *msg, int err, va_list va);
diff --git a/lib/xwrap.c b/lib/xwrap.c
index ee07fda2..a8214e57 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -958,14 +958,17 @@ time_t xvali_date(struct tm *tm, char *str)
}
// Parse date string (relative to current *t). Sets time_t and nanoseconds.
-void xparsedate(char *str, time_t *t, unsigned *nano)
+void xparsedate(char *str, time_t *t, unsigned *nano, int endian)
{
struct tm tm;
time_t now = *t;
- int len = 0, i;
- // Formats with years must come first.
- char *s = str, *p, *formats[] = {"%F %T", "%FT%T", "%F %H:%M", "%F",
- "%H:%M:%S", "%H:%M"};
+ int len = 0, i = 0;
+ // Formats with years must come first. Posix can't agree on whether 12 digits
+ // has year before (touch -t) or year after (date), so support both.
+ 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"};
*nano = 0;
@@ -983,72 +986,49 @@ void xparsedate(char *str, time_t *t, unsigned *nano)
if (isdigit(*s)) *nano += *s++-'0';
}
}
- if (s[len]) goto bad_dates;
*t = ll;
+ if (!s[len]) return;
+ xvali_date(0, str);
+ }
- return;
+ // 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);
}
- // Is it one of the fancy formats?
+ // Try each format
for (i = 0; i<ARRAY_LEN(formats); i++) {
localtime_r(&now, &tm);
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
- tm.tm_isdst = -1;
- if ((p = strptime(s, formats[i], &tm)) && !*p) {
- *t = xvali_date(&tm, str);
-
- return;
- }
- }
+ tm.tm_isdst = -endian;
+
+ if ((p = strptime(s, formats[i], &tm))) {
+ if (*p == '.') {
+ p++;
+ // If format didn't already specify seconds, grab seconds
+ if (i>2) {
+ len = 0;
+ sscanf(p, "%2u%n", &tm.tm_sec, &len);
+ p += len;
+ }
+ // nanoseconds
+ for (len = 0; len<9; len++) {
+ *nano *= 10;
+ if (isdigit(*p)) *nano += *p++-'0';
+ }
+ }
- // Posix format?
- sscanf(s, "%2u%2u%2u%2u%n", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
- &tm.tm_min, &len);
- if (len != 8) goto bad_dates;
- s += len;
- tm.tm_mon--;
-
- // If year specified, overwrite one we fetched earlier.
- if (*s && *s != '.') {
- unsigned year;
-
- len = 0;
- sscanf(s, "%u%n", &year, &len);
- if (len == 4) tm.tm_year = year - 1900;
- else if (len != 2) goto bad_dates;
- s += len;
-
- // 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 (len == 2) {
- unsigned r1 = tm.tm_year % 100, r2 = (tm.tm_year + 50) % 100,
- century = tm.tm_year - r1;
-
- 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 (!*p) break;
}
}
- // Fractional part?
- if (*s == '.') {
- len = 0;
- sscanf(s, ".%2u%n", &tm.tm_sec, &len);
- s += len;
- for (len = 0; len<9; len++) {
- *nano *= 10;
- if (isdigit(*s)) *nano += *s++-'0';
- }
- } else tm.tm_sec = 0;
// Sanity check field ranges
- *t = xvali_date(&tm, str);
-
- // Shouldn't be any trailing garbage.
- if (!*s) return;
+ *t = xvali_date((i!=ARRAY_LEN(formats)) ? &tm : 0, str);
-bad_dates:
- // monkey died
- xvali_date(0, str);
+ if (oldtz) setenv("TZ", oldtz, 1);
+ free(oldtz);
}
diff --git a/tests/date.test b/tests/date.test
index a317d35b..5c490b02 100644
--- a/tests/date.test
+++ b/tests/date.test
@@ -9,26 +9,26 @@
tz=Europe/Berlin
# Unix date parsing.
-testing "-d @0" "TZ=$tz date -d @0 2>&1" "Thu Jan 1 01:00:00 CET 1970\n" "" ""
+testing "-d @0" "TZ=$tz date -d @0" "Thu Jan 1 01:00:00 CET 1970\n" "" ""
testing "-d @0x123 invalid" "TZ=$tz date -d @0x123 2>/dev/null || echo expected error" "expected error\n" "" ""
# POSIX format with 2- and 4-digit years.
# All SKIP_HOST=1 because coreutils rejects POSIX format dates supplied to -d.
# These expected values are from running on the host without -d (not as root!).
SKIP_HOST=1 testing "-d MMDDhhmm" \
- "TZ=$tz date -d 06021234 2>&1" "Sun Jun 2 12:34:00 CEST $(date +%Y)\n" "" ""
+ "TZ=$tz date -d 06021234" "Sun Jun 2 12:34:00 CEST $(date +%Y)\n" "" ""
SKIP_HOST=1 testing "-d MMDDhhmmYY.SS" \
- "TZ=$tz date -d 1110143115.30 2>&1" "Tue Nov 10 14:31:30 CET 2015\n" "" ""
+ "TZ=$tz date -d 1110143115.30" "Tue Nov 10 14:31:30 CET 2015\n" "" ""
# busybox thinks this is the year 603 (ISO time 0602-12-34 19:82 with out of range fields normalized).
SKIP_HOST=1 testing "-d MMDDhhmmCCYY" \
- "TZ=$tz date -d 060212341982 2>&1" "Wed Jun 2 12:34:00 CEST 1982\n" "" ""
+ "TZ=$tz date -d 060212341982" "Wed Jun 2 12:34:00 CEST 1982\n" "" ""
SKIP_HOST=1 testing "-d MMDDhhmmCCYY.SS" \
- "TZ=$tz date -d 111014312015.30 2>&1" "Tue Nov 10 14:31:30 CET 2015\n" "" ""
+ "TZ=$tz date -d 111014312015.30" "Tue Nov 10 14:31:30 CET 2015\n" "" ""
# ISO date format.
-testing "-d 1980-01-02" "TZ=$tz date -d 1980-01-02 2>&1" "Wed Jan 2 00:00:00 CET 1980\n" "" ""
-testing "-d 1980-01-02 12:34" "TZ=$tz date -d '1980-01-02 12:34' 2>&1" "Wed Jan 2 12:34:00 CET 1980\n" "" ""
-testing "-d 1980-01-02 12:34:56" "TZ=$tz date -d '1980-01-02 12:34:56' 2>&1" "Wed Jan 2 12:34:56 CET 1980\n" "" ""
+testing "-d 1980-01-02" "TZ=$tz date -d 1980-01-02" "Wed Jan 2 00:00:00 CET 1980\n" "" ""
+testing "-d 1980-01-02 12:34" "TZ=$tz date -d '1980-01-02 12:34'" "Wed Jan 2 12:34:00 CET 1980\n" "" ""
+testing "-d 1980-01-02 12:34:56" "TZ=$tz date -d '1980-01-02 12:34:56'" "Wed Jan 2 12:34:56 CET 1980\n" "" ""
# Reject Unix times without a leading @.
testing "Unix time missing @" "TZ=$tz date 1438053157 2>/dev/null || echo no" \
diff --git a/tests/touch.test b/tests/touch.test
index da6fe99a..b010f7d3 100644
--- a/tests/touch.test
+++ b/tests/touch.test
@@ -32,10 +32,10 @@ testing "-t seconds" \
"touch -t 201201231234.56 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \
"20120123-123456.000000000\n" "" ""
-testing "-t -" "touch -t 200109081946.40 - > walrus && date -r walrus +%s" \
- "1000000000\n" "" ""
+testing "-t -" "TZ=utc touch -t 200109090146.40 - > walrus && TZ=utc date -r walrus +%s" \
+ "1000000000\n" "" ""
-testing "-t nanoseconds" \
+SKIP_HOST=1 testing "-t nanoseconds" \
"touch -t 201201231234.56123456789 walrus && date -r walrus +%Y%m%d-%H%M%S.%N" \
"20120123-123456.123456789\n" "" ""
@@ -66,13 +66,12 @@ testing "-t CCMMDDhhmm" \
testing "-a" "touch -t 197101020304 walrus &&
touch -t 197203040506 -a walrus && stat -c '%X %Y' walrus" \
- "68562360 31662240\n" "" ""
-testing "-m" "touch -t 197101020304 walrus &&
- touch -t 197203040506 -m walrus && stat -c '%X %Y' walrus" \
- "31662240 68562360\n" "" ""
-testing "-am" "touch -t 197101020304 walrus &&
- touch -t 197203040506 -am walrus && stat -c '%X %Y' walrus" \
- "68562360 68562360\n" "" ""
-
-#testing "-t"
+ "68555160 31655040\n" "" ""
+testing "-m" "TZ=utc touch -t 197101020304 walrus &&
+ TZ=utc touch -t 197203040506 -m walrus && TZ=utc stat -c '%X %Y' walrus" \
+ "31633440 68533560\n" "" ""
+testing "-am" "TZ=utc touch -t 197101020304 walrus &&
+ TZ=utc touch -t 197203040506 -am walrus && TZ=utc stat -c '%X %Y' walrus" \
+ "68533560 68533560\n" "" ""
+
rm walrus walrus2
diff --git a/toys/posix/date.c b/toys/posix/date.c
index 685ac8bf..a1762854 100644
--- a/toys/posix/date.c
+++ b/toys/posix/date.c
@@ -77,7 +77,7 @@ static void parse_date(char *str, time_t *t)
tzset();
}
time(t);
- xparsedate(str, t, &TT.nano);
+ xparsedate(str, t, &TT.nano, 1);
if (new_tz) {
if (old_tz) setenv("TZ", old_tz, 1);
else unsetenv("TZ");
diff --git a/toys/posix/touch.c b/toys/posix/touch.c
index 3775c8bc..5e33d7f7 100644
--- a/toys/posix/touch.c
+++ b/toys/posix/touch.c
@@ -41,67 +41,17 @@ void touch_main(void)
// use current time if no -t or -d
ts[0].tv_nsec = UTIME_NOW;
- if (FLAG(t) || FLAG(d)) {
- char *s, *date, **format;
- struct tm tm;
- int len = 0;
-
- // Initialize default values for time fields
- ts->tv_sec = time(0);
- ts->tv_nsec = 0;
-
- // List of search types
- if (FLAG(d)) {
- format = (char *[]){"%Y-%m-%dT%T", "%Y-%m-%d %T", 0};
- date = TT.d;
- } else {
- format = (char *[]){"%m%d%H%M", "%y%m%d%H%M", "%C%y%m%d%H%M", 0};
- date = TT.t;
- }
-
- // Trailing Z means UTC timezone, don't expect libc to know this.
- i = strlen(s = date);
- if (i && toupper(date[i-1])=='Z') {
- date[i-1] = 0;
- setenv("TZ", "UTC0", 1);
- }
- while (*format) {
- if (FLAG(t)) {
- s = strchr(date, '.');
- if ((s ? s-date : strlen(date)) != strlen(*format)) {
- format++;
- continue;
- }
- }
- localtime_r(&(ts->tv_sec), &tm);
- // Adjusting for daylight savings time gives the wrong answer.
- tm.tm_isdst = 0;
- tm.tm_sec = 0;
- s = strptime(date, *format++, &tm);
-
- // parse nanoseconds
- if (s && *s=='.' && isdigit(s[1])) {
- s++;
- if (FLAG(t))
- if (1 == sscanf(s, "%2u%n", &(tm.tm_sec), &len)) s += len;
- if (1 == sscanf(s, "%lu%n", &ts->tv_nsec, &len)) {
- s += len;
- if (ts->tv_nsec > 999999999) s = 0;
- else while (len++ < 9) ts->tv_nsec *= 10;
- }
- }
- if (s && !*s) break;
- }
+ if (FLAG(t) || FLAG(d)) {
+ time_t t = time(0);
+ unsigned nano;
- errno = 0;
- ts->tv_sec = mktime(&tm);
- if (!s || *s || ts->tv_sec == -1) perror_exit("bad '%s'", date);
+ xparsedate(TT.t ? TT.t : TT.d, &t, &nano, 0);
+ ts->tv_sec = t;
+ ts->tv_nsec = nano;
}
ts[1]=ts[0];
- // Set time from -r?
-
if (TT.r) {
struct stat st;