aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-09-17 09:18:58 +0100
committerRon Yorston <rmy@pobox.com>2021-09-17 11:41:25 +0100
commit9e16eecc70020e9a603d637f6a8fdfc7c95c30e1 (patch)
tree5700c068c09ad6028551449c2c9deac13b5c3b32
parent46299d0c4f4c9a4bbad38bbbe26f196e1bccdc52 (diff)
downloadbusybox-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_defconfig4
-rw-r--r--configs/mingw64_defconfig4
-rw-r--r--include/mingw.h3
-rw-r--r--libbb/time.c9
-rw-r--r--win32/Kbuild1
-rw-r--r--win32/strptime.c37
-rw-r--r--win32/timegm.c205
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#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_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 150CONFIG_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#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_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 150CONFIG_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
275time_t timegm(struct tm *tm);
276
275int nanosleep(const struct timespec *req, struct timespec *rem); 277int 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);
402struct tm *gmtime_r(const time_t *timep, struct tm *result); 404struct tm *gmtime_r(const time_t *timep, struct tm *result);
403struct tm *localtime_r(const time_t *timep, struct tm *result); 405struct tm *localtime_r(const time_t *timep, struct tm *result);
404char *strptime(const char *s, const char *format, struct tm *tm); 406char *strptime(const char *s, const char *format, struct tm *tm);
407char *mingw_strptime(const char *s, const char *format, struct tm *tm, long *gmt);
405size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm); 408size_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
26lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o 26lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o
27lib-$(CONFIG_PLATFORM_MINGW32) += system.o 27lib-$(CONFIG_PLATFORM_MINGW32) += system.o
28lib-$(CONFIG_PLATFORM_MINGW32) += termios.o 28lib-$(CONFIG_PLATFORM_MINGW32) += termios.o
29lib-$(CONFIG_FEATURE_TIMEZONE) += timegm.o
29lib-$(CONFIG_PLATFORM_MINGW32) += uname.o 30lib-$(CONFIG_PLATFORM_MINGW32) += uname.o
30lib-$(CONFIG_PLATFORM_MINGW32) += winansi.o 31lib-$(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
68static char const weekday_name[][10] = 70static char const weekday_name[][10] =
@@ -141,7 +143,8 @@ day_of_the_year (struct tm *tm)
141 143
142static char * 144static 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
594char *
595mingw_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----------------------------------------------------------------------
7Copyright © 2005-2020 Rich Felker, et al.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27----------------------------------------------------------------------
28*/
29#include "libbb.h"
30
31static 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
79static 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
90static 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
121static 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
194time_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}