Skip to content

Commit e3873ef

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 a7b2253 commit e3873ef

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
@@ -1555,7 +1555,7 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
15551555
return r;
15561556
}
15571557

1558-
static int jv2tm(jv a, struct tm *tm) {
1558+
static int jv2tm(jv a, struct tm *tm, int localtime) {
15591559
memset(tm, 0, sizeof(*tm));
15601560
static const size_t offsets[] = {
15611561
offsetof(struct tm, tm_year),
@@ -1585,13 +1585,25 @@ static int jv2tm(jv a, struct tm *tm) {
15851585
jv_free(n);
15861586
}
15871587

1588-
// We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.
1589-
// Setting tm_isdst to 0 is done by the memset.
1590-
// tm->tm_isdst = 0;
1588+
if (localtime) {
1589+
tm->tm_isdst = -1;
1590+
mktime(tm);
1591+
} else {
1592+
#ifdef HAVE_TIMEGM
1593+
timegm(tm);
1594+
#elif HAVE_TM_TM_GMT_OFF
1595+
// tm->tm_gmtoff = 0;
1596+
tm->tm_zone = "GMT";
1597+
#elif HAVE_TM___TM_GMT_OFF
1598+
// tm->__tm_gmtoff = 0;
1599+
tm->__tm_zone = "GMT";
1600+
#endif
1601+
// tm->tm_isdst = 0;
15911602

1592-
// The standard permits the tm structure to contain additional members. We
1593-
// hope it is okay to initialize them to zero, because the standard does not
1594-
// provide an alternative.
1603+
// The standard permits the tm structure to contain additional members. We
1604+
// hope it is okay to initialize them to zero, because the standard does not
1605+
// provide an alternative.
1606+
}
15951607

15961608
jv_free(a);
15971609
return 1;
@@ -1603,7 +1615,7 @@ static jv f_mktime(jq_state *jq, jv a) {
16031615
if (jv_get_kind(a) != JV_KIND_ARRAY)
16041616
return ret_error(a, jv_string("mktime requires array inputs"));
16051617
struct tm tm;
1606-
if (!jv2tm(a, &tm))
1618+
if (!jv2tm(a, &tm, 0))
16071619
return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));
16081620
time_t t = my_mktime(&tm);
16091621
if (t == (time_t)-1)
@@ -1701,7 +1713,7 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
17011713
if (jv_get_kind(b) != JV_KIND_STRING)
17021714
return ret_error2(a, b, jv_string("strftime/1 requires a string format"));
17031715
struct tm tm;
1704-
if (!jv2tm(a, &tm))
1716+
if (!jv2tm(a, &tm, 0))
17051717
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
17061718

17071719
const char *fmt = jv_string_value(b);
@@ -1732,7 +1744,7 @@ static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
17321744
if (jv_get_kind(b) != JV_KIND_STRING)
17331745
return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format"));
17341746
struct tm tm;
1735-
if (!jv2tm(a, &tm))
1747+
if (!jv2tm(a, &tm, 1))
17361748
return ret_error(b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
17371749
const char *fmt = jv_string_value(b);
17381750
size_t alloced = strlen(fmt) + 100;

0 commit comments

Comments
 (0)