123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- #include "util/tc_cron.h"
- #include "util/tc_platform.h"
- namespace tars
- {
- const std::vector<std::string> TC_Cron::DAYS = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
- const std::vector<std::string> TC_Cron::MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
- TC_Cron TC_Cron::makecron(const string& expr)
- {
- TC_Cron cron;
- if (expr.empty())
- {
- throw TC_CronException("[TC_Cron makecron] invalid empty cron expression");
- }
- vector<string> fields = TC_Common::sepstr<string>(expr, " ");
- if (fields.size() != 6)
- throw TC_CronException("[TC_Cron makecron] cron expression must have six fields");
- setCronField(fields[0], cron.seconds, CRON_MIN_SECONDS, CRON_MAX_SECONDS);
- setCronField(fields[1], cron.minutes, CRON_MIN_MINUTES, CRON_MAX_MINUTES);
- setCronField(fields[2], cron.hours, CRON_MIN_HOURS, CRON_MAX_HOURS);
- setCronDaysOfWeek(fields[5], cron.days_of_week);
- setCronDaysOfMonth(fields[3], cron.days_of_month);
- setCronMonth(fields[4], cron.months);
- cron.isset = true;
- return cron;
- }
- std::time_t TC_Cron::nextcron(const TC_Cron& cron, std::time_t date)
- {
- std::tm val;
- std::tm* dt = time_to_tm(&date, &val);
- if (dt == nullptr) return INVALID_TIME;
- time_t original = tm_to_time(*dt);
- if (INVALID_TIME == original) return INVALID_TIME;
- if (!findNext(cron, *dt, dt->tm_year))
- return INVALID_TIME;
- time_t calculated = tm_to_time(*dt);
- if (INVALID_TIME == calculated) return calculated;
- if (calculated == original)
- {
- addToField(*dt, CronField::second, 1);
- if (!findNext(cron, *dt, dt->tm_year))
- return INVALID_TIME;
- }
- return tm_to_time(*dt);
- }
- std::time_t TC_Cron::nextcron(const TC_Cron& cron)
- {
- std::time_t now = std::time(0);
- return nextcron(cron, now);
- }
- std::tm* TC_Cron::time_to_tm(std::time_t const* date, std::tm* const out)
- {
- #if TARGET_PLATFORM_WINDOWS
- errno_t err = localtime_s(out, date);
- return 0 == err ? out : nullptr;
- #else
- return localtime_r(date, out);
- #endif
- }
- std::time_t TC_Cron::tm_to_time(std::tm& tmt)
- {
- return std::mktime(&tmt);
- }
- bool TC_Cron::contains(const std::string& text, char ch)
- {
- return std::string::npos != text.find_first_of(ch);
- }
- cron_int TC_Cron::to_cron_int(const std::string& text)
- {
- try
- {
- return static_cast<cron_int>(std::stoul(text.data()));
- }
- catch (std::exception const& ex)
- {
- throw TC_CronException(string("[TC_Cron to_cron_int] ") + ex.what());
- }
- }
- std::string TC_Cron::replaceOrdinals(std::string text, const std::vector<std::string> & replacement)
- {
- for (size_t i = 0; i < replacement.size(); ++i)
- {
- auto pos = text.find(replacement[i]);
- if (std::string::npos != pos)
- {
- text.replace(pos, 3, std::to_string(i));
- }
- }
- return text;
- }
- std::pair<cron_int, cron_int> TC_Cron::makeRange(std::string field, cron_int minval, cron_int maxval)
- {
- cron_int first = 0;
- cron_int last = 0;
- if (field.size() == 1 && field[0] == '*')
- {
- first = minval;
- last = maxval;
- }
- else if (!contains(field, '-'))
- {
- first = to_cron_int(field);
- last = first;
- }
- else
- {
- auto parts = TC_Common::sepstr<string>(field, "-");
- if (parts.size() != 2)
- throw TC_CronException("TC_Cron::makeRange] specified range requires two fields");
- first = to_cron_int(parts[0]);
- last = to_cron_int(parts[1]);
- }
- if (first > maxval || last > maxval)
- {
- throw TC_CronException("TC_Cron::makeRange] specified range exceeds maximum");
- }
- if (first < minval || last < minval)
- {
- throw TC_CronException("TC_Cron::makeRange] specified range is less than minimum");
- }
- if (first > last)
- {
- throw TC_CronException("TC_Cron::makeRange] specified range start exceeds range end");
- }
- return { first, last };
- }
- void TC_Cron::setCronDaysOfWeek(std::string value, std::bitset<7>& target)
- {
- auto days = TC_Common::upper(value);
- auto days_replaced = replaceOrdinals(days, DAYS);
- if (days_replaced.size() == 1 && days_replaced[0] == '?')
- days_replaced[0] = '*';
- setCronField(days_replaced,target,CRON_MIN_DAYS_OF_WEEK,CRON_MAX_DAYS_OF_WEEK);
- }
- void TC_Cron::setCronDaysOfMonth(std::string value, std::bitset<31>& target)
- {
- if (value.size() == 1 && value[0] == '?')
- value[0] = '*';
- setCronField(value, target, CRON_MIN_DAYS_OF_MONTH, CRON_MAX_DAYS_OF_MONTH);
- }
- void TC_Cron::setCronMonth(std::string value, std::bitset<12>& target)
- {
- auto month = TC_Common::upper(value);
- auto month_replaced = replaceOrdinals(month, MONTHS);
- setCronField(month_replaced, target, CRON_MIN_MONTHS, CRON_MAX_MONTHS);
- }
- void TC_Cron::addToField(std::tm& tmt, CronField field, int const val)
- {
- switch (field)
- {
- case CronField::second:
- tmt.tm_sec += val;
- break;
- case CronField::minute:
- tmt.tm_min += val;
- break;
- case CronField::hour_of_day:
- tmt.tm_hour += val;
- break;
- case CronField::day_of_week:
- case CronField::day_of_month:
- tmt.tm_mday += val;
- break;
- case CronField::month:
- tmt.tm_mon += val;
- break;
- case CronField::year:
- tmt.tm_year += val;
- break;
- }
- if (INVALID_TIME == tm_to_time(tmt))
- throw TC_CronException("TC_Cron::addToField] invalid time expression");
- }
- void TC_Cron::setField(std::tm& tmt, CronField field, int const val)
- {
- switch (field)
- {
- case CronField::second:
- tmt.tm_sec = val;
- break;
- case CronField::minute:
- tmt.tm_min = val;
- break;
- case CronField::hour_of_day:
- tmt.tm_hour = val;
- break;
- case CronField::day_of_week:
- tmt.tm_wday = val;
- break;
- case CronField::day_of_month:
- tmt.tm_mday = val;
- break;
- case CronField::month:
- tmt.tm_mon = val;
- break;
- case CronField::year:
- tmt.tm_year = val;
- break;
- }
- if (INVALID_TIME == tm_to_time(tmt))
- throw TC_CronException("TC_Cron::setField] invalid time expression");
- }
- void TC_Cron::resetField(std::tm& tmt, CronField field)
- {
- switch (field)
- {
- case CronField::second:
- tmt.tm_sec = 0;
- break;
- case CronField::minute:
- tmt.tm_min = 0;
- break;
- case CronField::hour_of_day:
- tmt.tm_hour = 0;
- break;
- case CronField::day_of_week:
- tmt.tm_wday = 0;
- break;
- case CronField::day_of_month:
- tmt.tm_mday = 1;
- break;
- case CronField::month:
- tmt.tm_mon = 0;
- break;
- case CronField::year:
- tmt.tm_year = 0;
- break;
- }
- if (INVALID_TIME == tm_to_time(tmt))
- throw TC_CronException("TC_Cron::resetField] invalid time expression");
- }
- void TC_Cron::resetAllFields(std::tm& tmt, const std::bitset<7> & marked_fields)
- {
- for (size_t i = 0; i < marked_fields.size(); ++i)
- {
- if (marked_fields.test(i))
- resetField(tmt, static_cast<CronField>(i));
- }
- }
- void TC_Cron::markField(std::bitset<7>& orders, CronField field)
- {
- if (!orders.test(static_cast<size_t>(field)))
- orders.set(static_cast<size_t>(field));
- }
- size_t TC_Cron::findNextDay(
- std::tm& tmt,
- std::bitset<31> const& days_of_month,
- size_t day_of_month,
- const std::bitset<7> & days_of_week,
- size_t day_of_week,
- const std::bitset<7> & marked_fields)
- {
- unsigned int count = 0;
- unsigned int maximum = 366;
- while (
- (!days_of_month.test(day_of_month - CRON_MIN_DAYS_OF_MONTH) ||
- !days_of_week.test(day_of_week - CRON_MIN_DAYS_OF_WEEK))
- && count++ < maximum)
- {
- addToField(tmt, CronField::day_of_month, 1);
- day_of_month = tmt.tm_mday;
- day_of_week = tmt.tm_wday;
- resetAllFields(tmt, marked_fields);
- }
- return day_of_month;
- }
- bool TC_Cron::findNext(const TC_Cron & cron, std::tm& tmt, size_t const dot)
- {
- bool res = true;
- std::bitset<7> marked_fields{ 0 };
- std::bitset<7> empty_list{ 0 };
- unsigned int second = tmt.tm_sec;
- auto updated_second = findNext(cron.seconds,tmt,CRON_MIN_SECONDS,CRON_MAX_SECONDS,second,CronField::second,CronField::minute,empty_list);
- if (second == updated_second)
- {
- markField(marked_fields, CronField::second);
- }
- unsigned int minute = tmt.tm_min;
- auto update_minute = findNext(cron.minutes,tmt,CRON_MIN_MINUTES,CRON_MAX_MINUTES,minute,CronField::minute,CronField::hour_of_day,marked_fields);
- if (minute == update_minute)
- {
- markField(marked_fields, CronField::minute);
- }
- else
- {
- res = findNext(cron, tmt, dot);
- if (!res) return res;
- }
- unsigned int hour = tmt.tm_hour;
- auto updated_hour = findNext(cron.hours,tmt,CRON_MIN_HOURS,CRON_MAX_HOURS,hour,CronField::hour_of_day,CronField::day_of_week,marked_fields);
- if (hour == updated_hour)
- {
- markField(marked_fields, CronField::hour_of_day);
- }
- else
- {
- res = findNext(cron, tmt, dot);
- if (!res) return res;
- }
- unsigned int day_of_week = tmt.tm_wday;
- unsigned int day_of_month = tmt.tm_mday;
- auto updated_day_of_month = findNextDay(tmt,cron.days_of_month,day_of_month,cron.days_of_week,day_of_week,marked_fields);
- if (day_of_month == updated_day_of_month)
- {
- markField(marked_fields, CronField::day_of_month);
- }
- else
- {
- res = findNext(cron, tmt, dot);
- if (!res) return res;
- }
- unsigned int month = tmt.tm_mon;
- auto updated_month = findNext(cron.months,tmt,CRON_MIN_MONTHS,CRON_MAX_MONTHS,month,
- CronField::month,CronField::year,marked_fields);
- if (month != updated_month)
- {
- if (tmt.tm_year - dot > CRON_MAX_YEARS_DIFF)
- return false;
- res = findNext(cron, tmt, dot);
- if (!res) return res;
- }
- return res;
- }
- }
|