diff options
Diffstat (limited to 'toys/posix')
-rw-r--r-- | toys/posix/date.c | 133 |
1 files changed, 10 insertions, 123 deletions
diff --git a/toys/posix/date.c b/toys/posix/date.c index 43b558a5..685ac8bf 100644 --- a/toys/posix/date.c +++ b/toys/posix/date.c @@ -59,135 +59,29 @@ GLOBALS( unsigned nano; ) -static void check_range(int a, int low, int high) -{ - if (a<low) error_exit("%d<%d", a, low); - if (a>high) error_exit("%d>%d", a, high); -} - -static void check_tm(struct tm *tm) -{ - check_range(tm->tm_sec, 0, 60); - check_range(tm->tm_min, 0, 59); - check_range(tm->tm_hour, 0, 23); - check_range(tm->tm_mday, 1, 31); - check_range(tm->tm_mon, 0, 11); -} - -// Returns 0 success, nonzero for error. -static int parse_formats(char *str, time_t *t) -{ - struct tm tm; - time_t now; - int len = 0, i; - char *formats[] = { - // Formats with years must come first. - "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", - "%H:%M:%S", "%H:%M" - }; - - // Parse @UNIXTIME[.FRACTION] - if (*str == '@') { - long long ll; - - // Collect seconds and nanoseconds. - // &ll is not just t because we can't guarantee time_t is 64 bit (yet). - sscanf(str, "@%lld%n", &ll, &len); - if (str[len]=='.') { - str += len+1; - for (len = 0; len<9; len++) { - TT.nano *= 10; - if (isdigit(str[len])) TT.nano += str[len]-'0'; - } - } - if (str[len]) return 1; - *t = ll; - return 0; - } - - // Is it one of the fancy formats? - for (i = 0; i<ARRAY_LEN(formats); i++) { - char *p; - - now = time(0); - localtime_r(&now, &tm); - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - tm.tm_isdst = -1; - if ((p = strptime(str,formats[i],&tm)) && !*p) { - if ((*t = mktime(&tm)) != -1) return 0; - } - } - - // Posix format? - sscanf(str, "%2u%2u%2u%2u%n", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, - &tm.tm_min, &len); - if (len != 8) return 1; - str += len; - tm.tm_mon--; - - // If year specified, overwrite one we fetched earlier. - if (*str && *str != '.') { - unsigned year; - - len = 0; - sscanf(str, "%u%n", &year, &len); - if (len == 4) tm.tm_year = year - 1900; - else if (len != 2) return 1; - str += 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; - } - } - // Fractional part? - if (*str == '.') { - len = 0; - sscanf(str, ".%u%n", &tm.tm_sec, &len); - str += len; - } else tm.tm_sec = 0; - - // Does that look like a valid date? - check_tm(&tm); - if ((*t = mktime(&tm)) == -1) return 1; - - // Shouldn't be any trailing garbage. - return *str; -} - // Handles any leading `TZ="blah" ` in the input string. -static int parse_date(char *str, time_t *t) +static void parse_date(char *str, time_t *t) { - char *new_tz = NULL, *old_tz; - int result; + char *new_tz = NULL, *old_tz, *s = str; if (!strncmp(str, "TZ=\"", 4)) { // Extract the time zone and skip any whitespace. new_tz = str+4; - str = strchr(new_tz, '"'); - if (!str) return 1; - *str++ = '\0'; - while (isspace(*str)) ++str; + if (!(str = strchr(new_tz, '"'))) xvali_date(0, s); + *str++ = 0; + while (isspace(*str)) str++; // Switch $TZ. old_tz = getenv("TZ"); setenv("TZ", new_tz, 1); tzset(); } - result = parse_formats(str, t); + time(t); + xparsedate(str, t, &TT.nano); if (new_tz) { if (old_tz) setenv("TZ", old_tz, 1); else unsetenv("TZ"); } - return result; } // Print strftime plus %N escape(s). note: modifies fmt for %N @@ -236,10 +130,8 @@ void date_main(void) struct tm tm = {}; char *s = strptime(TT.d, TT.D+(*TT.D=='+'), &tm); - if (!s || *s) goto bad_showdate; - check_tm(&tm); - if ((t = mktime(&tm)) == -1) goto bad_showdate; - } else if (parse_date(TT.d, &t)) goto bad_showdate; + t = (s && *s) ? xvali_date(&tm, s) : xvali_date(0, TT.d); + } else parse_date(TT.d, &t); } else { struct timespec ts; struct stat st; @@ -264,7 +156,7 @@ void date_main(void) } else if (setdate) { struct timeval tv; - if (parse_date(setdate, &t)) goto bad_setdate; + parse_date(setdate, &t); tv.tv_sec = t; tv.tv_usec = TT.nano/1000; if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date"); @@ -279,9 +171,4 @@ void date_main(void) } return; - -bad_showdate: - setdate = TT.d; -bad_setdate: - error_exit("bad date '%s'", setdate); } |