diff options
author | Ron Yorston <rmy@pobox.com> | 2021-09-17 09:18:58 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2021-09-17 11:41:25 +0100 |
commit | 9e16eecc70020e9a603d637f6a8fdfc7c95c30e1 (patch) | |
tree | 5700c068c09ad6028551449c2c9deac13b5c3b32 | |
parent | 46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52 (diff) | |
download | busybox-w32-9e16eecc70020e9a603d637f6a8fdfc7c95c30e1.tar.gz busybox-w32-9e16eecc70020e9a603d637f6a8fdfc7c95c30e1.tar.bz2 busybox-w32-9e16eecc70020e9a603d637f6a8fdfc7c95c30e1.zip |
win32: changes to allow timezones in dates
Create mingw_strptime() to return timezone offset as a separate
argument (since Microsoft's struct tm doesn't have the required
member).
Import timegm() from musl.
Update parse_datestr() to use mingw_strptime().
Enable FEATURE_TIMEZONE in the default configuration.
GitHub issue #230.
-rw-r--r-- | configs/mingw32_defconfig | 4 | ||||
-rw-r--r-- | configs/mingw64_defconfig | 4 | ||||
-rw-r--r-- | include/mingw.h | 3 | ||||
-rw-r--r-- | libbb/time.c | 9 | ||||
-rw-r--r-- | win32/Kbuild | 1 | ||||
-rw-r--r-- | win32/strptime.c | 37 | ||||
-rw-r--r-- | win32/timegm.c | 205 |
7 files changed, 254 insertions, 9 deletions
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index 0882741d5..195020151 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig | |||
@@ -1,7 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
3 | # Busybox version: 1.35.0.git | 3 | # Busybox version: 1.35.0.git |
4 | # Fri Sep 10 14:50:08 2021 | 4 | # Fri Sep 17 09:17:23 2021 |
5 | # | 5 | # |
6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
@@ -147,7 +147,7 @@ CONFIG_FEATURE_COPYBUF_KB=4 | |||
147 | # CONFIG_MONOTONIC_SYSCALL is not set | 147 | # CONFIG_MONOTONIC_SYSCALL is not set |
148 | # CONFIG_IOCTL_HEX2STR_ERROR is not set | 148 | # CONFIG_IOCTL_HEX2STR_ERROR is not set |
149 | # CONFIG_FEATURE_HWIB is not set | 149 | # CONFIG_FEATURE_HWIB is not set |
150 | # CONFIG_FEATURE_TIMEZONE is not set | 150 | CONFIG_FEATURE_TIMEZONE=y |
151 | 151 | ||
152 | # | 152 | # |
153 | # Applets | 153 | # Applets |
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index 358d19d4e..aa704fe75 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig | |||
@@ -1,7 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
3 | # Busybox version: 1.35.0.git | 3 | # Busybox version: 1.35.0.git |
4 | # Fri Sep 10 14:50:08 2021 | 4 | # Fri Sep 17 09:17:23 2021 |
5 | # | 5 | # |
6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
@@ -147,7 +147,7 @@ CONFIG_FEATURE_COPYBUF_KB=4 | |||
147 | # CONFIG_MONOTONIC_SYSCALL is not set | 147 | # CONFIG_MONOTONIC_SYSCALL is not set |
148 | # CONFIG_IOCTL_HEX2STR_ERROR is not set | 148 | # CONFIG_IOCTL_HEX2STR_ERROR is not set |
149 | # CONFIG_FEATURE_HWIB is not set | 149 | # CONFIG_FEATURE_HWIB is not set |
150 | # CONFIG_FEATURE_TIMEZONE is not set | 150 | CONFIG_FEATURE_TIMEZONE=y |
151 | 151 | ||
152 | # | 152 | # |
153 | # Applets | 153 | # Applets |
diff --git a/include/mingw.h b/include/mingw.h index 03ef89029..d48ad3814 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
@@ -272,6 +272,8 @@ struct timespec { | |||
272 | }; | 272 | }; |
273 | #endif | 273 | #endif |
274 | 274 | ||
275 | time_t timegm(struct tm *tm); | ||
276 | |||
275 | int nanosleep(const struct timespec *req, struct timespec *rem); | 277 | int nanosleep(const struct timespec *req, struct timespec *rem); |
276 | 278 | ||
277 | /* | 279 | /* |
@@ -402,6 +404,7 @@ pid_t mingw_wait3(pid_t pid, int *status, int options, struct rusage *rusage); | |||
402 | struct tm *gmtime_r(const time_t *timep, struct tm *result); | 404 | struct tm *gmtime_r(const time_t *timep, struct tm *result); |
403 | struct tm *localtime_r(const time_t *timep, struct tm *result); | 405 | struct tm *localtime_r(const time_t *timep, struct tm *result); |
404 | char *strptime(const char *s, const char *format, struct tm *tm); | 406 | char *strptime(const char *s, const char *format, struct tm *tm); |
407 | char *mingw_strptime(const char *s, const char *format, struct tm *tm, long *gmt); | ||
405 | size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm); | 408 | size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm); |
406 | 409 | ||
407 | #define strftime mingw_strftime | 410 | #define strftime mingw_strftime |
diff --git a/libbb/time.c b/libbb/time.c index 41a69c754..ed4f50470 100644 --- a/libbb/time.c +++ b/libbb/time.c | |||
@@ -43,7 +43,12 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |||
43 | save = *ptm; | 43 | save = *ptm; |
44 | fmt = fmt_str; | 44 | fmt = fmt_str; |
45 | while (*fmt) { | 45 | while (*fmt) { |
46 | #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_TIMEZONE | ||
47 | long gmtoff; | ||
48 | endp = mingw_strptime(date_str, fmt, ptm, &gmtoff); | ||
49 | #else | ||
46 | endp = strptime(date_str, fmt, ptm); | 50 | endp = strptime(date_str, fmt, ptm); |
51 | #endif | ||
47 | if (endp && *endp == '\0') { | 52 | if (endp && *endp == '\0') { |
48 | #if ENABLE_FEATURE_TIMEZONE | 53 | #if ENABLE_FEATURE_TIMEZONE |
49 | if (strchr(fmt, 'z')) { | 54 | if (strchr(fmt, 'z')) { |
@@ -51,7 +56,11 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm) | |||
51 | struct tm *utm; | 56 | struct tm *utm; |
52 | 57 | ||
53 | /* we have timezone offset: obtain Unix time_t */ | 58 | /* we have timezone offset: obtain Unix time_t */ |
59 | #if ENABLE_PLATFORM_MINGW32 | ||
60 | ptm->tm_sec -= gmtoff; | ||
61 | #else | ||
54 | ptm->tm_sec -= ptm->tm_gmtoff; | 62 | ptm->tm_sec -= ptm->tm_gmtoff; |
63 | #endif | ||
55 | ptm->tm_isdst = 0; | 64 | ptm->tm_isdst = 0; |
56 | t = timegm(ptm); | 65 | t = timegm(ptm); |
57 | if (t == (time_t)-1) | 66 | if (t == (time_t)-1) |
diff --git a/win32/Kbuild b/win32/Kbuild index 6ab0dc077..c38a9ef4d 100644 --- a/win32/Kbuild +++ b/win32/Kbuild | |||
@@ -26,5 +26,6 @@ lib-$(CONFIG_PLATFORM_MINGW32) += strndup.o | |||
26 | lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o | 26 | lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o |
27 | lib-$(CONFIG_PLATFORM_MINGW32) += system.o | 27 | lib-$(CONFIG_PLATFORM_MINGW32) += system.o |
28 | lib-$(CONFIG_PLATFORM_MINGW32) += termios.o | 28 | lib-$(CONFIG_PLATFORM_MINGW32) += termios.o |
29 | lib-$(CONFIG_FEATURE_TIMEZONE) += timegm.o | ||
29 | lib-$(CONFIG_PLATFORM_MINGW32) += uname.o | 30 | lib-$(CONFIG_PLATFORM_MINGW32) += uname.o |
30 | lib-$(CONFIG_PLATFORM_MINGW32) += winansi.o | 31 | lib-$(CONFIG_PLATFORM_MINGW32) += winansi.o |
diff --git a/win32/strptime.c b/win32/strptime.c index 75e5e34f7..3205b95a2 100644 --- a/win32/strptime.c +++ b/win32/strptime.c | |||
@@ -19,6 +19,8 @@ | |||
19 | * File from gnulib (https://www.gnu.org/software/gnulib/), processed with | 19 | * File from gnulib (https://www.gnu.org/software/gnulib/), processed with |
20 | * coan source -U_LIBC -U_NL_CURRENT -UHAVE_TM_GMTOFF strptime.c | 20 | * coan source -U_LIBC -U_NL_CURRENT -UHAVE_TM_GMTOFF strptime.c |
21 | * and lightly edited. | 21 | * and lightly edited. |
22 | * | ||
23 | * A form of support for tm_gmtoff was later restored. | ||
22 | */ | 24 | */ |
23 | 25 | ||
24 | #include "libbb.h" | 26 | #include "libbb.h" |
@@ -62,7 +64,7 @@ enum ptime_locale_status { not, loc, raw }; | |||
62 | #define recursive(new_fmt) \ | 64 | #define recursive(new_fmt) \ |
63 | (*(new_fmt) != '\0' \ | 65 | (*(new_fmt) != '\0' \ |
64 | && (rp = __strptime_internal (rp, (new_fmt), tm, \ | 66 | && (rp = __strptime_internal (rp, (new_fmt), tm, \ |
65 | decided, era_cnt)) != NULL) | 67 | decided, era_cnt, gmtoff)) != NULL) |
66 | 68 | ||
67 | 69 | ||
68 | static char const weekday_name[][10] = | 70 | static char const weekday_name[][10] = |
@@ -141,7 +143,8 @@ day_of_the_year (struct tm *tm) | |||
141 | 143 | ||
142 | static char * | 144 | static char * |
143 | __strptime_internal (const char *rp, const char *fmt, struct tm *tm, | 145 | __strptime_internal (const char *rp, const char *fmt, struct tm *tm, |
144 | enum ptime_locale_status *decided, int era_cnt) | 146 | enum ptime_locale_status *decided, int era_cnt, |
147 | long *gmtoff) | ||
145 | { | 148 | { |
146 | 149 | ||
147 | int cnt; | 150 | int cnt; |
@@ -426,16 +429,25 @@ __strptime_internal (const char *rp, const char *fmt, struct tm *tm, | |||
426 | case 'z': | 429 | case 'z': |
427 | /* We recognize two formats: if two digits are given, these | 430 | /* We recognize two formats: if two digits are given, these |
428 | specify hours. If fours digits are used, minutes are | 431 | specify hours. If fours digits are used, minutes are |
429 | also specified. */ | 432 | also specified. And 'Z'. |
433 | |||
434 | Three formats! We recognize three formats... */ | ||
430 | { | 435 | { |
436 | bool neg; | ||
431 | int n; | 437 | int n; |
432 | 438 | ||
433 | val = 0; | 439 | val = 0; |
434 | while (*rp == ' ') | 440 | while (*rp == ' ') |
435 | ++rp; | 441 | ++rp; |
442 | if (*rp == 'Z') { | ||
443 | ++rp; | ||
444 | if (gmtoff) | ||
445 | *gmtoff = 0; | ||
446 | break; | ||
447 | } | ||
436 | if (*rp != '+' && *rp != '-') | 448 | if (*rp != '+' && *rp != '-') |
437 | return NULL; | 449 | return NULL; |
438 | ++rp; | 450 | neg = *rp++ == '-'; |
439 | n = 0; | 451 | n = 0; |
440 | while (n < 4 && *rp >= '0' && *rp <= '9') | 452 | while (n < 4 && *rp >= '0' && *rp <= '9') |
441 | { | 453 | { |
@@ -456,6 +468,11 @@ __strptime_internal (const char *rp, const char *fmt, struct tm *tm, | |||
456 | } | 468 | } |
457 | if (val > 1200) | 469 | if (val > 1200) |
458 | return NULL; | 470 | return NULL; |
471 | if (gmtoff) { | ||
472 | *gmtoff = (val * 3600) / 100; | ||
473 | if (neg) | ||
474 | *gmtoff = -*gmtoff; | ||
475 | } | ||
459 | } | 476 | } |
460 | break; | 477 | break; |
461 | case 'E': | 478 | case 'E': |
@@ -571,6 +588,16 @@ strptime (const char *buf, const char *format, struct tm *tm) | |||
571 | enum ptime_locale_status decided; | 588 | enum ptime_locale_status decided; |
572 | 589 | ||
573 | decided = raw; | 590 | decided = raw; |
574 | return __strptime_internal (buf, format, tm, &decided, -1); | 591 | return __strptime_internal (buf, format, tm, &decided, -1, NULL); |
592 | } | ||
593 | |||
594 | char * | ||
595 | mingw_strptime (const char *buf, const char *format, struct tm *tm, | ||
596 | long *gmtoff) | ||
597 | { | ||
598 | enum ptime_locale_status decided; | ||
599 | |||
600 | decided = raw; | ||
601 | return __strptime_internal (buf, format, tm, &decided, -1, gmtoff); | ||
575 | } | 602 | } |
576 | 603 | ||
diff --git a/win32/timegm.c b/win32/timegm.c new file mode 100644 index 000000000..abba2579f --- /dev/null +++ b/win32/timegm.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | timegm from musl (https://www.musl-libc.org/). | ||
3 | |||
4 | MIT licensed: | ||
5 | |||
6 | ---------------------------------------------------------------------- | ||
7 | Copyright © 2005-2020 Rich Felker, et al. | ||
8 | |||
9 | Permission is hereby granted, free of charge, to any person obtaining | ||
10 | a copy of this software and associated documentation files (the | ||
11 | "Software"), to deal in the Software without restriction, including | ||
12 | without limitation the rights to use, copy, modify, merge, publish, | ||
13 | distribute, sublicense, and/or sell copies of the Software, and to | ||
14 | permit persons to whom the Software is furnished to do so, subject to | ||
15 | the following conditions: | ||
16 | |||
17 | The above copyright notice and this permission notice shall be | ||
18 | included in all copies or substantial portions of the Software. | ||
19 | |||
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
23 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
25 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
27 | ---------------------------------------------------------------------- | ||
28 | */ | ||
29 | #include "libbb.h" | ||
30 | |||
31 | static long long __year_to_secs(long long year, int *is_leap) | ||
32 | { | ||
33 | int cycles, centuries, leaps, rem; | ||
34 | |||
35 | if (year-2ULL <= 136) { | ||
36 | int y = year; | ||
37 | leaps = (y-68)>>2; | ||
38 | if (!((y-68)&3)) { | ||
39 | leaps--; | ||
40 | if (is_leap) *is_leap = 1; | ||
41 | } else if (is_leap) *is_leap = 0; | ||
42 | return 31536000*(y-70) + 86400*leaps; | ||
43 | } | ||
44 | |||
45 | if (!is_leap) is_leap = &(int){0}; | ||
46 | cycles = (year-100) / 400; | ||
47 | rem = (year-100) % 400; | ||
48 | if (rem < 0) { | ||
49 | cycles--; | ||
50 | rem += 400; | ||
51 | } | ||
52 | if (!rem) { | ||
53 | *is_leap = 1; | ||
54 | centuries = 0; | ||
55 | leaps = 0; | ||
56 | } else { | ||
57 | if (rem >= 200) { | ||
58 | if (rem >= 300) centuries = 3, rem -= 300; | ||
59 | else centuries = 2, rem -= 200; | ||
60 | } else { | ||
61 | if (rem >= 100) centuries = 1, rem -= 100; | ||
62 | else centuries = 0; | ||
63 | } | ||
64 | if (!rem) { | ||
65 | *is_leap = 0; | ||
66 | leaps = 0; | ||
67 | } else { | ||
68 | leaps = rem / 4U; | ||
69 | rem %= 4U; | ||
70 | *is_leap = !rem; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | leaps += 97*cycles + 24*centuries - *is_leap; | ||
75 | |||
76 | return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; | ||
77 | } | ||
78 | |||
79 | static int __month_to_secs(int month, int is_leap) | ||
80 | { | ||
81 | static const int secs_through_month[] = { | ||
82 | 0, 31*86400, 59*86400, 90*86400, | ||
83 | 120*86400, 151*86400, 181*86400, 212*86400, | ||
84 | 243*86400, 273*86400, 304*86400, 334*86400 }; | ||
85 | int t = secs_through_month[month]; | ||
86 | if (is_leap && month >= 2) t+=86400; | ||
87 | return t; | ||
88 | } | ||
89 | |||
90 | static long long __tm_to_secs(const struct tm *tm) | ||
91 | { | ||
92 | int is_leap; | ||
93 | long long t; | ||
94 | long long year = tm->tm_year; | ||
95 | int month = tm->tm_mon; | ||
96 | if (month >= 12 || month < 0) { | ||
97 | int adj = month / 12; | ||
98 | month %= 12; | ||
99 | if (month < 0) { | ||
100 | adj--; | ||
101 | month += 12; | ||
102 | } | ||
103 | year += adj; | ||
104 | } | ||
105 | t = __year_to_secs(year, &is_leap); | ||
106 | t += __month_to_secs(month, is_leap); | ||
107 | t += 86400LL * (tm->tm_mday-1); | ||
108 | t += 3600LL * tm->tm_hour; | ||
109 | t += 60LL * tm->tm_min; | ||
110 | t += tm->tm_sec; | ||
111 | return t; | ||
112 | } | ||
113 | |||
114 | /* 2000-03-01 (mod 400 year, immediately after feb29 */ | ||
115 | #define LEAPOCH (946684800LL + 86400*(31+29)) | ||
116 | |||
117 | #define DAYS_PER_400Y (365*400 + 97) | ||
118 | #define DAYS_PER_100Y (365*100 + 24) | ||
119 | #define DAYS_PER_4Y (365*4 + 1) | ||
120 | |||
121 | static int __secs_to_tm(long long t, struct tm *tm) | ||
122 | { | ||
123 | long long days, secs, years; | ||
124 | int remdays, remsecs, remyears; | ||
125 | int qc_cycles, c_cycles, q_cycles; | ||
126 | int months; | ||
127 | int wday, yday, leap; | ||
128 | static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; | ||
129 | |||
130 | /* Reject time_t values whose year would overflow int */ | ||
131 | if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) | ||
132 | return -1; | ||
133 | |||
134 | secs = t - LEAPOCH; | ||
135 | days = secs / 86400; | ||
136 | remsecs = secs % 86400; | ||
137 | if (remsecs < 0) { | ||
138 | remsecs += 86400; | ||
139 | days--; | ||
140 | } | ||
141 | |||
142 | wday = (3+days)%7; | ||
143 | if (wday < 0) wday += 7; | ||
144 | |||
145 | qc_cycles = days / DAYS_PER_400Y; | ||
146 | remdays = days % DAYS_PER_400Y; | ||
147 | if (remdays < 0) { | ||
148 | remdays += DAYS_PER_400Y; | ||
149 | qc_cycles--; | ||
150 | } | ||
151 | |||
152 | c_cycles = remdays / DAYS_PER_100Y; | ||
153 | if (c_cycles == 4) c_cycles--; | ||
154 | remdays -= c_cycles * DAYS_PER_100Y; | ||
155 | |||
156 | q_cycles = remdays / DAYS_PER_4Y; | ||
157 | if (q_cycles == 25) q_cycles--; | ||
158 | remdays -= q_cycles * DAYS_PER_4Y; | ||
159 | |||
160 | remyears = remdays / 365; | ||
161 | if (remyears == 4) remyears--; | ||
162 | remdays -= remyears * 365; | ||
163 | |||
164 | leap = !remyears && (q_cycles || !c_cycles); | ||
165 | yday = remdays + 31 + 28 + leap; | ||
166 | if (yday >= 365+leap) yday -= 365+leap; | ||
167 | |||
168 | years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles; | ||
169 | |||
170 | for (months=0; days_in_month[months] <= remdays; months++) | ||
171 | remdays -= days_in_month[months]; | ||
172 | |||
173 | if (months >= 10) { | ||
174 | months -= 12; | ||
175 | years++; | ||
176 | } | ||
177 | |||
178 | if (years+100 > INT_MAX || years+100 < INT_MIN) | ||
179 | return -1; | ||
180 | |||
181 | tm->tm_year = years + 100; | ||
182 | tm->tm_mon = months + 2; | ||
183 | tm->tm_mday = remdays + 1; | ||
184 | tm->tm_wday = wday; | ||
185 | tm->tm_yday = yday; | ||
186 | |||
187 | tm->tm_hour = remsecs / 3600; | ||
188 | tm->tm_min = remsecs / 60 % 60; | ||
189 | tm->tm_sec = remsecs % 60; | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | time_t timegm(struct tm *tm) | ||
195 | { | ||
196 | struct tm new; | ||
197 | long long t = __tm_to_secs(tm); | ||
198 | if (__secs_to_tm(t, &new) < 0) { | ||
199 | errno = EOVERFLOW; | ||
200 | return -1; | ||
201 | } | ||
202 | *tm = new; | ||
203 | tm->tm_isdst = 0; | ||
204 | return t; | ||
205 | } | ||