1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
#include <bglibs/sysdeps.h>
#include <bglibs/systime.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <bglibs/msg.h>
#include <bglibs/wrap.h>
#include <bglibs/envstr.h>
#include <bglibs/striter.h>
#include <bglibs/str.h>
#include "bcron.h"
static int days_in_month(int month, int year)
{
switch (month) {
case 1: /* February is special */
return ((year % 4) != 0) ? 28 :
((year % 100) != 0) ? 29 :
((year % 400) != 0) ? 28 :
29;
case 3: /* April */
case 5: /* June */
case 8: /* September */
case 10: /* November */
return 30;
default: /* All the others have 31 */
return 31;
}
}
static time_t fixup_hour(time_t t, struct tm* tm)
{
*tm = *(localtime(&t));
/* From 13..23: The hour underflowed */
if (tm->tm_hour > 12) {
t += (24 - tm->tm_hour) * 60 * 60;
return fixup_hour(t, tm);
}
/* From 0..12: The hour overflowed */
else if (tm->tm_hour > 0) {
t -= tm->tm_hour * 60 * 60;
tm->tm_hour = 0;
}
return t;
}
static void advance_tmmonth(struct tm* tm)
{
tm->tm_sec = tm->tm_min = tm->tm_hour = 0;
tm->tm_wday = (tm->tm_wday
+ days_in_month(tm->tm_mon, tm->tm_year)
- (tm->tm_mday - 1)) % 7;
tm->tm_mday = 1;
if (++tm->tm_mon >= 12) {
tm->tm_mon = 0;
++tm->tm_year;
}
}
static void advance_tmday(struct tm* tm)
{
tm->tm_sec = tm->tm_min = tm->tm_hour = 0;
tm->tm_wday = (tm->tm_wday + 1) % 7;
if (++tm->tm_mday > days_in_month(tm->tm_mon, tm->tm_year))
advance_tmmonth(tm);
}
static void advance_tmhour(struct tm* tm)
{
tm->tm_sec = tm->tm_min = 0;
if (++tm->tm_hour >= 24)
advance_tmday(tm);
}
static time_t advance_month(time_t t, struct tm* tm)
{
t += (days_in_month(tm->tm_mon, tm->tm_year) - (tm->tm_mday - 1)) * 24*60*60
- tm->tm_hour * 60*60
- tm->tm_min*60
- tm->tm_sec;
if (daylight)
t = fixup_hour(t, tm);
else
advance_tmmonth(tm);
return t;
}
static time_t advance_day(time_t t, struct tm* tm)
{
t += 24*60*60 - tm->tm_hour*60*60 - tm->tm_min*60 - tm->tm_sec;
if (daylight)
t = fixup_hour(t, tm);
else
advance_tmday(tm);
return t;
}
static time_t advance_hour(time_t t, struct tm* tm, int daily)
{
const int prevhour = tm->tm_hour;
const int wasisdst = tm->tm_isdst;
t += 60*60 - tm->tm_min * 60 - tm->tm_sec;
if (daylight) {
*tm = *(localtime(&t));
/* Run the skipped hour twice.
*
* case1 example: entering repeated hour
* before: 1081058400 2004-04-04 01:00:00 EST
* after: 1081062000 2004-04-04 03:00:00 EDT
* want: 1081062000 2004-04-04 02:00:00 EDT
*
* case2 example: exiting repeated hour
* before: 1081062000 2004-04-04 02:00:00 EDT
* after: 1081065600 2004-04-04 04:00:00 EDT
* want: 1081062000 2004-04-04 03:00:00 EDT
*/
if (tm->tm_hour > prevhour + 1) {
tm->tm_hour = prevhour + 1;
if (wasisdst)
t -= 60*60;
}
else if (daily && tm->tm_hour == prevhour)
return advance_hour(t, tm, 0);
}
else
advance_tmhour(tm);
return t;
}
static time_t advance_minute(time_t t, struct tm* tm, int daily)
{
t += 60 - tm->tm_sec;
tm->tm_sec = 0;
if (++tm->tm_min >= 60)
t = advance_hour(t, tm, daily);
return t;
}
/** Calculate the next time this job should get run.
The algorithm used here is very simple: start at the last time the job
was run (or the current time if it hasn't been run yet), and scan
forward effectively minute-by-minute until a time that matches the
specified times is found. The scan is intentionally limited to one
year, since no job can be specified more than that much in the future.
Obviously, scanning every minute for a year for every job is far too
much work, so the scan is optimized by advancing over larger intervals
if no job is possible during that interval. For example, if a job is
specified to run only on the 1st of the month, the loop advances whole
days at a time until the month day equals 1. This means the loop will
complete in a maximum of 113 steps (12-1 months + 31-1 days + 24-1 hours
+ 60-1 minutes). Also, the more frequently a job needs to be scheduled,
the fewer steps are required to calculate its next time.
My first attempt at this loop calculated exclusively using broken down
time. However, using that method, it was impossible to both (1) prevent
jobs from running twice on the duplicated DST hour and (2) still run
jobs that are supposed to run during the duplicated DST hour.
*/
time_t timespec_next(const struct job_timespec* ts,
time_t start, const struct tm* starttm)
{
time_t t;
struct tm tm;
time_t timelimit;
const int daily = ts->hour_count <= 1;
tm = *starttm;
t = start - tm.tm_sec;
tm.tm_sec = 0;
timelimit = start + 366*24*60*60 * 10;
do {
assert(tm.tm_sec == 0);
if ((ts->months & (1ULL << tm.tm_mon)) != 0)
if ((ts->mdays & (1ULL << tm.tm_mday)) != 0
&& (ts->wdays & (1ULL << tm.tm_wday)) != 0)
if ((ts->hours & (1ULL << tm.tm_hour)) != 0)
if ((ts->minutes & (1ULL << tm.tm_min)) != 0)
return t;
else
t = advance_minute(t, &tm, daily);
else {
t = advance_hour(t, &tm, daily);
}
else
t = advance_day(t, &tm);
else
t = advance_month(t, &tm);
} while (t < timelimit);
return INT_MAX;
}
void timespec_next_init(void)
{
/* This sets daylight, which is used in calculating times. */
tzset();
debug4(DEBUG_SCHED, "Timezone: ", tzname[0], " alt: ", tzname[1]);
}
|