From 6fe67b354d3f1138972b2caadc3b71a3dd14de4d Mon Sep 17 00:00:00 2001 From: djcb Date: Sat, 17 Feb 2018 17:44:21 +0200 Subject: [PATCH] lib/parser: fix month days In the olden days, we stored dates like e.g. 20180131121234, and do a lexicographical check. With that, we could use e.g. upper-limits 201802312359 for "all dates in Feb 2018", even if Feb doesn't have 31 days. However, nowadays we use time_t values, and g_date_time_new_local raises errors for non-existent days; easiest fix is to massage things a bit; so let's do that. Fixes issue #1197. --- lib/parser/test-utils.cc | 3 +++ lib/parser/utils.cc | 41 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/parser/test-utils.cc b/lib/parser/test-utils.cc index 366eb33f..2c008595 100644 --- a/lib/parser/test-utils.cc +++ b/lib/parser/test-utils.cc @@ -62,6 +62,9 @@ test_date_basic () { "1972-12-14T09:10:23", true, "0093165023" }, { "1854-11-18T17:10:23", true, "0000000000" }, + { "2000-02-31T09:10:23", true, "0951861599" }, + { "2000-02-29T23:59:59", true, "0951861599" }, + { "2016", true, "1451599200" }, { "2016", false, "1483221599" }, diff --git a/lib/parser/utils.cc b/lib/parser/utils.cc index b33e2e3a..b8702a61 100644 --- a/lib/parser/utils.cc +++ b/lib/parser/utils.cc @@ -291,8 +291,40 @@ special_date (const std::string& d, bool is_first) return date_boundary (is_first); } -constexpr const char UserDateMin[] = "19700101000000"; -constexpr const char UserDateMax[] = "29991231235959"; +// if a date has a month day greater than the number of days in that month, +// change it to a valid date point to the last second in that month +static void +fixup_month (struct tm *tbuf) +{ + decltype(tbuf->tm_mday) max_days; + const auto month = tbuf->tm_mon + 1; + const auto year = tbuf->tm_year + 1900; + + switch (month) { + case 2: + if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + max_days = 29; + else + max_days = 28; + break; + case 4: + case 6: + case 9: + case 11: + max_days = 30; + break; + default: + max_days = 31; + break; + } + + if (tbuf->tm_mday > max_days) { + tbuf->tm_mday = max_days; + tbuf->tm_hour = 23; + tbuf->tm_min = 59; + tbuf->tm_sec = 59; + } +} std::string Mux::date_to_time_t_string (const std::string& dstr, bool is_first) @@ -310,6 +342,9 @@ Mux::date_to_time_t_string (const std::string& dstr, bool is_first) else if (dstr.find_first_of("ymdwhMs") != std::string::npos) return delta_ymwdhMs (dstr); + constexpr char UserDateMin[] = "19700101000000"; + constexpr char UserDateMax[] = "29991231235959"; + std::string date (is_first ? UserDateMin : UserDateMax); std::copy_if (dstr.begin(), dstr.end(), date.begin(),[](auto c){return isdigit(c);}); @@ -321,6 +356,8 @@ Mux::date_to_time_t_string (const std::string& dstr, bool is_first) !strptime (date.c_str(), "%Y", &tbuf)) return date_boundary (is_first); + fixup_month(&tbuf); + dtime = g_date_time_new_local (tbuf.tm_year + 1900, tbuf.tm_mon + 1, tbuf.tm_mday,