Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion cpp/src/arrow/util/value_parsing.h
Original file line number Diff line number Diff line change
Expand Up @@ -829,11 +829,18 @@ static inline bool ParseTimestampStrptime(const char* buf, size_t length,
if (!allow_trailing_chars && static_cast<size_t>(ret - clean_copy.c_str()) != length) {
return false;
}
// tm_year + 1900 can overflow int (e.g. with %s), and date::year only
// holds 16 bits
const int64_t year = static_cast<int64_t>(result.tm_year) + 1900;
if (year < static_cast<int>(arrow_vendored::date::year::min()) ||
year > static_cast<int>(arrow_vendored::date::year::max())) {
return false;
}
// Reject impossible dates such as February 30 or a zero day, which strptime
// may accept (the day of month is checked against 31 at most), instead of
// rolling them over into the next month; consistent with the ISO-8601 parser.
const arrow_vendored::date::year_month_day ymd =
arrow_vendored::date::year(result.tm_year + 1900) / (result.tm_mon + 1) /
arrow_vendored::date::year(static_cast<int>(year)) / (result.tm_mon + 1) /
result.tm_mday;
if (!ymd.ok()) {
return false;
Expand Down
19 changes: 19 additions & 0 deletions cpp/src/arrow/util/value_parsing_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,25 @@ TEST(TimestampParser, StrptimeWithoutDay) {
ASSERT_EQ(expected, converted);
}

TEST(TimestampParser, StrptimeYearOutOfRange) {
auto parser = TimestampParser::MakeStrptime("%s");
int64_t converted = 0;
// A value past 2038 (2100-01-01), as a strptime that parses %s into a 32-bit
// time_t wraps larger values into ordinary dates
if (!(*parser)("4102444800", 10, TimeUnit::SECOND, &converted) ||
converted != 4102444800) {
GTEST_SKIP() << "strptime does not support 64-bit %s values on this platform";
}

// Years that do not fit in a date::year are rejected rather than wrapped,
// also where tm_year + 1900 would overflow int
for (const std::string value :
{"1000000000000000", "67768036000000000", "67768036191676799"}) {
SCOPED_TRACE(value);
ASSERT_FALSE((*parser)(value.c_str(), value.size(), TimeUnit::SECOND, &converted));
}
}

TEST(TimestampParser, StrptimeZoneOffset) {
if (!kStrptimeSupportsZone) {
GTEST_SKIP() << "strptime does not support %z on this platform";
Expand Down
Loading