Commit | Line | Data |
---|---|---|
0e23f538 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
1720acee | 4 | |
0e23f538 TJ |
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 | */ | |
1720acee GE |
20 | /** @file |
21 | * @brief repeating time-points and intervals | |
22 | * | |
23 | * @copyright Copyright © 2009 by Intra2net AG | |
1720acee GE |
24 | * |
25 | */ | |
26 | #include <time.h> | |
55661be5 | 27 | #include <limits.h> |
1720acee | 28 | #include <stdexcept> |
300d1c09 | 29 | #include <iostream> |
1720acee GE |
30 | |
31 | #include <cron.hpp> | |
32 | ||
0c7e72d7 TJ |
33 | namespace I2n { |
34 | namespace Time { | |
fd6d9c59 | 35 | |
55661be5 | 36 | const time_t WeekCron::StNimmerleinsDay = static_cast<time_t>(INT_MAX); |
3d73a872 | 37 | |
882ecfd7 | 38 | /** |
ab205aa3 GE |
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 | /** | |
249bd63f TJ |
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 | */ | |
fd6d9c59 | 54 | WeekCron::WeekCron(const std::string& daystring, const int begin) |
249bd63f TJ |
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 | */ | |
fd6d9c59 | 69 | WeekCron::WeekCron(const std::string& daystring, const int begin, const int end, const int every) |
249bd63f TJ |
70 | : Begin(begin) |
71 | , End(end) | |
72 | , Every(every) | |
73 | , Week(daystring) | |
74 | { | |
75 | } | |
76 | ||
ab205aa3 GE |
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 | } | |
249bd63f TJ |
104 | |
105 | /** | |
106 | * Checks if the input values are sane | |
107 | * @return True if sane, false otherweise | |
108 | */ | |
5774a519 | 109 | bool WeekCron::is_sane() const |
1720acee | 110 | { |
5774a519 | 111 | if (Begin < 0 || Begin > 86399) |
1720acee GE |
112 | return false; |
113 | ||
5774a519 | 114 | if (Every != -1) |
1720acee | 115 | { |
5774a519 TJ |
116 | if (End < 0 || End > 86400 || |
117 | Every < 1 || Every > 86400 || | |
118 | Begin > End) | |
1720acee GE |
119 | return false; |
120 | } | |
121 | ||
122 | return true; | |
123 | } | |
124 | ||
882ecfd7 | 125 | /** |
3d73a872 TJ |
126 | * Returns the next point in time the item is scheduled for. |
127 | * Handles the full possibilities of WeekCron. | |
249bd63f TJ |
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 | |
3d73a872 | 131 | * returns constant #StNimmerleinsDay if it is scheduled never |
249bd63f | 132 | */ |
dfbe7ca9 | 133 | time_t WeekCron::get_next_run(time_t calc_from) const |
1720acee GE |
134 | { |
135 | if (!calc_from) | |
136 | calc_from=time(NULL); | |
137 | ||
138 | if (!is_sane()) | |
139 | throw std::runtime_error("illegal cron value"); | |
140 | ||
8de7c371 | 141 | if (calc_from <= 86400*14) |
fd6d9c59 | 142 | throw std::runtime_error("WeekCron doesn't work for timestamps near 0"); |
1720acee | 143 | |
5774a519 | 144 | if (Week.none_set()) |
3d73a872 | 145 | return StNimmerleinsDay; |
1720acee | 146 | |
5774a519 | 147 | if (Every == -1) |
1720acee GE |
148 | { |
149 | // point in time | |
5774a519 | 150 | return get_next_point(calc_from,Begin,true); |
1720acee GE |
151 | } |
152 | else | |
153 | { | |
154 | // interval | |
fd6d9c59 TJ |
155 | time_t next_begin = get_next_point(calc_from,Begin,true); |
156 | if (next_begin > get_next_point(calc_from,End-1,true)) | |
882ecfd7 GE |
157 | { |
158 | // next begin > next end means we are at the begin or within the interval | |
159 | ||
d70ccb7a | 160 | time_t interval_begin=get_previousnow_point(calc_from,Begin,true); |
882ecfd7 GE |
161 | |
162 | time_t within_interval=calc_from - interval_begin; | |
5774a519 TJ |
163 | time_t since_lastrun=within_interval % Every; |
164 | time_t next_exec=calc_from+(Every-since_lastrun); | |
882ecfd7 | 165 | |
8de7c371 | 166 | // next step at or after end? |
78b3ba9d | 167 | if (next_exec > get_next_point(calc_from,End-1,true)) |
5774a519 | 168 | return get_next_point(calc_from,Begin,true); |
882ecfd7 GE |
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 | |
fd6d9c59 | 175 | return next_begin; |
882ecfd7 | 176 | } |
1720acee GE |
177 | } |
178 | } | |
179 | ||
249bd63f | 180 | /** |
3d73a872 | 181 | * Returns the next point in time the item is scheduled for. Does not care about intervals. |
d70ccb7a | 182 | * Returns next point if scheduled for #calc_from. |
249bd63f TJ |
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 #calc_from calcucation start point. | |
187 | If yes, we will advance to the next day. | |
188 | * @return Next point in time | |
189 | */ | |
dfbe7ca9 | 190 | time_t WeekCron::get_next_point(const time_t calc_from, const int daysec, const bool todaycheck) const |
1720acee GE |
191 | { |
192 | struct tm ft; | |
dfbe7ca9 TJ |
193 | if (localtime_r(&calc_from,&ft) == NULL) |
194 | return calc_from; | |
1720acee GE |
195 | |
196 | // take care of the weekday | |
dfbe7ca9 | 197 | ft.tm_mday+=Week.days_till_set(static_cast<Week::WeekDay>(ft.tm_wday)); //lint !e737 !e713 |
1720acee | 198 | |
300d1c09 GE |
199 | fill_tm_with_wallclock(&ft,daysec); |
200 | ||
f60e4880 GE |
201 | // tm_isdst means to use the dst in use at the given time |
202 | ft.tm_isdst=-1; | |
1720acee | 203 | |
300d1c09 GE |
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 | |
dfbe7ca9 TJ |
208 | if (target == (time_t)-1) |
209 | return calc_from; | |
1720acee | 210 | |
882ecfd7 GE |
211 | // todays schedule could already been through or now |
212 | if (todaycheck && target <= calc_from) | |
1720acee | 213 | { |
882ecfd7 | 214 | // not today but the next matching weekday |
1720acee | 215 | ft.tm_mday++; |
f60e4880 GE |
216 | ft.tm_isdst=-1; |
217 | target=get_next_point(mktime(&ft),daysec,false); | |
1720acee GE |
218 | } |
219 | ||
220 | return target; | |
221 | } | |
222 | ||
249bd63f | 223 | /** |
d70ccb7a | 224 | * Returns the previous or current point in time the item was scheduled for. |
3d73a872 | 225 | * Does not care about intervals. |
d70ccb7a | 226 | * Returns #calc_from if scheduled for #calc_from. |
249bd63f TJ |
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 | |
d70ccb7a | 229 | * @param todaycheck If true we check if the calculated time point is after |
249bd63f | 230 | our #calc_from calcucation start point. |
d70ccb7a TJ |
231 | If yes, we will go back to the previos day |
232 | * @return Previous point in time | |
249bd63f | 233 | */ |
dfbe7ca9 | 234 | time_t WeekCron::get_previousnow_point(const time_t calc_from, const int daysec, const bool todaycheck) const |
882ecfd7 GE |
235 | { |
236 | struct tm ft; | |
dfbe7ca9 TJ |
237 | if (localtime_r(&calc_from,&ft) == NULL) |
238 | return calc_from; | |
882ecfd7 GE |
239 | |
240 | // take care of the weekday | |
dfbe7ca9 | 241 | ft.tm_mday-=Week.days_since_set(static_cast<Week::WeekDay>(ft.tm_wday)); //lint !e737 !e713 |
882ecfd7 | 242 | |
300d1c09 GE |
243 | fill_tm_with_wallclock(&ft,daysec); |
244 | ||
245 | // tm_isdst means to use the dst in use at the given time | |
f60e4880 | 246 | ft.tm_isdst=-1; |
882ecfd7 | 247 | |
300d1c09 GE |
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 | |
dfbe7ca9 TJ |
252 | if (target == (time_t)-1) |
253 | return calc_from; | |
882ecfd7 GE |
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 | { | |
f60e4880 | 259 | // not today but the previous matching weekday |
882ecfd7 | 260 | ft.tm_mday--; |
f60e4880 GE |
261 | ft.tm_isdst=-1; |
262 | target=get_previousnow_point(mktime(&ft),daysec,false); | |
882ecfd7 GE |
263 | } |
264 | ||
265 | return target; | |
266 | } | |
fd6d9c59 | 267 | |
300d1c09 GE |
268 | /** |
269 | * Converts #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 | ||
0c7e72d7 TJ |
286 | } |
287 | } |