From 8a1d24042dc037aa96a44f212fddc92130ec9474 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 17 Apr 2013 14:26:04 +0200 Subject: [PATCH] Implement complex 1.x timeperiod definitions. --- lib/base/qstring.cpp | 5 + lib/base/qstring.h | 1 + lib/icinga/legacytimeperiod.cpp | 282 +++++++++++++++++++++++++++++--- lib/icinga/legacytimeperiod.h | 6 + 4 files changed, 273 insertions(+), 21 deletions(-) diff --git a/lib/base/qstring.cpp b/lib/base/qstring.cpp index 1ae6cff0d..c1ea8f3b4 100644 --- a/lib/base/qstring.cpp +++ b/lib/base/qstring.cpp @@ -122,6 +122,11 @@ size_t String::GetLength(void) const return m_Data.size(); } +size_t String::Find(const String& str, size_t pos) const +{ + return m_Data.find(str, pos); +} + size_t String::FindFirstOf(const char *s, size_t pos) const { return m_Data.find_first_of(s, pos); diff --git a/lib/base/qstring.h b/lib/base/qstring.h index 5c7628e9d..e085045c3 100644 --- a/lib/base/qstring.h +++ b/lib/base/qstring.h @@ -75,6 +75,7 @@ public: void Clear(void); size_t GetLength(void) const; + size_t Find(const String& str, size_t pos = 0) const; size_t FindFirstOf(const char *s, size_t pos = 0) const; size_t FindFirstOf(char ch, size_t pos = 0) const; String SubStr(size_t first, size_t len = NPos) const; diff --git a/lib/icinga/legacytimeperiod.cpp b/lib/icinga/legacytimeperiod.cpp index 8d9dae931..262c47321 100644 --- a/lib/icinga/legacytimeperiod.cpp +++ b/lib/icinga/legacytimeperiod.cpp @@ -33,32 +33,272 @@ using namespace icinga; REGISTER_SCRIPTFUNCTION(LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc); +bool LegacyTimePeriod::IsInTimeRange(tm *begin, tm *end, int stride, tm *reference) +{ + time_t tsbegin, tsend, tsref; + tsbegin = mktime(begin); + tsend = mktime(end); + tsref = mktime(reference); + + if (tsref < tsbegin || tsref > tsend) + return false; + + int daynumber = (tsref - tsbegin) / (24 * 60 * 60); + + if (daynumber % stride == 0) + return false; + + return true; +} + +void LegacyTimePeriod::FindNthWeekday(int wday, int n, tm *reference) +{ + int seen = 0; + + ASSERT(n > 0); + + reference->tm_mday = 1; + + for (;;) { + mktime(reference); + + if (reference->tm_wday == wday) { + seen++; + + if (seen == n) + return; + } + + reference->tm_mday++; + } +} + +int LegacyTimePeriod::WeekdayFromString(const String& daydef) +{ + if (daydef == "sunday") + return 0; + else if (daydef == "monday") + return 1; + else if (daydef == "tuesday") + return 2; + else if (daydef == "wednesday") + return 3; + else if (daydef == "thursday") + return 4; + else if (daydef == "friday") + return 5; + else if (daydef == "saturday") + return 6; + else + return -1; +} + +int LegacyTimePeriod::MonthFromString(const String& monthdef) +{ + if (monthdef == "january") + return 0; + else if (monthdef == "february") + return 1; + else if (monthdef == "march") + return 2; + else if (monthdef == "april") + return 3; + else if (monthdef == "may") + return 4; + else if (monthdef == "june") + return 5; + else if (monthdef == "july") + return 6; + else if (monthdef == "august") + return 7; + else if (monthdef == "september") + return 8; + else if (monthdef == "october") + return 9; + else if (monthdef == "november") + return 10; + else if (monthdef == "december") + return 11; + else + return -1; +} + +void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, tm *reference) +{ + /* YYYY-MM-DD */ + if (timespec.GetLength() == 10 && timespec[4] == '-' && timespec[7] == '-') { + int year = Convert::ToLong(timespec.SubStr(0, 4)); + int month = Convert::ToLong(timespec.SubStr(5, 2)); + int day = Convert::ToLong(timespec.SubStr(7, 2)); + + if (begin) { + begin->tm_year = year - 1900; + begin->tm_mon = month; + begin->tm_mday = day; + begin->tm_hour = 0; + begin->tm_min = 0; + begin->tm_sec = 0; + } + + if (end) { + end->tm_year = year - 1900; + end->tm_mon = month; + end->tm_mday = day; + end->tm_hour = 24; + end->tm_min = 0; + end->tm_sec = 0; + } + + return; + } + + std::vector tokens; + boost::algorithm::split(tokens, timespec, boost::is_any_of(" ")); + + if (tokens.size() > 1 && tokens[0] == "day") { + int mday = Convert::ToLong(tokens[1]); + + if (begin) { + *begin = *reference; + begin->tm_mday = mday; + begin->tm_hour = 0; + begin->tm_min = 0; + begin->tm_sec = 0; + + /* Negative days are relative to the next month. */ + if (mday < 0) { + end->tm_mday--; + begin->tm_mon++; + } + } + + if (end) { + *end = *reference; + end->tm_mday = mday; + end->tm_hour = 24; + end->tm_min = 0; + end->tm_sec = 0; + + /* Negative days are relative to the next month. */ + if (mday < 0) { + end->tm_mday--; + end->tm_mon++; + } + } + + return; + } + + int wday; + + if (tokens.size() > 1 && (wday = WeekdayFromString(tokens[0])) != -1) { + tm myref = *reference; + + if (tokens.size() > 2) { + int mon = MonthFromString(tokens[2]); + + if (mon == -1) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid month in time specification: " + timespec)); + + myref.tm_mon = mon; + } + + int n = Convert::ToLong(tokens[1]); + + if (begin) { + *begin = myref; + FindNthWeekday(wday, n, begin); + begin->tm_hour = 0; + begin->tm_min = 0; + begin->tm_sec = 0; + } + + if (end) { + *end = myref; + FindNthWeekday(wday, n, end); + end->tm_hour = 0; + end->tm_min = 0; + end->tm_sec = 0; + end->tm_mday++; + } + + return; + } + + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid time specification: " + timespec)); +} + +void LegacyTimePeriod::ParseTimeRange(const String& timerange, tm *begin, tm *end, int *stride, tm *reference) +{ + String def = timerange; + + /* Figure out the stride. */ + size_t pos = def.FindFirstOf('/'); + + if (pos != String::NPos) { + String strStride = def.SubStr(pos + 1); + strStride.Trim(); + *stride = Convert::ToLong(strStride); + + /* Remove the stride parameter from the definition. */ + def = def.SubStr(0, pos); + } else { + *stride = 1; /* User didn't specify anything, assume default. */ + } + + /* Figure out whether the user has specified two dates. */ + pos = def.Find("- "); + + std::cout << "XXX: " << def << std::endl; + + if (pos != String::NPos) { + String first = def.SubStr(0, pos); + first.Trim(); + + String second = def.SubStr(pos + 1); + second.Trim(); + + ParseTimeSpec(first, begin, NULL, reference); + + /* If the second definition starts with a number we need + * to add the first word from the first definition, e.g.: + * day 1 - 15 --> "day 15" */ + bool is_number = true; + size_t xpos = second.FindFirstOf(' '); + String fword = second.SubStr(0, xpos); + + try { + Convert::ToLong(fword); + } catch (...) { + is_number = false; + } + + if (is_number) { + xpos = first.FindFirstOf(' '); + ASSERT(xpos != String::NPos); + second = first.SubStr(0, xpos + 1) + second; + } + + ParseTimeSpec(second, NULL, end, reference); + } else { + ParseTimeSpec(def, begin, end, reference); + } +} + bool LegacyTimePeriod::IsInDayDefinition(const String& daydef, tm *reference) { - if (daydef == "sunday" || daydef == "monday" || daydef == "tuesday" || - daydef == "wednesday" || daydef == "thursday" || daydef == "friday" || - daydef == "saturday") { - int wday; - - if (daydef == "sunday") - wday = 0; - else if (daydef == "monday") - wday = 1; - else if (daydef == "tuesday") - wday = 2; - else if (daydef == "wednesday") - wday = 3; - else if (daydef == "thursday") - wday = 4; - else if (daydef == "friday") - wday = 5; - else if (daydef == "saturday") - wday = 6; + /* Week specifications are special in that they don't have a reference frame. */ + int wday = WeekdayFromString(daydef); + if (wday != -1) return reference->tm_wday == wday; - } - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid day definition: " + daydef)); + tm begin, end; + int stride; + + ParseTimeRange(daydef, &begin, &end, &stride, reference); + + return IsInTimeRange(&begin, &end, stride, reference); } Dictionary::Ptr LegacyTimePeriod::ProcessTimeRange(const String& timerange, tm *reference) diff --git a/lib/icinga/legacytimeperiod.h b/lib/icinga/legacytimeperiod.h index 818f45020..131961fa3 100644 --- a/lib/icinga/legacytimeperiod.h +++ b/lib/icinga/legacytimeperiod.h @@ -37,6 +37,12 @@ class I2_ICINGA_API LegacyTimePeriod public: static Array::Ptr ScriptFunc(const TimePeriod::Ptr& tp, double start, double end); + static bool IsInTimeRange(tm *begin, tm *end, int stride, tm *reference); + static void FindNthWeekday(int wday, int n, tm *reference); + static int WeekdayFromString(const String& daydef); + static int MonthFromString(const String& monthdef); + static void ParseTimeSpec(const String& timespec, tm *begin, tm *end, tm *reference); + static void ParseTimeRange(const String& timerange, tm *begin, tm *end, int *stride, tm *reference); static bool IsInDayDefinition(const String& daydef, tm *reference); static Dictionary::Ptr ProcessTimeRange(const String& timerange, tm *reference); static void ProcessTimeRanges(const String& timeranges, tm *reference, const Array::Ptr& result); -- 2.40.0