using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml.Serialization; using System.Runtime.Serialization; using System.Diagnostics; namespace sharpknife.Data { public enum RecurrenceType { Minute = 2, Day = 5, Week = 10, Month = 15, } public static class ScheduleRuleHelper { static public RecurrenceType GetRecurrenceTypeOfRule(ISchedulationRule rule) { RecurrenceType res = RecurrenceType.Day; if (rule is WeekRule) { res = RecurrenceType.Week; } else if (rule is MonthRule) { res = RecurrenceType.Month; } else if (rule is DayRule) { res = RecurrenceType.Day; } else if (rule is MinuteRule) { res = RecurrenceType.Minute; } else { throw new Exception("SchedulationRules: RecurenceType unknown"); } return res; } } [XmlInclude(typeof(MinuteRule))] [XmlInclude(typeof(DayRule))] [XmlInclude(typeof(WeekRule))] [XmlInclude(typeof(MonthRule))] [DataContract] public abstract class ISchedulationRule { [XmlAttribute] [DataMember] public int RecurEvery { get; set; } public abstract DateTime CalculateFirstRun(SchedulationRules rules, DateTime startFrom); public abstract DateTime CalculateNextRun(SchedulationRules rules, DateTime lastRun); public RecurrenceType RecurrenceType { get {return ScheduleRuleHelper.GetRecurrenceTypeOfRule(this);} } public abstract bool CheckValues(); } [DataContract] public class MinuteRule : ISchedulationRule { public MinuteRule() { this.RecurEvery = 1; } public MinuteRule(int everyMinute) { this.RecurEvery = everyMinute; } public override DateTime CalculateFirstRun(SchedulationRules rules, DateTime startFrom) { DateTime virtualLastRun = (startFrom.Date < DateTime.Now.Date ? DateTime.Now : startFrom); return virtualLastRun; } public override DateTime CalculateNextRun(SchedulationRules rules, DateTime lastRun) { if (RecurEvery <= 0) RecurEvery = 1; DateTime res = new DateTime(lastRun.Year, lastRun.Month, lastRun.Day, lastRun.Hour, lastRun.Minute, 0); res = res.AddMinutes(RecurEvery); return res; } public override bool CheckValues() { this.RecurEvery = Math.Max(1, this.RecurEvery); return true; } } [DataContract] public class DayRule : ISchedulationRule { public DayRule() { this.RecurEvery = 1; } public override DateTime CalculateFirstRun(SchedulationRules rules, DateTime startFrom) { if (RecurEvery <= 0) RecurEvery = 1; DateTime virtualLastRun = (startFrom.Date < DateTime.Now.Date ? DateTime.Now.AddDays(-1 * this.RecurEvery) : startFrom); return virtualLastRun; } public override DateTime CalculateNextRun(SchedulationRules rules, DateTime lastRun) { if (RecurEvery <= 0) RecurEvery = 1; DateTime res = new DateTime(lastRun.Year, lastRun.Month, lastRun.Day, rules.StartAtHH, rules.StartAtMM, 0); if (res.Hour <= lastRun.Hour) { res = res.AddDays(RecurEvery); } return res; } public override bool CheckValues() { this.RecurEvery = Math.Max(1, this.RecurEvery); return true; } } [DataContract] public class WeekRule : ISchedulationRule { public WeekRule() { this.RecurEvery = 1; } [XmlAttribute] [DataMember] public bool Sunday { get; set; } [XmlAttribute] [DataMember] public bool Monday { get; set; } [XmlAttribute] [DataMember] public bool Tuesday { get; set; } [XmlAttribute] [DataMember] public bool Wednesday { get; set; } [XmlAttribute] [DataMember] public bool Thursday { get; set; } [XmlAttribute] [DataMember] public bool Friday { get; set; } [XmlAttribute] [DataMember] public bool Saturday { get; set; } private DayOfWeek[] DaysOfWeek() { List res = new List(); if (Sunday) res.Add(DayOfWeek.Sunday); if (Monday) res.Add(DayOfWeek.Monday); if (Tuesday) res.Add(DayOfWeek.Tuesday); if (Wednesday) res.Add(DayOfWeek.Wednesday); if (Thursday) res.Add(DayOfWeek.Thursday); if (Friday) res.Add(DayOfWeek.Friday); if (Saturday) res.Add(DayOfWeek.Saturday); return res.ToArray(); } public override DateTime CalculateFirstRun(SchedulationRules rules, DateTime startFrom) { if (RecurEvery <= 0) RecurEvery = 1; DateTime virtualLastRun = (startFrom.Date < DateTime.Now.Date ? DateTime.Now.AddDays(-1 * this.RecurEvery * 7) : /*startFrom*/ CalculateNextRun(rules, startFrom.AddDays(-1 * this.RecurEvery * 7))); return virtualLastRun; } public override DateTime CalculateNextRun(SchedulationRules rules, DateTime lastRun) { DateTime res = DateTime.MaxValue; DayOfWeek[] days = DaysOfWeek(); for (int i = 0; i < days.Length; i++) { DayOfWeek day = days[i]; DateTime tempRes = CalculateNextRun(rules, day, lastRun); res = (tempRes < res ? tempRes : res); } return res; } public DateTime CalculateNextRun(SchedulationRules rules, DayOfWeek day, DateTime lastRun) { if (RecurEvery <= 0) RecurEvery = 1; DayOfWeek firstDay = day; DateTime res = new DateTime(lastRun.Year, lastRun.Month, lastRun.Day, rules.StartAtHH, rules.StartAtMM, 0); if (res.DayOfWeek != firstDay) { res = res.AddDays(firstDay - res.DayOfWeek); } if (res.DayOfYear <= lastRun.DayOfYear && res.Hour <= lastRun.Hour) { res = res.AddDays(7 * RecurEvery); } return res; } public override bool CheckValues() { this.RecurEvery = Math.Max(1, this.RecurEvery); return true; } } [DataContract] public class MonthRule : ISchedulationRule { public MonthRule() { this.RecurEvery = 1; this.DayOfMonth = 1; } [XmlAttribute] [DataMember] public int DayOfMonth { get; set; } public override DateTime CalculateFirstRun(SchedulationRules rules, DateTime startFrom) { if (RecurEvery <= 0) RecurEvery = 1; DateTime virtualLastRun = (startFrom.Date < DateTime.Now.Date ? DateTime.Now.AddMonths(-1 * this.RecurEvery) : /*startFrom*/ CalculateNextRun(rules, startFrom.AddMonths(-1 * this.RecurEvery))); return virtualLastRun; } public override DateTime CalculateNextRun(SchedulationRules rules, DateTime lastRun) { if (RecurEvery <= 0) RecurEvery = 1; DateTime res = new DateTime(lastRun.Year, lastRun.Month, 1, rules.StartAtHH, rules.StartAtMM, 0); if (lastRun.Day > this.DayOfMonth || (lastRun.Day == this.DayOfMonth && lastRun.Hour >= rules.StartAtHH) || lastRun.Day == DateTime.DaysInMonth(lastRun.Year, lastRun.Month)) res = res.AddMonths(this.RecurEvery); int lastDayOfMonth = DateTime.DaysInMonth(res.Year, res.Month); int day = Math.Min(lastDayOfMonth, this.DayOfMonth); res = new DateTime(res.Year, res.Month, day, res.Hour, res.Minute, 0); return res; } public override bool CheckValues() { this.RecurEvery = Math.Max(1, this.RecurEvery); return true; } } [DataContract] [KnownType(typeof(WeekRule))] [KnownType(typeof(DayRule))] [KnownType(typeof(MinuteRule))] [KnownType(typeof(MonthRule))] public class SchedulationRules { public RecurrenceType RecurrenceType { get{ return Rule.RecurrenceType; } } [XmlAttribute] [DataMember] public int StartAtHH { get; set; } [XmlAttribute] [DataMember] public int StartAtMM { get; set; } [DataMember] public ISchedulationRule Rule { get; set; } public DateTime CalculateFirstRun( DateTime startFrom) { return this.Rule.CalculateFirstRun(this, startFrom); } public DateTime CalculateNextRun(DateTime lastRun) { return this.Rule.CalculateNextRun(this, lastRun); } } public class SchedulationRulesEngine { public DateTime GetFirstRun(SchedulationRules rules, DateTime startFrom) { DateTime res = rules.CalculateFirstRun(startFrom); while (res < DateTime.Now) { res = rules.CalculateNextRun(res); if (res == DateTime.MinValue) { return res; } } ; return res; } public DateTime GetNextRun(SchedulationRules rules, DateTime lastRun) { DateTime res = lastRun; do { res = rules.CalculateNextRun(res); if (res == DateTime.MinValue) { return res; } } while (res < DateTime.Now); return res; } } }