parsedate: bsearch the time zones

There are 69 entries, bsearch is faster than linear search for random
access.

This now also makes the matching case sensitive (zone names always in
uppercase). No docs said otherwise and all tests assumed uppercase.

Extended test 517

Closes #21266
This commit is contained in:
Daniel Stenberg 2026-04-08 10:30:36 +02:00
parent 5d84aa41a0
commit 283db12e05
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
2 changed files with 84 additions and 84 deletions

View File

@ -25,6 +25,7 @@
#include "parsedate.h"
#include "curlx/strparse.h"
#include "curlx/strcopy.h"
/*
A brief summary of the date string formats this parser groks:
@ -108,89 +109,83 @@ static const char * const weekday[] = {
struct tzinfo {
char name[5];
int offset; /* +/- in minutes */
int16_t offset; /* +/- in minutes */
};
/* Here's a bunch of frequently used time zone names. These were supported
by the old getdate parser. */
#define tDAYZONE (-60) /* offset for daylight savings time */
/* alpha-sorted list of time zones */
static const struct tzinfo tz[] = {
{ "GMT", 0 }, /* Greenwich Mean */
{ "UT", 0 }, /* Universal Time */
{ "UTC", 0 }, /* Universal (Coordinated) */
{ "WET", 0 }, /* Western European */
{ "BST", 0 + tDAYZONE }, /* British Summer */
{ "WAT", 60 }, /* West Africa */
{ "AST", 240 }, /* Atlantic Standard */
{ "A", -1 * 60 }, /* Alpha */
{ "ADT", 240 + tDAYZONE }, /* Atlantic Daylight */
{ "EST", 300 }, /* Eastern Standard */
{ "EDT", 300 + tDAYZONE }, /* Eastern Daylight */
{ "CST", 360 }, /* Central Standard */
{ "CDT", 360 + tDAYZONE }, /* Central Daylight */
{ "MST", 420 }, /* Mountain Standard */
{ "MDT", 420 + tDAYZONE }, /* Mountain Daylight */
{ "PST", 480 }, /* Pacific Standard */
{ "PDT", 480 + tDAYZONE }, /* Pacific Daylight */
{ "YST", 540 }, /* Yukon Standard */
{ "YDT", 540 + tDAYZONE }, /* Yukon Daylight */
{ "HST", 600 }, /* Hawaii Standard */
{ "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */
{ "CAT", 600 }, /* Central Alaska */
{ "AHST", 600 }, /* Alaska-Hawaii Standard */
{ "NT", 660 }, /* Nome */ /* spellchecker:disable-line */
{ "IDLW", 720 }, /* International Date Line West */
{ "AST", 240 }, /* Atlantic Standard */
{ "B", -2 * 60 }, /* Bravo */
{ "BST", 0 + tDAYZONE }, /* British Summer */
{ "C", -3 * 60 }, /* Charlie */
{ "CAT", 600 }, /* Central Alaska */
{ "CCT", -480 }, /* China Coast, USSR Zone 7 */
{ "CDT", 360 + tDAYZONE }, /* Central Daylight */
{ "CEST", -60 + tDAYZONE }, /* Central European Summer */
{ "CET", -60 }, /* Central European */
{ "CST", 360 }, /* Central Standard */
{ "D", -4 * 60 }, /* Delta */
{ "E", -5 * 60 }, /* Echo */
{ "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */
{ "EAST", -600 }, /* Eastern Australian Standard */
{ "EDT", 300 + tDAYZONE }, /* Eastern Daylight */
{ "EET", -120 }, /* Eastern Europe, USSR Zone 1 */
{ "EST", 300 }, /* Eastern Standard */
{ "F", -6 * 60 }, /* Foxtrot */
{ "FST", -60 + tDAYZONE }, /* French Summer */
{ "FWT", -60 }, /* French Winter */
{ "G", -7 * 60 }, /* Golf */
{ "GMT", 0 }, /* Greenwich Mean */
{ "GST", -600 }, /* Guam Standard, USSR Zone 9 */
{ "H", -8 * 60 }, /* Hotel */
{ "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */
{ "HST", 600 }, /* Hawaii Standard */
{ "I", -9 * 60 }, /* India */
{ "IDLE", -720 }, /* International Date Line East */
{ "IDLW", 720 }, /* International Date Line West */
{ "JST", -540 }, /* Japan Standard, USSR Zone 8 */
{ "K", -10 * 60 }, /* Kilo */
{ "L", -11 * 60 }, /* Lima */
{ "M", -12 * 60 }, /* Mike */
{ "MDT", 420 + tDAYZONE }, /* Mountain Daylight */
{ "MEST", -60 + tDAYZONE }, /* Middle European Summer */
{ "MESZ", -60 + tDAYZONE }, /* Middle European Summer */
{ "MET", -60 }, /* Middle European */
{ "MEWT", -60 }, /* Middle European Winter */
{ "MEST", -60 + tDAYZONE }, /* Middle European Summer */
{ "CEST", -60 + tDAYZONE }, /* Central European Summer */
{ "MESZ", -60 + tDAYZONE }, /* Middle European Summer */
{ "FWT", -60 }, /* French Winter */
{ "FST", -60 + tDAYZONE }, /* French Summer */
{ "EET", -120 }, /* Eastern Europe, USSR Zone 1 */
{ "MST", 420 }, /* Mountain Standard */
{ "N", 60 }, /* November */
{ "NT", 660 }, /* Nome */ /* spellchecker:disable-line */
{ "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */
{ "NZST", -720 }, /* New Zealand Standard */
{ "NZT", -720 }, /* New Zealand */
{ "O", 2 * 60 }, /* Oscar */
{ "P", 3 * 60 }, /* Papa */
{ "PDT", 480 + tDAYZONE }, /* Pacific Daylight */
{ "PST", 480 }, /* Pacific Standard */
{ "Q", 4 * 60 }, /* Quebec */
{ "R", 5 * 60 }, /* Romeo */
{ "S", 6 * 60 }, /* Sierra */
{ "T", 7 * 60 }, /* Tango */
{ "U", 8 * 60 }, /* Uniform */
{ "UT", 0 }, /* Universal Time */
{ "UTC", 0 }, /* Universal (Coordinated) */
{ "V", 9 * 60 }, /* Victor */
{ "W", 10 * 60 }, /* Whiskey */
{ "WADT", -420 + tDAYZONE }, /* West Australian Daylight */
{ "WAST", -420 }, /* spellchecker:disable-line */
/* West Australian Standard */
{ "WADT", -420 + tDAYZONE }, /* West Australian Daylight */
{ "CCT", -480 }, /* China Coast, USSR Zone 7 */
{ "JST", -540 }, /* Japan Standard, USSR Zone 8 */
{ "EAST", -600 }, /* Eastern Australian Standard */
{ "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */
{ "GST", -600 }, /* Guam Standard, USSR Zone 9 */
{ "NZT", -720 }, /* New Zealand */
{ "NZST", -720 }, /* New Zealand Standard */
{ "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */
{ "IDLE", -720 }, /* International Date Line East */
/* Next up: Military timezone names. RFC822 allowed these, but (as noted in
RFC 1123) had their signs wrong. Here we use the correct signs to match
actual military usage.
*/
{ "A", -1 * 60 }, /* Alpha */
{ "B", -2 * 60 }, /* Bravo */
{ "C", -3 * 60 }, /* Charlie */
{ "D", -4 * 60 }, /* Delta */
{ "E", -5 * 60 }, /* Echo */
{ "F", -6 * 60 }, /* Foxtrot */
{ "G", -7 * 60 }, /* Golf */
{ "H", -8 * 60 }, /* Hotel */
{ "I", -9 * 60 }, /* India */
/* "J", Juliet is not used as a timezone, to indicate the observer's local
time */
{ "K", -10 * 60 }, /* Kilo */
{ "L", -11 * 60 }, /* Lima */
{ "M", -12 * 60 }, /* Mike */
{ "N", 1 * 60 }, /* November */
{ "O", 2 * 60 }, /* Oscar */
{ "P", 3 * 60 }, /* Papa */
{ "Q", 4 * 60 }, /* Quebec */
{ "R", 5 * 60 }, /* Romeo */
{ "S", 6 * 60 }, /* Sierra */
{ "T", 7 * 60 }, /* Tango */
{ "U", 8 * 60 }, /* Uniform */
{ "V", 9 * 60 }, /* Victor */
{ "W", 10 * 60 }, /* Whiskey */
{ "X", 11 * 60 }, /* X-ray */
{ "Y", 12 * 60 }, /* Yankee */
{ "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */
{ "WAT", 60 }, /* West Africa */
{ "WET", 0 }, /* Western European */
{ "X", 11 * 60 }, /* X-ray */
{ "Y", 12 * 60 }, /* Yankee */
{ "YDT", 540 + tDAYZONE }, /* Yukon Daylight */
{ "YST", 540 }, /* Yukon Standard */
{ "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */
};
/* returns:
@ -233,22 +228,24 @@ static int checkmonth(const char *check, size_t len)
return -1; /* return the offset or -1, no real offset is -1 */
}
/* return the time zone offset between GMT and the input one, in number
of seconds or -1 if the timezone was not found/legal */
static int tzcompare(const void *m1, const void *m2)
{
const struct tzinfo *tz1 = m1;
const struct tzinfo *tz2 = m2;
return strcmp(tz1->name, tz2->name);
}
/* return the time zone offset between GMT and the input one, in number of
seconds or -1 if the timezone was not found/legal */
static int checktz(const char *check, size_t len)
{
unsigned int i;
const struct tzinfo *what = tz;
if(len > 4) /* longer than any valid timezone */
return -1;
for(i = 0; i < CURL_ARRAYSIZE(tz); i++) {
size_t ilen = strlen(what->name);
if((ilen == len) &&
curl_strnequal(check, what->name, len))
if(len <= 4) {
const struct tzinfo *what;
struct tzinfo find;
curlx_strcopy(find.name, sizeof(find.name), check, len);
what = bsearch(&find, tz, CURL_ARRAYSIZE(tz), sizeof(tz[0]), tzcompare);
if(what)
return what->offset * 60;
what++;
}
return -1;
}

View File

@ -52,6 +52,9 @@ static CURLcode test_lib517(const char *URL)
{ "1994.Nov.6", 784080000 },
{ "Sun/Nov/6/94/GMT", 784080000 },
{ "Sun, 06 Nov 1994 08:49:37 CET", 784108177 },
{ "Sun, 06 Nov 1994 08:49:37 cet", -1 }, /* lowercase time zone */
{ "Sun, 06 Nov 1994 08:49:37 utc", -1 }, /* lowercase time zone */
{ "Sun, 06 Nov 1994 08:49:37 gmt", -1 }, /* lowercase time zone */
{ "06 Nov 1994 08:49:37 EST", 784129777 },
{ "Sun, 06 Nov 1994 08:49:37 UT", 784111777 },
{ "Sun, 12 Sep 2004 15:05:58 -0700", 1095026758 },