int last_day_of_month(std::tm cur) const {
if (cur.tm_mon + 1 > 11) {
cur.tm_mon = 0;
cur.tm_year++;
} else {
cur.tm_mon++;
}
cur.tm_mday = 1;
cur.tm_hour = 0;
cur.tm_min = 0;
add(cur, std::chrono::hours(-24));
return cur.tm_mday;
}
Clock::time_point cron_to_prev(const Clock::time_point from) const {
int loop_times = 0;
auto now = Clock::to_time_t(from);
std::tm prev(*std::localtime(&now));
std::tm old = prev;
prev.tm_sec = 0;
add(prev, std::chrono::minutes(-1)); // last min
bool change_mon = false;
bool change_day = false;
bool change_week = false;
bool change_hour = false;
while (true) {
if (loop_times++ > MAX_LOOP_TIMES) {
printf("!!! seems dead loop in cron_to_prev !!!\n");
return Clock::time_point(Clock::duration(0));
}
if (minute != -1 && prev.tm_min != minute) {
add(prev, std::chrono::minutes(-1));
continue;
}
if (hour != -1 && prev.tm_hour != hour) {
add(prev, std::chrono::hours(-1));
continue;
}
if (day != -1 && prev.tm_mday != day) {
add(prev, std::chrono::hours(-24));
continue;
}
if (day_of_week != -1 && prev.tm_wday != day_of_week) {
add(prev, std::chrono::hours(-24));
continue;
}
if (month != -1 && prev.tm_mon != month) {
if (prev.tm_mon == 0) {
prev.tm_mon = 11;
prev.tm_year--;
} else {
prev.tm_mon--;
}
add(prev, std::chrono::seconds(0)); // bug fix
continue;
}
break;
}
if (old.tm_hour != prev.tm_hour) {
change_hour = true;
}
if (old.tm_mday != prev.tm_mday) {
change_day = true;
}
if (old.tm_wday != prev.tm_wday) {
change_week = true;
}
if (old.tm_mon != prev.tm_mon) {
change_mon = true;
}
if (change_hour || change_day || change_week || change_mon) {
if (minute == -1
&& (hour != -1 || day != -1 || day_of_week != -1 || month != -1)) {
prev.tm_min = 59;
}
}
if (change_day || change_week || change_mon) {
if (hour == -1 && (day != -1 || day_of_week != -1 || month != -1)) {
prev.tm_hour = 23;
}
}
if (change_mon) {
if (day == -1 && month != -1) { // last day in this month
prev.tm_mday = last_day_of_month(prev);// [1,31]
}
}
if (change_week || change_mon) {
if (month != -1 && day_of_week != -1) { // last week-day in this month
prev.tm_mday = last_day_of_month(prev); // last day in this month
add(prev, std::chrono::seconds(0));
while(true) {
if (prev.tm_wday == day_of_week) {
break;
}
add(prev, std::chrono::hours(-24));
}
}
}
// telling mktime to figure out dst
prev.tm_isdst = -1;
return Clock::from_time_t(std::mktime(&prev));
}
// http://*.com/a/322058/1284550
Clock::time_point cron_to_next(const Clock::time_point) const {
int loop_times = 0;
auto now = Clock::to_time_t(from);
std::tm next(*std::localtime(&now));
// it will always at least run the next minute
next.tm_sec = 0;
add(next, std::chrono::minutes(1));
while (true) {
if (loop_times++ > MAX_LOOP_TIMES) {
printf("!!! seems dead loop in cron_to_next !!!\n");
return Clock::time_point(Clock::duration(0));
}
if (month != -1 && next.tm_mon != month) {
if (next.tm_mon + 1 > 11) {
next.tm_mon = 0;
next.tm_year++;
} else {
next.tm_mon++;
}
next.tm_mday = 1;
next.tm_hour = 0;
next.tm_min = 0;
add(next, std::chrono::seconds(0)); // bug fix
continue;
}
if (day != -1 && next.tm_mday != day) {
add(next, std::chrono::hours(24));
next.tm_hour = 0;
next.tm_min = 0;
continue;
}
if (day_of_week != -1 && next.tm_wday != day_of_week) {
add(next, std::chrono::hours(24));
next.tm_hour = 0;
next.tm_min = 0;
continue;
}
if (hour != -1 && next.tm_hour != hour) {
add(next, std::chrono::hours(1));
next.tm_min = 0;
continue;
}
if (minute != -1 && next.tm_min != minute) {
add(next, std::chrono::minutes(1));
continue;
}
break;
}
// telling mktime to figure out dst
next.tm_isdst = -1;
return Clock::from_time_t(std::mktime(&next));
}
Bosma::Cron cron("3 * 1 10 *");
auto fromTp = try_parse("2010-03-06 10:10:00", "%Y-%m-%d %H:%M:%S");