These bugs are gonna take a while

main
Jef Roosens 2022-04-12 14:22:40 +02:00
parent cb22c1c9d3
commit eb65bb8a69
Signed by untrusted user: Jef Roosens
GPG Key ID: B75D4F293C7052DB
3 changed files with 59 additions and 23 deletions

View File

@ -17,17 +17,10 @@ fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool {
pub fn cron(conf Config) ? { pub fn cron(conf Config) ? {
mut queue := datatypes.MinHeap<time.Time>{} mut queue := datatypes.MinHeap<time.Time>{}
for _ in 0..5000 { ce := parse_expression('0 3') ?
minute := rand.int_in_range(0, 60) ? t := time.parse('2002-01-01 00:00:00') ?
hour := rand.int_in_range(0, 23) ?
ce := parse_expression('$minute $hour') ?
t := ce.next_from_now() ? println(t)
// println(t) t2 := ce.next(t) ?
queue.insert(t) println(t2)
}
for queue.len() > 0 {
println(queue.pop() ?)
}
} }

View File

@ -2,8 +2,6 @@ module cron
import time import time
const days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
struct CronExpression { struct CronExpression {
minutes []int minutes []int
hours []int hours []int
@ -15,6 +13,13 @@ struct CronExpression {
// always pick a moment in the future, even if ref matches completely up to the // 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. // minute. This function conciously does not take gap years into account.
pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { 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 // 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 // the length of their respective array in the CronExpression object, that
// means we've looped back around. This means that the "bigger" value has // 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. // 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 // For example, if we're going to a new day, the hour & minute will always
// be their smallest value again. // 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++ month_index++
} }
if month_index < ce.months.len { 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++ day_index++
} }
if day_index < ce.days.len { 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++ hour_index++
} }
if hour_index < ce.hours.len { if hour_index < ce.hours.len {
// Minute is the only value where we explicitely make sure we // 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. // 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++ 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 { if minute_index == ce.minutes.len && hour_index < ce.hours.len {
hour_index += 1 hour_index += 1
} }
if hour_index == ce.hours.len && day_index < ce.days.len { if hour_index == ce.hours.len && day_index < ce.days.len {
day_index += 1 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 // 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 // the smallest value & loop over to the next month that does have this
// day. // 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] day = ce.days[0]
month_index += 1 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 month_index += 1
// If for whatever reason the day value ends up being something // 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] 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 the month loops over, we need to increment the year.
if month_index >= ce.months.len { if month_index >= ce.months.len {

View File

@ -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
}