#include "vieter_cron.h" #include const uint8_t month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; vieter_cron_expression *vieter_cron_expression_init() { return malloc(sizeof(vieter_cron_expression)); } void ce_free(vieter_cron_expression *ce) { free(ce->months); free(ce->days); free(ce->hours); free(ce->minutes); free(ce); } void vieter_cron_next(vieter_cron_simple_time *out, vieter_cron_expression *ce, vieter_cron_simple_time *ref) { // For all of these values, the rule is the following: if their value is // the length of their respective array in the CronExpression object, that // means we've looped back around. This means that the "bigger" value has // to be incremented by one. For example, if the minutes have looped // around, that means that the hour has to be incremented as well. uint8_t month_index = 0; uint8_t day_index = 0; uint8_t hour_index = 0; uint8_t minute_index = 0; // This chain is the same logic multiple times, namely that if a "bigger" // value loops around, then the smaller value will always reset as well. // For example, if we're going to a new day, the hour & minute will always // be their smallest value again. while (month_index < ce->month_count && ref->month > ce->months[month_index]) { month_index++; } if (month_index < ce->month_count && ref->month == ce->months[month_index]) { while (day_index < ce->day_count && ref->day > ce->days[day_index]) { day_index++; } if (day_index < ce->day_count && ref->day == ce->days[day_index]) { while (hour_index < ce->hour_count && ref->hour > ce->hours[hour_index]) { hour_index++; } if (hour_index < ce->hour_count && ref->hour == ce->hours[hour_index]) { // Minute is the only value where we explicitely make sure we // can't match sref's value exactly. This is to ensure we only // return values in the future. while (minute_index < ce->minute_count && ref->minute >= ce->minutes[minute_index]) { minute_index++; } } } } // Here, we increment the "bigger" values by one if the smaller ones loop // around. The order is important, as it allows a sort-of waterfall effect // to occur which updates all values if required. if (minute_index == ce->minute_count && hour_index < ce->hour_count) { hour_index++; } if (hour_index == ce->hour_count && day_index < ce->day_count) { day_index++; } if (day_index == ce->day_count && month_index < ce->month_count) { month_index++; } out->minute = ce->minutes[minute_index % ce->minute_count]; out->hour = ce->hours[hour_index % ce->hour_count]; out->day = ce->days[day_index % ce->day_count]; // Sometimes, we end up with a day that does not exist within the selected // month, e.g. day 30 in February. When this occurs, we reset day back to // the smallest value & loop over to the next month that does have this // day. if (out->day > month_days[ce->months[month_index % ce->month_count] - 1]) { out->day = ce->days[0]; month_index++; while (out->day > month_days[ce->months[month_index % ce->month_count] - 1]) { month_index++; } } out->month = ce->months[month_index % ce->month_count]; if (month_index >= ce->month_count) { out->year = ref->year + 1; } else { out->year = ref->year; } } void vieter_cron_next_from_now(vieter_cron_simple_time *out, vieter_cron_expression *ce) { time_t t = time(NULL); struct tm gm; gmtime_r(&t, &gm); vieter_cron_simple_time ref = {// tm_year contains years since 1900 .year = 1900 + gm.tm_year, // tm_mon goes from 0 to 11 .month = gm.tm_mon + 1, .day = gm.tm_mday, .hour = gm.tm_hour, .minute = gm.tm_min}; vieter_cron_next(out, ce, &ref); }