From d2c8ca4fc7f65956140bcf7d1a1e945d805fcfc8 Mon Sep 17 00:00:00 2001 From: Fulvio Date: Wed, 1 Feb 2023 17:02:53 +0100 Subject: [PATCH] Allow to import dates with up to 31 days, even for the wrong months. This is consistent with game_save which allows such dates for new games. --- gtest/test_pgnparser.cpp | 22 +++++++++++++------- src/date.h | 45 +++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/gtest/test_pgnparser.cpp b/gtest/test_pgnparser.cpp index 05221a71..1a9572be 100644 --- a/gtest/test_pgnparser.cpp +++ b/gtest/test_pgnparser.cpp @@ -250,10 +250,6 @@ TEST(Test_PgnParser, date_parsePGNTag) { test("2018.08.31", "2018.08.31"); test("2018.08.32", "2018.08.??"); test("2018.01.00", "2018.01.??"); - test("2018.01.32", "2018.01.??"); - test("2018.02.30", "2018.02.??"); - test("2018.04.31", "2018.04.??"); - test("2018.11.31", "2018.11.??"); test("2018.01aaa", "2018.01.??"); test("2018.01", "2018.01.??"); @@ -283,16 +279,28 @@ TEST(Test_PgnParser, date_parsePGNTag) { test("2000.02.28", "2000.02.28"); test("1000.02.28", "1000.02.28"); test("2020.02.29", "2020.02.29"); - test("2018.02.29", "2018.02.??"); test("2012.02.29", "2012.02.29"); test("2000.02.29", "2000.02.29"); - test("1900.02.29", "1900.02.??"); - test("1000.02.29", "1000.02.??"); + + // Accept incorrect days up to 31 + test("2018.01.32", "2018.01.??"); + test("2018.02.32", "2018.02.??"); + test("2018.04.31", "2018.04.31"); + test("2018.11.31", "2018.11.31"); + test("2018.02.31", "2018.02.31"); + test("2018.02.30", "2018.02.30"); + test("2018.02.29", "2018.02.29"); + test("1900.02.29", "1900.02.29"); + test("1000.02.29", "1000.02.29"); test("????.??.??", "????.??.??"); test("2023.??.??", "2023.??.??"); test("2023.01.??", "2023.01.??"); test("2023.??.30", "2023.??.30"); + + test("2023.10.2", "2023.10.02"); + test("2023.3.14", "2023.03.14"); + test("2023.1.2", "2023.01.02"); } TEST(Test_PgnParser, TagPairs) { diff --git a/src/date.h b/src/date.h index f6ff6fba..cf3a608c 100644 --- a/src/date.h +++ b/src/date.h @@ -167,45 +167,38 @@ date_EncodeFromString (const char * str) * @returns the dateT object corresponding to @e str. */ inline dateT date_parsePGNTag(const char* str, size_t len) { - auto to_int = [](unsigned char ch) { - return ch - static_cast('0'); - }; auto is_digit = [](auto v) { return v >= 0 && v <= 9; }; - if (len < 4) + if (len < 4 || len > 10) return {}; - int tmp[4]; - std::transform(str, str + 4, tmp, to_int); + int tmp[10]; + std::transform(str, str + len, tmp, [](unsigned char ch) { + return ch - static_cast('0'); + }); + std::fill(tmp + len, tmp + 10, -1); + uint32_t year = tmp[0] * 1000 + tmp[1] * 100 + tmp[2] * 10 + tmp[3]; if (!std::all_of(tmp, tmp + 4, is_digit) || year > YEAR_MAX) return {}; uint32_t month = 0; - if (len > 6) { - auto d1 = to_int(str[5]); - auto d2 = to_int(str[6]); - if (is_digit(d1) && is_digit(d2)) { - month = d1 * 10 + d2; - if (month > 12) - month = 0; + if (!is_digit(tmp[4]) && is_digit(tmp[5])) { + if (!is_digit(tmp[6])) { + // Accept the format YYYY.M.DD or YYYY.M.D + std::rotate(tmp + 5, tmp + 9, tmp + 10); + tmp[5] = 0; } + month = tmp[5] * 10 + tmp[6]; + if (month > 12) + month = 0; } uint32_t day = 0; - if (len > 9) { - auto d1 = to_int(str[8]); - auto d2 = to_int(str[9]); - if (is_digit(d1) && is_digit(d2)) { - day = d1 * 10 + d2; - constexpr unsigned char days[] = {31, 31, 28, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31}; - if (day > days[month]) { - if (day != 29 || year % 4 || (year % 100 == 0 && year % 400)) { - day = 0; - } - } - } + if (!is_digit(tmp[7]) && is_digit(tmp[8])) { + day = is_digit(tmp[9]) ? tmp[8] * 10 + tmp[9] : tmp[8]; + if (day > 31) + day = 0; } return (year << YEAR_SHIFT) | (month << MONTH_SHIFT) | (day << DAY_SHIFT);