//
// Copyright (c) 2014, Novant LLC
// Licensed under the MIT License
//
// History:
// 10 Sep 2014 Andy Frank Creation
//
**
** CronSchedule defines a schedule for when a CronJob should run.
**
@Serializable { simple=true }
abstract const class CronSchedule
{
//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////
** Return true if this schedule triggers for the given timestamp.
abstract Bool trigger(DateTime now, DateTime? last)
//////////////////////////////////////////////////////////////////////////
// Serializable
//////////////////////////////////////////////////////////////////////////
static new fromStr(Str str, Bool checked := true)
{
try
{
s := str.split(' ')
switch (s.first)
{
case "every": return parseEvery(s)
case "daily": return parseDaily(s)
case "weekly": return parseWeekly(s)
case "monthly": return parseMonthly(s)
default: throw Err()
}
}
catch (Err err)
{
if (checked) throw ParseErr("Invalid schedule str '$str'", err)
return null
}
}
private static EverySchedule parseEvery(Str[] s)
{
if (s.size != 2) throw Err()
return EverySchedule(Duration.fromStr(s[1]))
}
private static DailySchedule parseDaily(Str[] s)
{
if (s.size != 3) throw Err()
if (s[1] != "at") throw Err()
return DailySchedule(Time.fromStr(s[2] + ":00"))
}
private static WeeklySchedule parseWeekly(Str[] s)
{
if (s.size != 5) throw Err()
if (s[1] != "on") throw Err()
w := Weekday[,]
s[2].split(',').each |x| { w.add(Weekday.fromStr(x)) }
if (s[3] != "at") throw Err()
return WeeklySchedule(w.unique.sort, Time.fromStr(s[4] + ":00"))
}
private static MonthlySchedule parseMonthly(Str[] s)
{
if (s.size != 5) throw Err()
if (s[1] != "on") throw Err()
days := Int[,]
s[2].split(',').each |x|
{
d := x.toInt(10, false)
if (d == null || d < 1 || d > 31) throw Err()
days.add(d)
}
if (s[3] != "at") throw Err()
return MonthlySchedule(days.unique.sort, Time.fromStr(s[4] + ":00"))
}
}
**************************************************************************
** EverySchedule
**************************************************************************
internal const class EverySchedule : CronSchedule
{
new make(Duration dur) { this.dur=dur }
const Duration dur
override Bool trigger(DateTime now, DateTime? last)
{
last==null ? true : now - last >= dur
}
override Int hash() { toStr.hash }
override Bool equals(Obj? obj)
{
that := obj as EverySchedule
return that?.dur == this.dur
}
override Str toStr() { "every $dur" }
}
**************************************************************************
** DailySchedule
**************************************************************************
internal const class DailySchedule : CronSchedule
{
new make(Time time) { this.time=time }
const Time time
override Bool trigger(DateTime now, DateTime? last)
{
if (now.time >= time)
{
if (last == null) return true
if (now.date > last.date) return true
}
return false
}
override Int hash() { toStr.hash }
override Bool equals(Obj? obj)
{
that := obj as DailySchedule
return that?.time == this.time
}
override Str toStr() { "daily at " + time.toLocale("hh:mm") }
}
**************************************************************************
** WeeklySchedule
**************************************************************************
internal const class WeeklySchedule : CronSchedule
{
new make(Weekday[] weekdays, Time time)
{
this.weekdays = weekdays
this.time = time
}
const Weekday[] weekdays
const Time time
override Bool trigger(DateTime now, DateTime? last)
{
if (!weekdays.contains(now.date.weekday)) return false
if (now.time < time) return false
return last==null ? true : now.date > last.date
}
override Int hash() { toStr.hash }
override Bool equals(Obj? obj)
{
that := obj as WeeklySchedule
if (that == null) return false
return that.weekdays == this.weekdays && that.time == this.time
}
override Str toStr() { "weekly on " + weekdays.join(",") + " at " + time.toLocale("hh:mm") }
}
**************************************************************************
** MonthlySchedule
**************************************************************************
internal const class MonthlySchedule : CronSchedule
{
new make(Int[] days, Time time)
{
this.days = days
this.time = time
}
const Int[] days
const Time time
override Bool trigger(DateTime now, DateTime? last)
{
if (!days.contains(now.date.day)) return false
if (now.time < time) return false
return last==null ? true : now.date > last.date
}
override Int hash() { toStr.hash }
override Bool equals(Obj? obj)
{
that := obj as MonthlySchedule
if (that == null) return false
return that.days == this.days && that.time == this.time
}
override Str toStr() { "monthly on " + days.join(",") + " at " + time.toLocale("hh:mm") }
}