Skip to content

Commit 0f096c2

Browse files
committed
fix: populate timezone data when formatting time
The `jv2tm` function was zeroing fields of `struct tm` that were not specified by the standard. However, depending on the libc this produced incorrect timezone data when used together with formatting functions. This change tries to fill the timezone data using either `mktime`, `timegm`, or manually.
1 parent b86ff49 commit 0f096c2

File tree

1 file changed

+22
-10
lines changed

1 file changed

+22
-10
lines changed

src/builtin.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,7 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
15941594
return r;
15951595
}
15961596

1597-
static int jv2tm(jv a, struct tm *tm) {
1597+
static int jv2tm(jv a, struct tm *tm, int localtime) {
15981598
memset(tm, 0, sizeof(*tm));
15991599
static const size_t offsets[] = {
16001600
offsetof(struct tm, tm_year),
@@ -1624,13 +1624,25 @@ static int jv2tm(jv a, struct tm *tm) {
16241624
jv_free(n);
16251625
}
16261626

1627-
// We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.
1628-
// Setting tm_isdst to 0 is done by the memset.
1629-
// tm->tm_isdst = 0;
1627+
if (localtime) {
1628+
tm->tm_isdst = -1;
1629+
mktime(tm);
1630+
} else {
1631+
#ifdef HAVE_TIMEGM
1632+
timegm(tm);
1633+
#elif HAVE_TM_TM_GMT_OFF
1634+
// tm->tm_gmtoff = 0;
1635+
tm->tm_zone = "GMT";
1636+
#elif HAVE_TM___TM_GMT_OFF
1637+
// tm->__tm_gmtoff = 0;
1638+
tm->__tm_zone = "GMT";
1639+
#endif
1640+
// tm->tm_isdst = 0;
16301641

1631-
// The standard permits the tm structure to contain additional members. We
1632-
// hope it is okay to initialize them to zero, because the standard does not
1633-
// provide an alternative.
1642+
// The standard permits the tm structure to contain additional members. We
1643+
// hope it is okay to initialize them to zero, because the standard does not
1644+
// provide an alternative.
1645+
}
16341646

16351647
jv_free(a);
16361648
return 1;
@@ -1642,7 +1654,7 @@ static jv f_mktime(jq_state *jq, jv a) {
16421654
if (jv_get_kind(a) != JV_KIND_ARRAY)
16431655
return ret_error(a, jv_string("mktime requires array inputs"));
16441656
struct tm tm;
1645-
if (!jv2tm(a, &tm))
1657+
if (!jv2tm(a, &tm, 0))
16461658
return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));
16471659
time_t t = my_mktime(&tm);
16481660
if (t == (time_t)-1)
@@ -1740,7 +1752,7 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
17401752
if (jv_get_kind(b) != JV_KIND_STRING)
17411753
return ret_error2(a, b, jv_string("strftime/1 requires a string format"));
17421754
struct tm tm;
1743-
if (!jv2tm(a, &tm))
1755+
if (!jv2tm(a, &tm, 0))
17441756
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
17451757

17461758
const char *fmt = jv_string_value(b);
@@ -1771,7 +1783,7 @@ static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
17711783
if (jv_get_kind(b) != JV_KIND_STRING)
17721784
return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format"));
17731785
struct tm tm;
1774-
if (!jv2tm(a, &tm))
1786+
if (!jv2tm(a, &tm, 1))
17751787
return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
17761788
const char *fmt = jv_string_value(b);
17771789
size_t alloced = strlen(fmt) + 100;

0 commit comments

Comments
 (0)