date,touch: allow timezone offsets in dates
No binary (build failed)
—
(log yok)
| @@ -266,6 +266,7 @@ int date_main(int argc UNUSED_PARAM, char **argv) | |
| /* If date string is given, update tm_time, and maybe set date */ | |
| if (date_str != NULL) { | |
| + | int check_dst = 1; |
| /* Zero out fields - take her back to midnight! */ | |
| tm_time.tm_sec = 0; | |
| tm_time.tm_min = 0; | |
| @@ -276,12 +277,12 @@ int date_main(int argc UNUSED_PARAM, char **argv) | |
| if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) | |
| bb_error_msg_and_die(bb_msg_invalid_date, date_str); | |
| } else { | |
| - | parse_datestr(date_str, &tm_time); |
| + | check_dst = parse_datestr(date_str, &tm_time); |
| } | |
| /* Correct any day of week and day of year etc. fields */ | |
| - | /* Be sure to recheck dst (but not if date is time_t format) */ |
| - | if (date_str[0] != '@') |
| + | /* Be sure to recheck dst (but not if date is UTC) */ |
| + | if (check_dst) |
| tm_time.tm_isdst = -1; | |
| ts.tv_sec = validate_tm_time(date_str, &tm_time); | |
| ts.tv_nsec = 0; | |
| @@ -140,15 +140,17 @@ int touch_main(int argc UNUSED_PARAM, char **argv) | |
| if (opts & (OPT_d|OPT_t)) { | |
| struct tm tm_time; | |
| time_t t; | |
| + | int check_dst; |
| //memset(&tm_time, 0, sizeof(tm_time)); | |
| /* Better than memset: makes "HH:MM" dates meaningful */ | |
| time(&t); | |
| localtime_r(&t, &tm_time); | |
| - | parse_datestr(date_str, &tm_time); |
| + | check_dst = parse_datestr(date_str, &tm_time); |
| /* Correct any day of week and day of year etc. fields */ | |
| - | tm_time.tm_isdst = -1; /* Be sure to recheck dst */ |
| + | if (check_dst) |
| + | tm_time.tm_isdst = -1; /* recheck dst unless date is UTC */ |
| t = validate_tm_time(date_str, &tm_time); | |
| timebuf[1].tv_sec = timebuf[0].tv_sec = t; | |
| @@ -690,7 +690,7 @@ struct BUG_too_small { | |
| }; | |
| - | void parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC; |
| + | int parse_datestr(const char *date_str, struct tm *ptm) FAST_FUNC; |
| time_t validate_tm_time(const char *date_str, struct tm *ptm) FAST_FUNC; | |
| char *strftime_HHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; | |
| char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; | |
| @@ -395,3 +395,14 @@ config FEATURE_HWIB | |
| default y | |
| help | |
| Support for printing infiniband addresses in network applets. | |
| + | |
| + | config FEATURE_TIMEZONE |
| + | bool "Allow timezone in dates" |
| + | default y |
| + | depends on DESKTOP |
| + | help |
| + | Permit the use of timezones when parsing user-provided data |
| + | strings, e.g. '1996-04-09 12:45:00 -0500'. |
| + | |
| + | This requires support for the '%z' extension to strptime() which |
| + | may not be available in all implementations. |
| @@ -8,7 +8,9 @@ | |
| */ | |
| #include "libbb.h" | |
| - | void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) |
| + | /* Returns 0 if the time structure contains an absolute UTC time which |
| + | * should not be subject to DST adjustment by the caller. */ |
| + | int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) |
| { | |
| char end = '\0'; | |
| #if ENABLE_DESKTOP | |
| @@ -27,6 +29,10 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |
| "%b %d %T %Y" "\0" /* month_name d HH:MM:SS YYYY */ | |
| "%Y-%m-%d %R" "\0" /* yyyy-mm-dd HH:MM */ | |
| "%Y-%m-%d %T" "\0" /* yyyy-mm-dd HH:MM:SS */ | |
| + | #if ENABLE_FEATURE_TIMEZONE |
| + | "%Y-%m-%d %R %z" "\0" /* yyyy-mm-dd HH:MM TZ */ |
| + | "%Y-%m-%d %T %z" "\0" /* yyyy-mm-dd HH:MM:SS TZ */ |
| + | #endif |
| "%Y-%m-%d %H" "\0" /* yyyy-mm-dd HH */ | |
| "%Y-%m-%d" "\0" /* yyyy-mm-dd */ | |
| /* extra NUL */; | |
| @@ -38,8 +44,28 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |
| fmt = fmt_str; | |
| while (*fmt) { | |
| endp = strptime(date_str, fmt, ptm); | |
| - | if (endp && *endp == '\0') |
| - | return; |
| + | if (endp && *endp == '\0') { |
| + | #if ENABLE_FEATURE_TIMEZONE |
| + | if (strchr(fmt, 'z')) { |
| + | time_t t; |
| + | struct tm *utm; |
| + | |
| + | /* we have timezone offset: obtain Unix time_t */ |
| + | ptm->tm_sec -= ptm->tm_gmtoff; |
| + | ptm->tm_isdst = 0; |
| + | t = timegm(ptm); |
| + | if (t == (time_t)-1) |
| + | break; |
| + | /* convert Unix time_t to struct tm in user's locale */ |
| + | utm = localtime(&t); |
| + | if (!utm) |
| + | break; |
| + | *ptm = *utm; |
| + | return 0; |
| + | } |
| + | #endif |
| + | return 1; |
| + | } |
| *ptm = save; | |
| while (*++fmt) | |
| continue; | |
| @@ -124,7 +150,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |
| struct tm *lt = localtime(&t); | |
| if (lt) { | |
| *ptm = *lt; | |
| - | return; |
| + | return 0; |
| } | |
| } | |
| end = '1'; | |
| @@ -241,6 +267,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |
| if (end != '\0') { | |
| bb_error_msg_and_die(bb_msg_invalid_date, date_str); | |
| } | |
| + | return 1; |
| } | |
| time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm) | |
| @@ -0,0 +1,32 @@ | |
| + | # FEATURE: CONFIG_FEATURE_TIMEZONE |
| + | |
| + | # 'Z' is UTC |
| + | dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5Z') |
| + | dt=$(echo "$dt" | cut -b1-19) |
| + | test x"$dt" = x"Sat Jan 2 03:04:05" |
| + | |
| + | # '+0600' is six hours ahead of UTC |
| + | dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5 +0600') |
| + | dt=$(echo "$dt" | cut -b1-19) |
| + | test x"$dt" = x"Fri Jan 1 21:04:05" |
| + | |
| + | # '-0600' is six hours behind UTC |
| + | dt=$(TZ=UTC0 busybox date -d '1999-1-2 3:4:5 -0600') |
| + | dt=$(echo "$dt" | cut -b1-19) |
| + | test x"$dt" = x"Sat Jan 2 09:04:05" |
| + | |
| + | # before dst is switched on |
| + | dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-03-28 00:59:59 +0000') |
| + | test x"$dt" = x"Sun Mar 28 00:59:59 GMT 2021" |
| + | |
| + | # after dst is switched on |
| + | dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-03-28 01:00:01 +0000') |
| + | test x"$dt" = x"Sun Mar 28 02:00:01 BST 2021" |
| + | |
| + | # before dst is switched off |
| + | dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-10-31 00:00:01 +0000') |
| + | test x"$dt" = x"Sun Oct 31 01:00:01 BST 2021" |
| + | |
| + | # after dst is switched off: back to 01:00:01 but with different TZ |
| + | dt=$(TZ=GMT0BST,M3.5.0/1,M10.5.0/2 busybox date -d '2021-10-31 01:00:01 +0000') |
| + | test x"$dt" = x"Sun Oct 31 01:00:01 GMT 2021" |