Implement complex 1.x timeperiod definitions.

This commit is contained in:
Gunnar Beutner 2013-04-17 14:26:04 +02:00
parent 2e600a7810
commit 8a1d24042d
4 changed files with 273 additions and 21 deletions

View File

@ -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);

View File

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

View File

@ -33,32 +33,272 @@ using namespace icinga;
REGISTER_SCRIPTFUNCTION(LegacyTimePeriod, &LegacyTimePeriod::ScriptFunc);
bool LegacyTimePeriod::IsInDayDefinition(const String& daydef, tm *reference)
bool LegacyTimePeriod::IsInTimeRange(tm *begin, tm *end, int stride, tm *reference)
{
if (daydef == "sunday" || daydef == "monday" || daydef == "tuesday" ||
daydef == "wednesday" || daydef == "thursday" || daydef == "friday" ||
daydef == "saturday") {
int wday;
time_t tsbegin, tsend, tsref;
tsbegin = mktime(begin);
tsend = mktime(end);
tsref = mktime(reference);
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;
if (tsref < tsbegin || tsref > tsend)
return false;
return reference->tm_wday == wday;
int daynumber = (tsref - tsbegin) / (24 * 60 * 60);
if (daynumber % stride == 0)
return false;
return true;
}
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid day definition: " + daydef));
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<String> 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)
{
/* 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;
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)

View File

@ -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);