/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * @brief repeating time-points and intervals * * @copyright Copyright © 2009 by Intra2net AG * */ #include #include #include #include #include namespace I2n { namespace Time { const time_t WeekCron::StNimmerleinsDay = static_cast(INT_MAX); /** * Constructor, leaves object in invalid state */ WeekCron::WeekCron() : Begin(-1) , End(0) , Every(-1) , Week() { } /** * Constructor * @param daystring String representing the active weekdays as numbers. 0 is Sunday. * @param begin Start point of time in seconds since the start of the day */ WeekCron::WeekCron(const std::string& daystring, const int begin) : Begin(begin) , End(0) , Every(-1) , Week(daystring) { } /** * Constructor * @param daystring String representing the active weekdays as numbers. 0 is Sunday. * @param begin Start point of time in seconds since the start of the day * @param end End point of time in seconds since the start of the day. Only used when every != -1 * @param every Repeat event every xxx seconds in the half-open interval of begin and end. -1 is disabled */ WeekCron::WeekCron(const std::string& daystring, const int begin, const int end, const int every) : Begin(begin) , End(end) , Every(every) , Week(daystring) { } /** * Constructor * @param week I2n::Time::Week object representing the active days * @param begin Start point of time in seconds since the start of the day */ WeekCron::WeekCron(const I2n::Time::Week& week, const int begin) : Begin(begin) , End(0) , Every(-1) , Week(week) { } /** * Constructor * @param week I2n::Time::Week object representing the active days * @param begin Start point of time in seconds since the start of the day * @param end End point of time in seconds since the start of the day. Only used when every != -1 * @param every Repeat event every xxx seconds in the half-open interval of begin and end. -1 is disabled */ WeekCron::WeekCron(const I2n::Time::Week& week, const int begin, const int end, const int every) : Begin(begin) , End(end) , Every(every) , Week(week) { } /** * Checks if the input values are sane * @return True if sane, false otherweise */ bool WeekCron::is_sane() const { if (Begin < 0 || Begin > 86399) return false; if (Every != -1) { if (End < 0 || End > 86400 || Every < 1 || Every > 86400 || Begin > End) return false; } return true; } /** * Returns the next point in time the item is scheduled for. * Handles the full possibilities of WeekCron. * @note if it is scheduled for calc_from we return the next schedule, not now! * @param calc_from unix-time to start calculating from (0 means now) * @return Next point in time the item is scheduled for * returns constant #StNimmerleinsDay if it is scheduled never */ time_t WeekCron::get_next_run(time_t calc_from) const { if (!calc_from) calc_from=time(NULL); if (!is_sane()) throw std::runtime_error("illegal cron value"); if (calc_from <= 86400*14) throw std::runtime_error("WeekCron doesn't work for timestamps near 0"); if (Week.none_set()) return StNimmerleinsDay; if (Every == -1) { // point in time return get_next_point(calc_from,Begin,true); } else { // interval time_t next_begin = get_next_point(calc_from,Begin,true); if (next_begin > get_next_point(calc_from,End-1,true)) { // next begin > next end means we are at the begin or within the interval time_t interval_begin=get_previousnow_point(calc_from,Begin,true); time_t within_interval=calc_from - interval_begin; time_t since_lastrun=within_interval % Every; time_t next_exec=calc_from+(Every-since_lastrun); // next step at or after end? if (next_exec > get_next_point(calc_from,End-1,true)) return get_next_point(calc_from,Begin,true); else return next_exec; } else { // next begin < next end means we are out of the interval: next begin is next run return next_begin; } } } /** * Returns the next point in time the item is scheduled for. Does not care about intervals. * Returns next point if scheduled for @a calc_from. * @param calc_from Point of time to start calculations from * @param daysec Start point of time in seconds since the start of the day * @param todaycheck If true we check if the calculated time point is before our @a calc_from calcucation start point. If yes, we will advance to the next day. * @return Next point in time */ time_t WeekCron::get_next_point(const time_t calc_from, const int daysec, const bool todaycheck) const { struct tm ft; if (localtime_r(&calc_from,&ft) == NULL) return calc_from; // take care of the weekday ft.tm_mday+=Week.days_till_set(static_cast(ft.tm_wday)); //lint !e737 !e713 fill_tm_with_wallclock(&ft,daysec); // tm_isdst means to use the dst in use at the given time ft.tm_isdst=-1; time_t target=mktime(&ft); // check for completely illegal time, should not happen without bug in this func, // return calc_from as safeguard if (target == (time_t)-1) return calc_from; // todays schedule could already been through or now if (todaycheck && target <= calc_from) { // not today but the next matching weekday ft.tm_mday++; ft.tm_isdst=-1; target=get_next_point(mktime(&ft),daysec,false); } return target; } /** * Returns the previous or current point in time the item was scheduled for. * Does not care about intervals. * Returns @a calc_from if scheduled for @a calc_from. * @param calc_from Point of time to start calculations from * @param daysec Start point of time in seconds since the start of the day * @param todaycheck If true we check if the calculated time point is after our @a calc_from calcucation start point. If yes, we will go back to the previos day * @return Previous point in time */ time_t WeekCron::get_previousnow_point(const time_t calc_from, const int daysec, const bool todaycheck) const { struct tm ft; if (localtime_r(&calc_from,&ft) == NULL) return calc_from; // take care of the weekday ft.tm_mday-=Week.days_since_set(static_cast(ft.tm_wday)); //lint !e737 !e713 fill_tm_with_wallclock(&ft,daysec); // tm_isdst means to use the dst in use at the given time ft.tm_isdst=-1; time_t target=mktime(&ft); // check for completely illegal time, should not happen without bug in this func, // return calc_from as safeguard if (target == (time_t)-1) return calc_from; // target later than we are looking for // target==calc_from is ok (that's why it is called lastnow...) if (todaycheck && target > calc_from) { // not today but the previous matching weekday ft.tm_mday--; ft.tm_isdst=-1; target=get_previousnow_point(mktime(&ft),daysec,false); } return target; } /** * Converts @a daysec into hour/minute/second and fills it into the provided struct tm * @param ft struct tm to fill wallclock time into * @param daysec Start point of time in seconds since the start of the day */ void WeekCron::fill_tm_with_wallclock(struct tm *ft, const int daysec) const { int remain=daysec; ft->tm_hour=remain/3600; remain-=ft->tm_hour*3600; ft->tm_min=remain/60; remain-=ft->tm_min*60; ft->tm_sec=remain; } } }