1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-26 12:56:37 +02:00

api: fix parsing of date/times with timezone offset in function util_parse_time

This commit is contained in:
Sébastien Helleu
2025-08-30 18:42:50 +02:00
parent 7980a6d100
commit 5acbfe9b7d
3 changed files with 99 additions and 142 deletions
+1
View File
@@ -19,6 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
### Fixed
- api: fix parsing of date/times with timezone offset in function util_parse_time
- relay/api: fix crash when an invalid HTTP request is received from a client
## Version 4.7.1 (2025-08-16)
+47 -90
View File
@@ -349,8 +349,8 @@ util_strftimeval (char *string, int max, const char *format, struct timeval *tv)
int
util_parse_time (const char *datetime, struct timeval *tv)
{
char *string, *pos, *pos2, *pos_colon, *pos_hyphen, str_usec[16], *error;
char str_date[128], str_date2[256];
char *string, *pos, *pos2, *pos_colon, *pos_hyphen, *pos_dot;
char str_usec[16], *error, str_date[128], str_date2[256];
struct tm tm_date, tm_date_gm, tm_date_local, *local_time;
time_t time_now, time_gm, time_local;
long long value;
@@ -370,6 +370,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
pos_colon = strchr (datetime, ':');
pos_hyphen = strchr (datetime, '-');
pos_dot = strchr (datetime, '.');
if (pos_colon && !pos_hyphen)
{
/* add current date: "21:01:02" -> "2024-01-04T21:01:02" */
@@ -381,7 +382,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
strftime (str_date, sizeof (str_date), "%Y-%m-%dT", local_time);
snprintf (string, sizeof (str_date2), "%s%s", str_date, datetime);
}
else if (!pos_colon && pos_hyphen)
else if (!pos_colon && pos_hyphen && (!pos_dot || (pos_hyphen < pos_dot)))
{
/* add time (midnight): "2024-01-04" -> "2024-01-04T00:00:00" */
string = malloc (strlen (datetime) + 16 + 1);
@@ -436,7 +437,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
memmove (pos, pos2, strlen (pos2) + 1);
}
/* extract timezone and remove it from string2 */
/* extract timezone and remove it from string */
pos = strrchr (string, 'Z');
if (pos)
{
@@ -451,7 +452,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
pos = strchr (string, ' ');
if (pos)
{
pos2 = strchr (pos, '+');
pos2 = strrchr (pos, '+');
if (pos2)
{
pos2[0] = '\0';
@@ -460,7 +461,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
}
else
{
pos2 = strchr (pos, '-');
pos2 = strrchr (pos, '-');
if (pos2)
{
pos2[0] = '\0';
@@ -491,109 +492,65 @@ util_parse_time (const char *datetime, struct timeval *tv)
}
}
if (strchr (string, '-'))
if (strchr (string, '-') && strchr (string, ':'))
{
if (strchr (string, ':'))
if (strchr (string, 'T'))
{
if (strchr (string, 'T'))
/* ISO 8601 format like: "2024-01-04T21:01:02" */
/* initialize structure, because strptime does not do it */
memset (&tm_date, 0, sizeof (struct tm));
pos = strptime (string, "%Y-%m-%dT%H:%M:%S", &tm_date);
if (pos && (tm_date.tm_year > 0))
{
/* ISO 8601 format like: "2024-01-04T21:01:02.123Z" */
/* initialize structure, because strptime does not do it */
memset (&tm_date, 0, sizeof (struct tm));
pos = strptime (string, "%Y-%m-%dT%H:%M:%S", &tm_date);
if (pos && (tm_date.tm_year > 0))
if (use_local_time)
{
if (use_local_time)
{
tv->tv_sec = mktime (&tm_date);
}
else
{
/* convert to UTC and add timezone_offset */
time_now = mktime (&tm_date);
gmtime_r (&time_now, &tm_date_gm);
localtime_r (&time_now, &tm_date_local);
time_gm = mktime (&tm_date_gm);
time_local = mktime (&tm_date_local);
tv->tv_sec = mktime (&tm_date_local)
+ (time_local - time_gm)
+ timezone_offset;
}
rc = 1;
tm_date.tm_isdst = -1;
tv->tv_sec = mktime (&tm_date);
}
}
else
{
/* like ISO 8601 but with space like: "2024-01-04 21:01:02.123Z" */
/* initialize structure, because strptime does not do it */
memset (&tm_date, 0, sizeof (struct tm));
pos = strptime (string, "%Y-%m-%d %H:%M:%S", &tm_date);
if (pos && (tm_date.tm_year > 0))
else
{
if (use_local_time)
{
tv->tv_sec = mktime (&tm_date);
}
else
{
/* convert to UTC and add timezone_offset */
time_now = mktime (&tm_date);
gmtime_r (&time_now, &tm_date_gm);
localtime_r (&time_now, &tm_date_local);
time_gm = mktime (&tm_date_gm);
time_local = mktime (&tm_date_local);
tv->tv_sec = mktime (&tm_date_local)
+ (time_local - time_gm)
+ timezone_offset;
}
rc = 1;
/* convert to UTC and add timezone_offset */
time_now = mktime (&tm_date);
gmtime_r (&time_now, &tm_date_gm);
localtime_r (&time_now, &tm_date_local);
time_gm = mktime (&tm_date_gm);
time_local = mktime (&tm_date_local);
tv->tv_sec = mktime (&tm_date_local)
+ (time_local - time_gm)
- timezone_offset;
}
rc = 1;
}
}
else
{
/* ISO 8601 format like: "2024-01-04" */
/* like ISO 8601 but with space like: "2024-01-04 21:01:02" */
/* initialize structure, because strptime does not do it */
memset (&tm_date, 0, sizeof (struct tm));
pos = strptime (string, "%Y-%m-%d", &tm_date);
pos = strptime (string, "%Y-%m-%d %H:%M:%S", &tm_date);
if (pos && (tm_date.tm_year > 0))
{
tv->tv_sec = mktime (&tm_date);
if (use_local_time)
{
tm_date.tm_isdst = -1;
tv->tv_sec = mktime (&tm_date);
}
else
{
/* convert to UTC and add timezone_offset */
time_now = mktime (&tm_date);
gmtime_r (&time_now, &tm_date_gm);
localtime_r (&time_now, &tm_date_local);
time_gm = mktime (&tm_date_gm);
time_local = mktime (&tm_date_local);
tv->tv_sec = mktime (&tm_date_local)
+ (time_local - time_gm)
- timezone_offset;
}
rc = 1;
}
}
}
else if (strchr (string, ':'))
{
/* hour format like: "21:01:02" */
time_now = time (NULL);
local_time = localtime (&time_now);
strftime (str_date, sizeof (str_date), "%Y-%m-%dT", local_time);
snprintf (str_date2, sizeof (str_date2), "%s%s", str_date, string);
/* initialize structure, because strptime does not do it */
memset (&tm_date, 0, sizeof (struct tm));
pos = strptime (str_date2, "%Y-%m-%dT%H:%M:%S", &tm_date);
if (pos)
{
if (use_local_time)
{
tv->tv_sec = mktime (&tm_date);
}
else
{
/* convert to UTC and add timezone_offset */
time_now = mktime (&tm_date);
gmtime_r (&time_now, &tm_date_gm);
localtime_r (&time_now, &tm_date_local);
time_gm = mktime (&tm_date_gm);
time_local = mktime (&tm_date_local);
tv->tv_sec = mktime (&tm_date_local)
+ (time_local - time_gm)
+ timezone_offset;
}
rc = 1;
}
}
else
{
/* timestamp format: "1704402062" */
+51 -52
View File
@@ -291,8 +291,9 @@ TEST(CoreUtil, ParseTime)
time_t date;
char str_time[128];
/* test with local timezone: UTC+1 */
setenv ("TZ", "UTC+1", 1);
/* test with UTC timezone */
setenv ("TZ", "UTC", 1);
tzset ();
LONGS_EQUAL(0, util_parse_time (NULL, NULL));
LONGS_EQUAL(0, util_parse_time (NULL, &tv));
@@ -305,9 +306,8 @@ TEST(CoreUtil, ParseTime)
/*
* expected: 2023-12-25T00:00:00Z == 1703462400
* (local timezone UTC+1: 1703466000)
*/
WEE_PARSE_DATE(1, 1703466000, 0, "2023-12-25");
WEE_PARSE_DATE(1, 1703462400, 0, "2023-12-25");
/* expected: current date with specified local time */
date = time (NULL);
@@ -329,42 +329,42 @@ TEST(CoreUtil, ParseTime)
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S+0100", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
LONGS_EQUAL(0, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S +01:00", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
LONGS_EQUAL(0, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S +02:00", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date + 3600) && (tv.tv_sec <= date + 3600 + 10));
CHECK((tv.tv_sec >= date - 7200) && (tv.tv_sec <= date - 7200 + 10));
LONGS_EQUAL(0, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S.456789+0100", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
LONGS_EQUAL(456789, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S.456789 +01:00", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
LONGS_EQUAL(456789, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S.456789 +02:00", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date + 3600) && (tv.tv_sec <= date + 3600 + 10));
CHECK((tv.tv_sec >= date - 7200) && (tv.tv_sec <= date - 7200 + 10));
LONGS_EQUAL(456789, tv.tv_usec);
/* expected: current date with specified UTC time */
@@ -372,31 +372,29 @@ TEST(CoreUtil, ParseTime)
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%SZ", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
LONGS_EQUAL(0, tv.tv_usec);
date = time (NULL);
local_time = localtime (&date);
strftime (str_time, sizeof (str_time), "%H:%M:%S.456789Z", local_time);
LONGS_EQUAL(1, util_parse_time (str_time, &tv));
CHECK((tv.tv_sec >= date - 3600) && (tv.tv_sec <= date - 3600 + 10));
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
LONGS_EQUAL(456789, tv.tv_usec);
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
* (local timezone UTC+1: 1703503749.456789)
*/
WEE_PARSE_DATE(1, 1703500149 + 3600, 0, "2023-12-25T10:29:09");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09Z");
WEE_PARSE_DATE(1, 1703500149, 456000, "2023-12-25T10:29:09.456Z");
WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25T10:29:09.456789Z");
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
* (local timezone UTC+1: 1703503749.456789)
* with space instead of "T"
*/
WEE_PARSE_DATE(1, 1703500149 + 3600, 0, "2023-12-25 10:29:09");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09Z");
WEE_PARSE_DATE(1, 1703500149, 456000, "2023-12-25 10:29:09.456Z");
WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789Z");
@@ -413,20 +411,20 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T10:29:09-00:00");
WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25T10:29:09.456789-00:00");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+02");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25T10:29:09+0001");
WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25T10:29:09.456789+0001");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25T12:29:09+02");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25T10:29:09+0001");
WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25T10:29:09.456789+0001");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+0200");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25T10:29:09+02:00");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09+0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09+01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25T10:29:09.456789+01:30");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25T10:29:09+0200");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25T10:29:09+02:00");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09+0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09+01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25T10:29:09.456789+01:30");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25T10:29:09-0001");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09-0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25T10:29:09-01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25T10:29:09.456789-01:30");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25T10:29:09-0001");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09-0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25T10:29:09-01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25T10:29:09.456789-01:30");
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
@@ -440,20 +438,20 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09-00:00");
WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789-00:00");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+02");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09+0001");
WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25 10:29:09.456789+0001");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+02");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09+0001");
WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25 10:29:09.456789+0001");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+0200");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09+02:00");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09+0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09+01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789+01:30");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+0200");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09+02:00");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09+0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09+01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789+01:30");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09-0001");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09-0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09-01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789-01:30");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09-0001");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09-0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09-01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789-01:30");
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
@@ -467,20 +465,20 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 -00:00");
WEE_PARSE_DATE(1, 1703500149, 456789, "2023-12-25 10:29:09.456789 -00:00");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +02");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09 +0001");
WEE_PARSE_DATE(1, 1703500149 + 60, 456789, "2023-12-25 10:29:09.456789 +0001");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +02");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09 +0001");
WEE_PARSE_DATE(1, 1703500149 - 60, 456789, "2023-12-25 10:29:09.456789 +0001");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +0200");
WEE_PARSE_DATE(1, 1703500149 + 7200, 0, "2023-12-25 10:29:09 +02:00");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 +0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 +01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789 +01:30");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +0200");
WEE_PARSE_DATE(1, 1703500149 - 7200, 0, "2023-12-25 10:29:09 +02:00");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 +0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 +01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789 +01:30");
WEE_PARSE_DATE(1, 1703500149 - 60, 0, "2023-12-25 10:29:09 -0001");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 -0130");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 0, "2023-12-25 10:29:09 -01:30");
WEE_PARSE_DATE(1, 1703500149 - 3600 - 1800, 456789, "2023-12-25 10:29:09.456789 -01:30");
WEE_PARSE_DATE(1, 1703500149 + 60, 0, "2023-12-25 10:29:09 -0001");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 -0130");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 0, "2023-12-25 10:29:09 -01:30");
WEE_PARSE_DATE(1, 1703500149 + 3600 + 1800, 456789, "2023-12-25 10:29:09.456789 -01:30");
/* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 */
WEE_PARSE_DATE(1, 1703500149, 0, "1703500149");
@@ -495,6 +493,7 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(1, 1703500149, 456789, "1703500149.456789123456789123456789123456789");
setenv ("TZ", "", 1);
tzset ();
}
/*