From eb65bb8a69cebc4c687bbecee6214f57bfa61db4 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Tue, 12 Apr 2022 14:22:40 +0200 Subject: [PATCH] These bugs are gonna take a while --- src/cron/cron.v | 17 +++++------------ src/cron/expression.v | 26 ++++++++++++++----------- src/cron/expression_test.v | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 src/cron/expression_test.v diff --git a/src/cron/cron.v b/src/cron/cron.v index cd32c448..931d1c8d 100644 --- a/src/cron/cron.v +++ b/src/cron/cron.v @@ -17,17 +17,10 @@ fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool { pub fn cron(conf Config) ? { mut queue := datatypes.MinHeap{} - for _ in 0..5000 { - minute := rand.int_in_range(0, 60) ? - hour := rand.int_in_range(0, 23) ? - ce := parse_expression('$minute $hour') ? + ce := parse_expression('0 3') ? + t := time.parse('2002-01-01 00:00:00') ? - t := ce.next_from_now() ? - // println(t) - queue.insert(t) - } - - for queue.len() > 0 { - println(queue.pop() ?) - } + println(t) + t2 := ce.next(t) ? + println(t2) } diff --git a/src/cron/expression.v b/src/cron/expression.v index d275a423..600e252b 100644 --- a/src/cron/expression.v +++ b/src/cron/expression.v @@ -2,8 +2,6 @@ module cron import time -const days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - struct CronExpression { minutes []int hours []int @@ -15,6 +13,13 @@ struct CronExpression { // always pick a moment in the future, even if ref matches completely up to the // minute. This function conciously does not take gap years into account. pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { + // If the given ref matches the next cron occurence up to the minute, it + // will return that value. Because we always want to return a value in the + // future, we artifically shift the ref 60 seconds to make sure we always + // match in the future. A shift of 60 seconds is enough because the cron + // expression does not allow for accuracy smaller than one minute. + sref := 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 @@ -29,25 +34,25 @@ pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { // 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. - for month_index < ce.months.len && ref.month > ce.months[month_index] { + for month_index < ce.months.len && sref.month > ce.months[month_index] { month_index++ } if month_index < ce.months.len { - for day_index < ce.days.len && ref.day > ce.days[day_index] { + for day_index < ce.days.len && sref.day > ce.days[day_index] { day_index++ } if day_index < ce.days.len { - for hour_index < ce.hours.len && ref.hour > ce.hours[hour_index] { + for hour_index < ce.hours.len && sref.hour > ce.hours[hour_index] { hour_index++ } if hour_index < ce.hours.len { // Minute is the only value where we explicitely make sure we - // can't match ref's value exactly. This is to ensure we only + // can't match sref's value exactly. This is to ensure we only // return values in the future. - for minute_index < ce.minutes.len && ref.minute >= ce.minutes[minute_index] { + for minute_index < ce.minutes.len && sref.minute >= ce.minutes[minute_index] { minute_index++ } } @@ -60,7 +65,6 @@ pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { if minute_index == ce.minutes.len && hour_index < ce.hours.len { hour_index += 1 } - if hour_index == ce.hours.len && day_index < ce.days.len { day_index += 1 } @@ -77,11 +81,11 @@ pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { // 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 day > cron.days_in_month[ce.months[month_index % ce.months.len] - 1] { + if day > time.month_days[ce.months[month_index % ce.months.len] - 1] { day = ce.days[0] month_index += 1 - for day > cron.days_in_month[ce.months[month_index & ce.months.len] - 1] { + for day > time.month_days[ce.months[month_index & ce.months.len] - 1] { month_index += 1 // If for whatever reason the day value ends up being something @@ -94,7 +98,7 @@ pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { } month := ce.months[month_index % ce.months.len] - mut year := ref.year + mut year := sref.year // If the month loops over, we need to increment the year. if month_index >= ce.months.len { diff --git a/src/cron/expression_test.v b/src/cron/expression_test.v new file mode 100644 index 00000000..bc489777 --- /dev/null +++ b/src/cron/expression_test.v @@ -0,0 +1,39 @@ +module cron + +import time { new_time, Time, parse } + +fn test_next_simple() ? { + ce := parse_expression('0 3') ? + t := parse('2002-01-01 00:00:00') ? + t2 := ce.next(t) ? + + assert t2.year == 2002 + assert t2.month == 1 + assert t2.day == 1 + assert t2.hour == 3 + assert t2.minute == 0 +} + +fn test_next_identical() ? { + ce := parse_expression('0 3') ? + t := parse('2002-01-01 03:00:00') ? + t2 := ce.next(t) ? + + assert t2.year == 2002 + assert t2.month == 1 + assert t2.day == 2 + assert t2.hour == 3 + assert t2.minute == 0 +} + +fn test_next_next_day() ? { + ce := parse_expression('0 3') ? + t := parse('2002-01-01 04:00:00') ? + t2 := ce.next(t) ? + + assert t2.year == 2002 + assert t2.month == 1 + assert t2.day == 2 + assert t2.hour == 3 + assert t2.minute == 0 +}