Applied, thank you.
On Thu, Sep 16, 2021 at 11:26 AM Ron Yorston <r...@pobox.com> wrote: > > Allow ISO 8601 style dates to include a timezone offset. Like > the '@' format these dates aren't relative to the user's current > timezone and shouldn't be subject to DST adjustment. > > - The implementation uses the strptime() '%z' format specifier. > This an extension which may not be available so the use of > timezones is a configuration option. > > - The 'touch' applet has been updated to respect whether DST > adjustment is required, matching 'date'. > > function old new delta > parse_datestr 624 730 +106 > static.fmt_str 106 136 +30 > touch_main 388 392 +4 > date_main 818 819 +1 > ------------------------------------------------------------------------------ > (add/remove: 0/0 grow/shrink: 4/0 up/down: 141/0) Total: 141 bytes > > Signed-off-by: Ron Yorston <r...@pobox.com> > --- > coreutils/date.c | 7 ++++--- > coreutils/touch.c | 6 ++++-- > include/libbb.h | 2 +- > libbb/Config.src | 11 +++++++++++ > libbb/time.c | 32 ++++++++++++++++++++++++++++---- > testsuite/date/date-timezone | 32 ++++++++++++++++++++++++++++++++ > 6 files changed, 80 insertions(+), 10 deletions(-) > create mode 100644 testsuite/date/date-timezone > > diff --git a/coreutils/date.c b/coreutils/date.c > index 7061f1719..abcc37c33 100644 > --- a/coreutils/date.c > +++ b/coreutils/date.c > @@ -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; > diff --git a/coreutils/touch.c b/coreutils/touch.c > index 78100ba1d..7e13a27be 100644 > --- a/coreutils/touch.c > +++ b/coreutils/touch.c > @@ -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; > diff --git a/include/libbb.h b/include/libbb.h > index 7d6ab4a93..1ec8d2d3b 100644 > --- a/include/libbb.h > +++ b/include/libbb.h > @@ -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; > diff --git a/libbb/Config.src b/libbb/Config.src > index f97de8ef7..58c5fad50 100644 > --- a/libbb/Config.src > +++ b/libbb/Config.src > @@ -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. > diff --git a/libbb/time.c b/libbb/time.c > index 365b1df02..e4d108c83 100644 > --- a/libbb/time.c > +++ b/libbb/time.c > @@ -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,25 @@ 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); > + /* convert Unix time_t to struct tm in user's > locale */ > + if (t == (time_t)-1 || (utm = localtime(&t)) > == NULL) > + break; > + *ptm = *utm; > + return 0; > + } > +#endif > + return 1; > + } > *ptm = save; > while (*++fmt) > continue; > @@ -124,7 +147,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 +264,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) > diff --git a/testsuite/date/date-timezone b/testsuite/date/date-timezone > new file mode 100644 > index 000000000..8628aa1d7 > --- /dev/null > +++ b/testsuite/date/date-timezone > @@ -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" > -- > 2.31.1 > > _______________________________________________ > busybox mailing list > busybox@busybox.net > http://lists.busybox.net/mailman/listinfo/busybox _______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox