forked from vieter-v/vieter
				
			Alpha version cron 'next' function
							parent
							
								
									e3da3d0d7f
								
							
						
					
					
						commit
						135b6c3d7f
					
				|  | @ -27,6 +27,29 @@ pub fn cron(conf Config) ? { | ||||||
| 	// } | 	// } | ||||||
| 
 | 
 | ||||||
| 	// println(queue) | 	// println(queue) | ||||||
| 	exp := '10/2 5 *' | 	// exp := '10/2 5 *' | ||||||
| 	println(parse_expression(exp) ?) | 	// println(parse_expression(exp) ?) | ||||||
|  | 	ce := parse_expression('0 3 */2') ? | ||||||
|  | 	println(ce) | ||||||
|  | 	// ce := CronExpression{ | ||||||
|  | 	// 	minutes: [0] | ||||||
|  | 	// 	hours: [3] | ||||||
|  | 	// 	days: [1, 2, 3, 4, 5, 6] | ||||||
|  | 	// 	months: [1, 2] | ||||||
|  | 	// } | ||||||
|  | 	mut t := time.Time{ | ||||||
|  | 		year: 2022 | ||||||
|  | 		month: 2 | ||||||
|  | 		minute: 9 | ||||||
|  | 		hour: 13 | ||||||
|  | 		day: 12 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// mut t := time.now() | ||||||
|  | 	println(t) | ||||||
|  | 
 | ||||||
|  | 	for _ in 1..25 { | ||||||
|  | 		t = ce.next(t) ? | ||||||
|  | 		println(t) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,50 +3,117 @@ module cron | ||||||
| import math | import math | ||||||
| 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 []u32 | 	minutes []int | ||||||
| 	hours   []u32 | 	hours   []int | ||||||
| 	days    []u32 | 	days    []int | ||||||
|  | 	months  []int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // next calculates the earliest time this cron expression is valid. | // next calculates the earliest time this cron expression is valid. | ||||||
| pub fn (ce &CronExpression) next(ref &time.Time) time.Time { | pub fn (ce &CronExpression) next(ref time.Time) ?time.Time { | ||||||
| 	res := time.Time{} | 	mut minute_index := 0 | ||||||
|  | 	mut hour_index := 0 | ||||||
|  | 	mut day_index := 0 | ||||||
|  | 	mut month_index := 0 | ||||||
| 
 | 
 | ||||||
| 	mut day := 0 | 	for month_index < ce.months.len && ref.month > ce.months[month_index] { | ||||||
| 	mut hour := 0 | 		month_index++ | ||||||
| 	mut minute := 0 | 	} | ||||||
| 
 | 
 | ||||||
| 	// Find the next minute | 	if month_index < ce.months.len { | ||||||
| 	// If ref.minute is greater than  | 		for day_index < ce.days.len && ref.day > ce.days[day_index] { | ||||||
| 	if ref.minute >= ce.minutes[ce.minutes.len - 1] || ref.minute < ce.minutes[0] { | 			day_index++ | ||||||
| 		minute = ce.minutes[0] | 		} | ||||||
| 	}else{ | 
 | ||||||
| 		for i in 0..ce.minutes.len { | 		if day_index < ce.days.len { | ||||||
| 			if ce.minutes[i] > ref.minute { | 			for hour_index < ce.hours.len && ref.hour > ce.hours[hour_index] { | ||||||
| 				minute = ce.minutes[i] | 				hour_index++ | ||||||
| 				break | 			} | ||||||
|  | 
 | ||||||
|  | 			if hour_index < ce.hours.len { | ||||||
|  | 				// For each unit, we calculate what the next value is | ||||||
|  | 				for minute_index < ce.minutes.len && ref.minute >= ce.minutes[minute_index] { | ||||||
|  | 					minute_index++ | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return res | 
 | ||||||
|  | 	// Sometime we have to shift values one more | ||||||
|  | 	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 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if day_index == ce.days.len && month_index < ce.months.len { | ||||||
|  | 		month_index += 1 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mut minute := ce.minutes[minute_index % ce.minutes.len] | ||||||
|  | 	mut hour := ce.hours[hour_index % ce.hours.len] | ||||||
|  | 	mut day := ce.days[day_index % ce.days.len] | ||||||
|  | 
 | ||||||
|  | 	mut reset := false | ||||||
|  | 
 | ||||||
|  | 	// If the day can't be planned in the current month, we go to the next one | ||||||
|  | 	// and go back to day one | ||||||
|  | 	if day > days_in_month[ce.months[month_index % ce.months.len] - 1] { | ||||||
|  | 		month_index += 1 | ||||||
|  | 		day = ce.days[0] | ||||||
|  | 
 | ||||||
|  | 		// Make sure we only plan in a month that the day occurs in | ||||||
|  | 		for day > days_in_month[ce.months[month_index & ce.months.len] - 1] { | ||||||
|  | 			month_index += 1 | ||||||
|  | 
 | ||||||
|  | 			// Prevent scenario where there are no months that can be scheduled. | ||||||
|  | 			if month_index == 2 * ce.months.len { | ||||||
|  | 				return error('No schedulable moment.') | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	month := ce.months[month_index % ce.months.len] | ||||||
|  | 	mut year := ref.year | ||||||
|  | 
 | ||||||
|  | 	if month_index >= ce.months.len { | ||||||
|  | 		year++ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return time.Time{ | ||||||
|  | 		year: year | ||||||
|  | 		month: month | ||||||
|  | 		day: day | ||||||
|  | 		minute: minute | ||||||
|  | 		hour: hour | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (ce &CronExpression) next_from_now() ?time.Time { | ||||||
|  | 	return ce.next(time.now()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // parse_range parses a given string into a range of sorted integers, if | // parse_range parses a given string into a range of sorted integers, if | ||||||
| // possible. | // possible. | ||||||
| fn parse_range(s string, min u32, max u32) ?[]u32 { | fn parse_range(s string, min int, max int) ?[]int { | ||||||
| 	mut out := []u32{} | 	mut out := []int{} | ||||||
| 	mut start := min | 	mut start := min | ||||||
| 	mut interval := u32(1) | 	mut interval := 1 | ||||||
| 
 | 
 | ||||||
| 	if s != '*' { | 	if s != '*' { | ||||||
| 		exps := s.split('/') | 		exps := s.split('/') | ||||||
| 
 | 
 | ||||||
| 		start = math.min(max, math.max(exps[0].u32(), min)) | 		start = math.min(max, math.max(exps[0].int(), min)) | ||||||
| 
 | 
 | ||||||
| 		if exps.len > 1 { | 		if exps.len > 1 { | ||||||
| 			interval = exps[1].u32() | 			interval = exps[1].int() | ||||||
| 		} | 		} | ||||||
| 		// Here, s solely consists of a number, so that's the only value we | 		// Here, s solely consists of a number, so that's the only value we | ||||||
| 		// should return. | 		// should return. | ||||||
|  | @ -69,15 +136,22 @@ fn parse_range(s string, min u32, max u32) ?[]u32 { | ||||||
| 
 | 
 | ||||||
| // min hour day month day-of-week | // min hour day month day-of-week | ||||||
| fn parse_expression(exp string) ?CronExpression { | fn parse_expression(exp string) ?CronExpression { | ||||||
| 	parts := exp.split(' ') | 	mut parts := exp.split(' ') | ||||||
| 
 | 
 | ||||||
| 	if parts.len != 3 { | 	if parts.len < 2 || parts.len > 4 { | ||||||
| 		return error('Expression must contain 5 space-separated parts.') | 		return error('Expression must contain between 2 and 4 space-separated parts.') | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// For ease of use, we allow the user to only specify as many parts as they | ||||||
|  | 	// need. | ||||||
|  | 	for parts.len < 4 { | ||||||
|  | 		parts << '*' | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return CronExpression{ | 	return CronExpression{ | ||||||
| 		minutes: parse_range(parts[0], 0, 59) ? | 		minutes: parse_range(parts[0], 0, 59) ? | ||||||
| 		hours: parse_range(parts[1], 0, 23) ? | 		hours: parse_range(parts[1], 0, 23) ? | ||||||
| 		days: parse_range(parts[2], 0, 31) ? | 		days: parse_range(parts[2], 1, 31) ? | ||||||
|  | 		months: parse_range(parts[3], 1, 12) ? | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,27 +2,27 @@ module cron | ||||||
| 
 | 
 | ||||||
| // =====parse_range===== | // =====parse_range===== | ||||||
| fn test_parse_star_range() ? { | fn test_parse_star_range() ? { | ||||||
| 	assert parse_range('*', 0, 5) ? == [u32(0), 1, 2, 3, 4, 5] | 	assert parse_range('*', 0, 5) ? == [0, 1, 2, 3, 4, 5] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_number() ? { | fn test_parse_number() ? { | ||||||
| 	assert parse_range('4', 0, 5) ? == [u32(4)] | 	assert parse_range('4', 0, 5) ? == [4] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_number_too_large() ? { | fn test_parse_number_too_large() ? { | ||||||
| 	assert parse_range('10', 0, 6) ? == [u32(6)] | 	assert parse_range('10', 0, 6) ? == [6] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_number_too_small() ? { | fn test_parse_number_too_small() ? { | ||||||
| 	assert parse_range('0', 2, 6) ? == [u32(2)] | 	assert parse_range('0', 2, 6) ? == [2] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_star() ? { | fn test_parse_step_star() ? { | ||||||
| 	assert parse_range('*/4', 0, 20) ? == [u32(0), 4, 8, 12, 16, 20] | 	assert parse_range('*/4', 0, 20) ? == [0, 4, 8, 12, 16, 20] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_star_too_large() ? { | fn test_parse_step_star_too_large() ? { | ||||||
| 	assert parse_range('*/21', 0, 20) ? == [u32(0)] | 	assert parse_range('*/21', 0, 20) ? == [0] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_zero() ? { | fn test_parse_step_zero() ? { | ||||||
|  | @ -30,15 +30,15 @@ fn test_parse_step_zero() ? { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_number() ? { | fn test_parse_step_number() ? { | ||||||
| 	assert parse_range('5/4', 0, 20) ? == [u32(5), 9, 13, 17] | 	assert parse_range('5/4', 0, 20) ? == [5, 9, 13, 17] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_number_too_large() ? { | fn test_parse_step_number_too_large() ? { | ||||||
| 	assert parse_range('10/4', 0, 5) ? == [u32(5)] | 	assert parse_range('10/4', 0, 5) ? == [5] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn test_parse_step_number_too_small() ? { | fn test_parse_step_number_too_small() ? { | ||||||
| 	assert parse_range('2/4', 5, 10) ? == [u32(5), 9] | 	assert parse_range('2/4', 5, 10) ? == [5, 9] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue