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.
This commit is contained in:
djcb 2018-02-17 17:44:21 +02:00
parent 15ba4699ab
commit 6fe67b354d
2 changed files with 42 additions and 2 deletions

View File

@ -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" },

View File

@ -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,