Skip to content

Commit

Permalink
Allow to import dates with up to 31 days, even for the wrong months.
Browse files Browse the repository at this point in the history
This is consistent with game_save which allows such dates for new games.
  • Loading branch information
benini committed Feb 1, 2023
1 parent afbd82f commit d2c8ca4
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 33 deletions.
22 changes: 15 additions & 7 deletions gtest/test_pgnparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.??");

Expand Down Expand Up @@ -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) {
Expand Down
45 changes: 19 additions & 26 deletions src/date.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned char>('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<unsigned char>('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);
Expand Down

0 comments on commit d2c8ca4

Please sign in to comment.