LibCore: Support wildcard-skipping characters in Core::DateTime::parse

For example, LibJS will need to parse date strings of the form:

    Wed Dec 31 1969 19:00:00 GMT-0500 (Eastern Standard Time)

This string contains both the time zone offset (-05:00) and a display
name for the time zone (Eastern Standard Time). Because we will already
handle time zone adjustments when we handle the offsets, we will want to
just skip the time zone name. This patch will let us use a format string
of the form "GMT%z (%+)" to do so.
This commit is contained in:
Timothy Flynn 2023-11-07 09:52:37 -05:00 committed by Andreas Kling
parent 350fdf1e43
commit ee7ab5d053
2 changed files with 41 additions and 0 deletions

View file

@ -83,3 +83,26 @@ TEST_CASE(parse_time_zone_name)
test("%Y/%m/%d %R %Z"sv, "2023/01/23 10:50 Europe/Paris"sv, 2023, 01, 23, 17, 50);
test("%Y/%m/%d %R %Z"sv, "2023/01/23 10:50 Australia/Perth"sv, 2023, 01, 23, 10, 50);
}
TEST_CASE(parse_wildcard_characters)
{
EXPECT(!Core::DateTime::parse("%+"sv, ""sv).has_value());
EXPECT(!Core::DateTime::parse("foo%+"sv, "foo"sv).has_value());
EXPECT(!Core::DateTime::parse("[%*]"sv, "[foo"sv).has_value());
EXPECT(!Core::DateTime::parse("[%*]"sv, "foo]"sv).has_value());
EXPECT(!Core::DateTime::parse("%+%b"sv, "fooJan"sv).has_value());
auto test = [](auto format, auto time, u32 year, u32 month, u32 day) {
auto result = Core::DateTime::parse(format, time);
VERIFY(result.has_value());
EXPECT_EQ(year, result->year());
EXPECT_EQ(month, result->month());
EXPECT_EQ(day, result->day());
};
test("%Y %+ %m %d"sv, "2023 whf 01 23"sv, 2023, 01, 23);
test("%Y %m %d %+"sv, "2023 01 23 whf"sv, 2023, 01, 23);
test("%Y [%+] %m %d"sv, "2023 [well hello friends!] 01 23"sv, 2023, 01, 23);
test("%Y %m %d [%+]"sv, "2023 01 23 [well hello friends!]"sv, 2023, 01, 23);
}

View file

@ -512,6 +512,24 @@ Optional<DateTime> DateTime::parse(StringView format, StringView string)
tm_represents_utc_time = true;
break;
case '+': {
Optional<char> next_format_character;
if (format_pos + 1 < format.length()) {
next_format_character = format[format_pos + 1];
// Disallow another formatter directly after %+. This is to avoid ambiguity when parsing a string like
// "ignoreJan" with "%+%b", as it would be non-trivial to know that where the %b field begins.
if (next_format_character == '%')
return {};
}
auto discarded = string_lexer.consume_until([&](auto ch) { return ch == next_format_character; });
if (discarded.is_empty())
return {};
break;
}
case '%':
consume('%');
break;