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

api: add support of date like ISO 8601 but with spaces in function util_parse_time

So for example the format "2024-01-04 22:01:02.123456 +0100" is supported in
addition to ""2024-01-04T22:01:02.123456+0100".
This commit is contained in:
Sébastien Helleu
2025-08-30 11:51:20 +02:00
parent f630c36af0
commit 7980a6d100
8 changed files with 275 additions and 49 deletions
+1
View File
@@ -10,6 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
### Changed
- api: add support of date like ISO 8601 but with spaces in function util_parse_time
- build: require Curl ≥ 7.68.0 ([#2268](https://github.com/weechat/weechat/issues/2268))
- build: require GnuTLS ≥ 3.6.3 ([#2268](https://github.com/weechat/weechat/issues/2268))
- build: require libgcrypt ≥ 1.8.0 ([#2268](https://github.com/weechat/weechat/issues/2268))
+1 -1
View File
@@ -4847,7 +4847,7 @@ This function is not available in scripting API.
==== util_parse_time
_WeeChat ≥ 4.2.0._
_WeeChat ≥ 4.2.0, updated in 4.8.0._
Parse date/time with support of microseconds.
+1 -1
View File
@@ -4928,7 +4928,7 @@ Cette fonction n'est pas disponible dans l'API script.
==== util_parse_time
_WeeChat ≥ 4.2.0._
_WeeChat ≥ 4.2.0, mis à jour dans la 4.8.0._
Analyser la date/heure avec le support des microsecondes.
+1 -1
View File
@@ -5068,7 +5068,7 @@ Questa funzione non è disponibile nelle API per lo scripting.
// TRANSLATION MISSING
==== util_parse_time
_WeeChat ≥ 4.2.0._
_WeeChat ≥ 4.2.0, updated in 4.8.0._
Parse date/time with support of microseconds.
+1 -1
View File
@@ -4982,7 +4982,7 @@ weechat_util_strftimeval (time, sizeof (time), "%@%FT%T.%fZ", &tv);
// TRANSLATION MISSING
==== util_parse_time
_WeeChat ≥ 4.2.0._
_WeeChat ≥ 4.2.0, updated in 4.8.0._
Parse date/time with support of microseconds.
+1 -1
View File
@@ -4706,7 +4706,7 @@ weechat_util_strftimeval (time, sizeof (time), "%@%FT%T.%fZ", &tv);
==== util_parse_time
_WeeChat ≥ 4.2.0._
_WeeChat ≥ 4.2.0, ажурирано у верзији 4.8.0._
Парсира датум/време са подршком за милисекунде.
+154 -44
View File
@@ -271,24 +271,75 @@ util_strftimeval (char *string, int max, const char *format, struct timeval *tv)
/*
* Parses a date/time string, which can be one of these formats:
* "2024-01-04" -> date at midnight
* "2024-01-04T22:01:02" -> ISO 8601, local time
* "2024-01-04T22:01:02.123" -> ISO 8601, local time, with milliseconds
* "2024-01-04T22:01:02.123456" -> ISO 8601, local time, with microseconds
* "2024-01-04T21:01:02Z" -> ISO 8601, UTC
* "2024-01-04T21:01:02.123Z" -> ISO 8601, UTC, with milliseconds
* "2024-01-04T21:01:02.123456Z" -> ISO 8601, UTC, with microseconds
* "22:01:02" -> current date, local time
* "22:01:02.123" -> current date, local time with milliseconds
* "22.01:02.123456" -> current date, local time with microseconds
* "21:01:02Z" -> current date, UTC
* "21:01:02.123Z" -> current date, UTC, with milliseconds
* "21.01:02.123456Z" -> current date, UTC, with microseconds
* "1704402062" -> timestamp date
* "1704402062.123" -> timestamp date, with milliseconds
* "1704402062,123" -> timestamp date, with milliseconds
* "1704402062.123456" -> timestamp date, with microseconds
* "1704402062,123456" -> timestamp date, with microseconds
*
* "2024-01-04" -> date at midnight
*
* "2024-01-04T22:01:02" -> ISO 8601, local time
* "2024-01-04T22:01:02.123" -> ISO 8601, local time, milliseconds
* "2024-01-04T22:01:02.123456" -> ISO 8601, local time, microseconds
*
* "2024-01-04T22:01:02+0100" -> ISO 8601, local time + offset
* "2024-01-04T22:01:02+01:00" -> ISO 8601, local time + offset
* "2024-01-04T22:01:02.123+0100" -> ISO 8601, local time + offset, milliseconds
* "2024-01-04T22:01:02.123+01:00" -> ISO 8601, local time + offset, milliseconds
* "2024-01-04T22:01:02.123456+0100" -> ISO 8601, local time + offset, microseconds
* "2024-01-04T22:01:02.123456+01:00" -> ISO 8601, local time + offset, microseconds
*
* "2024-01-04T21:01:02Z" -> ISO 8601, UTC
* "2024-01-04T21:01:02.123Z" -> ISO 8601, UTC, milliseconds
* "2024-01-04T21:01:02.123456Z" -> ISO 8601, UTC, microseconds
*
* "2024-01-04 22:01:02" -> ~ISO 8601 (space), local time
* "2024-01-04 22:01:02.123" -> ~ISO 8601 (space), local time, milliseconds
* "2024-01-04 22:01:02.123456" -> ~ISO 8601 (space), local time, microseconds
*
* "2024-01-04 22:01:02+0100" -> ~ISO 8601 (space), local time + offset
* "2024-01-04 22:01:02+01:00" -> ~ISO 8601 (space), local time + offset
* "2024-01-04 22:01:02 +0100" -> ~ISO 8601 (space), local time + offset
* "2024-01-04 22:01:02 +01:00" -> ~ISO 8601 (space), local time + offset
* "2024-01-04 22:01:02.123" -> ~ISO 8601 (space), local time + offset, milliseconds
* "2024-01-04 22:01:02.123+0100" -> ~ISO 8601 (space), local time + offset, milliseconds
* "2024-01-04 22:01:02.123+01:00" -> ~ISO 8601 (space), local time + offset, milliseconds
* "2024-01-04 22:01:02.123 +0100" -> ~ISO 8601 (space), local time + offset, milliseconds
* "2024-01-04 22:01:02.123 +01:00" -> ~ISO 8601 (space), local time + offset, milliseconds
* "2024-01-04 22:01:02.123456" -> ~ISO 8601 (space), local time + offset, microseconds
* "2024-01-04 22:01:02.123456+0100" -> ~ISO 8601 (space), local time + offset, microseconds
* "2024-01-04 22:01:02.123456+01:00" -> ~ISO 8601 (space), local time + offset, microseconds
* "2024-01-04 22:01:02.123456 +0100" -> ~ISO 8601 (space), local time + offset, microseconds
* "2024-01-04 22:01:02.123456 +01:00" -> ~ISO 8601 (space), local time + offset, microseconds
*
* "2024-01-04 21:01:02Z" -> ~ISO 8601 (space), UTC
* "2024-01-04 21:01:02.123Z" -> ~ISO 8601 (space), UTC, milliseconds
* "2024-01-04 21:01:02.123456Z" -> ~ISO 8601 (space), UTC, microseconds
*
* "22:01:02" -> current date, local time
* "22:01:02.123" -> current date, local time, milliseconds
* "22:01:02.123456" -> current date, local time, microseconds
*
* "22:01:02+0100" -> current date, local time + offset
* "22:01:02+01:00" -> current date, local time + offset
* "22:01:02 +0100" -> current date, local time + offset
* "22:01:02 +01:00" -> current date, local time + offset
* "22:01:02.123" -> current date, local time + offset, milliseconds
* "22:01:02.123+0100" -> current date, local time + offset, milliseconds
* "22:01:02.123+01:00" -> current date, local time + offset, milliseconds
* "22:01:02.123 +0100" -> current date, local time + offset, milliseconds
* "22:01:02.123 +01:00" -> current date, local time + offset, milliseconds
* "22:01:02.123456" -> current date, local time + offset, microseconds
* "22:01:02.123456+0100" -> current date, local time + offset, microseconds
* "22:01:02.123456+01:00" -> current date, local time + offset, microseconds
* "22:01:02.123456 +0100" -> current date, local time + offset, microseconds
* "22:01:02.123456 +01:00" -> current date, local time + offset, microseconds
*
* "21:01:02Z" -> current date, UTC
* "21:01:02.123Z" -> current date, UTC, milliseconds
* "21:01:02.123456Z" -> current date, UTC, microseconds
*
* "1704402062" -> timestamp date
* "1704402062.123" -> timestamp date, milliseconds
* "1704402062,123" -> timestamp date, milliseconds
* "1704402062.123456" -> timestamp date, microseconds
* "1704402062,123456" -> timestamp date, microseconds
*
* Returns:
* 1: OK
@@ -298,7 +349,7 @@ 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, str_usec[16], *error;
char *string, *pos, *pos2, *pos_colon, *pos_hyphen, str_usec[16], *error;
char 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;
@@ -317,11 +368,35 @@ util_parse_time (const char *datetime, struct timeval *tv)
timezone_offset = 0;
offset_factor = 1;
string = strdup (datetime);
if (!string)
return 0;
pos_colon = strchr (datetime, ':');
pos_hyphen = strchr (datetime, '-');
if (pos_colon && !pos_hyphen)
{
/* add current date: "21:01:02" -> "2024-01-04T21:01:02" */
string = malloc (strlen (datetime) + 16 + 1);
if (!string)
return 0;
time_now = time (NULL);
local_time = localtime (&time_now);
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)
{
/* add time (midnight): "2024-01-04" -> "2024-01-04T00:00:00" */
string = malloc (strlen (datetime) + 16 + 1);
if (!string)
return 0;
snprintf (string, sizeof (str_date2), "%sT00:00:00", datetime);
}
else
{
string = strdup (datetime);
if (!string)
return 0;
}
/* extract microseconds and remove them from string2 */
/* extract microseconds and remove them from string */
pos = strchr (string, '.');
if (!pos)
pos = strchr (string, ',');
@@ -347,6 +422,10 @@ util_parse_time (const char *datetime, struct timeval *tv)
value = strtoll (str_usec, &error, 10);
if (error && !error[0])
{
/*
* just in case: this should not happen as minus is not
* supported and we truncate at 6 digits
*/
if (value < 0)
value = 0;
else if (value > 999999)
@@ -358,7 +437,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
}
/* extract timezone and remove it from string2 */
pos = strchr (string, 'Z');
pos = strrchr (string, 'Z');
if (pos)
{
pos[0] = '\0';
@@ -368,6 +447,8 @@ util_parse_time (const char *datetime, struct timeval *tv)
else
{
pos = strchr (string, 'T');
if (!pos)
pos = strchr (string, ' ');
if (pos)
{
pos2 = strchr (pos, '+');
@@ -414,29 +495,59 @@ util_parse_time (const char *datetime, struct timeval *tv)
{
if (strchr (string, ':'))
{
/* 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 (strchr (string, 'T'))
{
if (use_local_time)
/* 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))
{
tv->tv_sec = mktime (&tm_date);
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
}
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))
{
/* 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;
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;
}
rc = 1;
}
}
else
@@ -457,8 +568,7 @@ util_parse_time (const char *datetime, struct timeval *tv)
/* 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);
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));
+115
View File
@@ -300,6 +300,9 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(0, 0, 0, "");
WEE_PARSE_DATE(0, 0, 0, "invalid");
/* invalid: negative microseconds */
WEE_PARSE_DATE(0, 0, 0, "1703500149.-456789");
/*
* expected: 2023-12-25T00:00:00Z == 1703462400
* (local timezone UTC+1: 1703466000)
@@ -321,6 +324,49 @@ TEST(CoreUtil, ParseTime)
CHECK((tv.tv_sec >= date) && (tv.tv_sec <= date + 10));
LONGS_EQUAL(456789, tv.tv_usec);
/* expected: current date with specified local time + timezone offset */
date = time (NULL);
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));
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));
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));
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));
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));
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));
LONGS_EQUAL(456789, tv.tv_usec);
/* expected: current date with specified UTC time */
date = time (NULL);
local_time = localtime (&date);
@@ -345,6 +391,16 @@ TEST(CoreUtil, ParseTime)
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: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");
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
* with timezone offset
@@ -372,6 +428,60 @@ TEST(CoreUtil, ParseTime)
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
* with space instead of "T" and timezone offset
*/
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09+00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09+0000");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09+00:00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09-00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09-0000");
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+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");
/*
* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789
* with space instead of "T" and timezone offset after a space
*/
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 +00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 +0000");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 +00:00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 -00");
WEE_PARSE_DATE(1, 1703500149, 0, "2023-12-25 10:29:09 -0000");
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 +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");
/* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 */
WEE_PARSE_DATE(1, 1703500149, 0, "1703500149");
WEE_PARSE_DATE(1, 1703500149, 456000, "1703500149.456");
@@ -379,6 +489,11 @@ TEST(CoreUtil, ParseTime)
WEE_PARSE_DATE(1, 1703500149, 456000, "1703500149,456");
WEE_PARSE_DATE(1, 1703500149, 456789, "1703500149,456789");
/* expected: 2023-12-25T10:29:09.456789Z == 1703500149.456789 */
/* with extra digits after microseconds */
WEE_PARSE_DATE(1, 1703500149, 456789, "1703500149.4567891");
WEE_PARSE_DATE(1, 1703500149, 456789, "1703500149.456789123456789123456789123456789");
setenv ("TZ", "", 1);
}