base64 encoder/decoder: Add parameter to control linefeed handling
[libi2ncommon] / src / cron.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  * @brief repeating time-points and intervals 
22  *
23  * @copyright Copyright © 2009 by Intra2net AG
24  *
25  */
26 #include <time.h>
27 #include <limits.h>
28 #include <stdexcept>
29 #include <iostream>
30
31 #include <cron.hpp>
32
33 namespace I2n {
34 namespace Time {
35
36 const time_t WeekCron::StNimmerleinsDay = static_cast<time_t>(INT_MAX);
37
38 /**
39     * Constructor, leaves object in invalid state
40     */
41 WeekCron::WeekCron()
42     : Begin(-1)
43     , End(0)
44     , Every(-1)
45     , Week()
46 {
47 }
48
49 /**
50     * Constructor
51     * @param daystring String representing the active weekdays as numbers. 0 is Sunday.
52     * @param begin Start point of time in seconds since the start of the day
53     */
54 WeekCron::WeekCron(const std::string& daystring, const int begin)
55     : Begin(begin)
56     , End(0)
57     , Every(-1)
58     , Week(daystring)
59 {
60 }
61
62 /**
63     * Constructor
64     * @param daystring String representing the active weekdays as numbers. 0 is Sunday.
65     * @param begin Start point of time in seconds since the start of the day
66     * @param end End point of time in seconds since the start of the day. Only used when every != -1
67     * @param every Repeat event every xxx seconds in the half-open interval of begin and end. -1 is disabled
68     */
69 WeekCron::WeekCron(const std::string& daystring, const int begin, const int end, const int every)
70     : Begin(begin)
71     , End(end)
72     , Every(every)
73     , Week(daystring)
74 {
75 }
76
77 /**
78     * Constructor
79     * @param week I2n::Time::Week object representing the active days
80     * @param begin Start point of time in seconds since the start of the day
81     */
82 WeekCron::WeekCron(const I2n::Time::Week& week, const int begin)
83     : Begin(begin)
84     , End(0)
85     , Every(-1)
86     , Week(week)
87 {
88 }
89
90 /**
91     * Constructor
92     * @param week I2n::Time::Week object representing the active days
93     * @param begin Start point of time in seconds since the start of the day
94     * @param end End point of time in seconds since the start of the day. Only used when every != -1
95     * @param every Repeat event every xxx seconds in the half-open interval of begin and end. -1 is disabled
96     */
97 WeekCron::WeekCron(const I2n::Time::Week& week, const int begin, const int end, const int every)
98     : Begin(begin)
99     , End(end)
100     , Every(every)
101     , Week(week)
102 {
103 }
104
105 /**
106  * Checks if the input values are sane
107  * @return True if sane, false otherweise
108  */
109 bool WeekCron::is_sane() const
110 {
111     if (Begin < 0 || Begin > 86399)
112         return false;
113
114     if (Every != -1)
115     {
116         if (End < 0 || End > 86400 ||
117             Every < 1 || Every > 86400 ||
118             Begin > End)
119             return false;
120     }
121
122     return true;
123 }
124
125 /**
126  * Returns the next point in time the item is scheduled for.
127  * Handles the full possibilities of WeekCron.
128  * @note if it is scheduled for calc_from we return the next schedule, not now!
129  * @param calc_from unix-time to start calculating from (0 means now)
130  * @return Next point in time the item is scheduled for
131  *         returns constant #StNimmerleinsDay if it is scheduled never
132  */
133 time_t WeekCron::get_next_run(time_t calc_from) const
134 {
135     if (!calc_from)
136         calc_from=time(NULL);
137
138     if (!is_sane())
139         throw std::runtime_error("illegal cron value");
140
141     if (calc_from <= 86400*14)
142         throw std::runtime_error("WeekCron doesn't work for timestamps near 0");
143
144     if (Week.none_set())
145         return StNimmerleinsDay;
146
147     if (Every == -1)
148     {
149         // point in time
150         return get_next_point(calc_from,Begin,true);
151     }
152     else
153     {
154         // interval
155         time_t next_begin = get_next_point(calc_from,Begin,true);
156         if (next_begin > get_next_point(calc_from,End-1,true))
157         {
158             // next begin > next end means we are at the begin or within the interval
159
160             time_t interval_begin=get_previousnow_point(calc_from,Begin,true);
161
162             time_t within_interval=calc_from - interval_begin;
163             time_t since_lastrun=within_interval % Every;
164             time_t next_exec=calc_from+(Every-since_lastrun);
165
166             // next step at or after end?
167             if (next_exec > get_next_point(calc_from,End-1,true))
168                 return get_next_point(calc_from,Begin,true);
169             else
170                 return next_exec;
171         }
172         else
173         {
174             // next begin < next end means we are out of the interval: next begin is next run
175             return next_begin;
176         }
177     }
178 }
179
180 /**
181  * Returns the next point in time the item is scheduled for. Does not care about intervals.
182  * Returns next point if scheduled for @a calc_from.
183  * @param calc_from Point of time to start calculations from
184  * @param daysec Start point of time in seconds since the start of the day
185  * @param todaycheck If true we check if the calculated time point is before
186                                   our @a calc_from calcucation start point.
187                                   If yes, we will advance to the next day.
188  * @return Next point in time
189  */
190 time_t WeekCron::get_next_point(const time_t calc_from, const int daysec, const bool todaycheck) const
191 {
192     struct tm ft;
193     if (localtime_r(&calc_from,&ft) == NULL)
194         return calc_from;
195
196     // take care of the weekday
197     ft.tm_mday+=Week.days_till_set(static_cast<Week::WeekDay>(ft.tm_wday));                     //lint !e737 !e713
198
199     fill_tm_with_wallclock(&ft,daysec);
200
201     // tm_isdst means to use the dst in use at the given time
202     ft.tm_isdst=-1;
203
204     time_t target=mktime(&ft);
205
206     // check for completely illegal time, should not happen without bug in this func,
207     // return calc_from as safeguard
208     if (target == (time_t)-1)
209         return calc_from;
210
211     // todays schedule could already been through or now
212     if (todaycheck && target <= calc_from)
213     {
214         // not today but the next matching weekday
215         ft.tm_mday++;
216         ft.tm_isdst=-1;
217         target=get_next_point(mktime(&ft),daysec,false);
218     }
219
220     return target;
221 }
222
223 /**
224  * Returns the previous or current point in time the item was scheduled for.
225  * Does not care about intervals.
226  * Returns @a calc_from if scheduled for @a calc_from.
227  * @param calc_from Point of time to start calculations from
228  * @param daysec Start point of time in seconds since the start of the day
229  * @param todaycheck If true we check if the calculated time point is after
230                                   our @a calc_from calcucation start point.
231                                   If yes, we will go back to the previos day
232  * @return Previous point in time
233  */
234 time_t WeekCron::get_previousnow_point(const time_t calc_from, const int daysec, const bool todaycheck) const
235 {
236     struct tm ft;
237     if (localtime_r(&calc_from,&ft) == NULL)
238         return calc_from;
239
240     // take care of the weekday
241     ft.tm_mday-=Week.days_since_set(static_cast<Week::WeekDay>(ft.tm_wday));                            //lint !e737 !e713
242
243     fill_tm_with_wallclock(&ft,daysec);
244
245     // tm_isdst means to use the dst in use at the given time
246     ft.tm_isdst=-1;
247
248     time_t target=mktime(&ft);
249
250     // check for completely illegal time, should not happen without bug in this func,
251     // return calc_from as safeguard
252     if (target == (time_t)-1)
253         return calc_from;
254
255     // target later than we are looking for
256     // target==calc_from is ok (that's why it is called lastnow...)
257     if (todaycheck && target > calc_from)
258     {
259         // not today but the previous matching weekday
260         ft.tm_mday--;
261         ft.tm_isdst=-1;
262         target=get_previousnow_point(mktime(&ft),daysec,false);
263     }
264
265     return target;
266 }
267
268 /**
269  * Converts @a daysec into hour/minute/second and fills it into the provided struct tm
270  * @param ft struct tm to fill wallclock time into
271  * @param daysec Start point of time in seconds since the start of the day
272  */
273 void WeekCron::fill_tm_with_wallclock(struct tm *ft, const int daysec) const
274 {
275     int remain=daysec;
276
277     ft->tm_hour=remain/3600;
278     remain-=ft->tm_hour*3600;
279
280     ft->tm_min=remain/60;
281     remain-=ft->tm_min*60;
282
283     ft->tm_sec=remain;
284 }
285
286 }
287 }