Click here to Skip to main content
15,894,405 members
Articles / Programming Languages / C#
Article

A component for event scheduling inside an application

Rate me:
Please Sign up or sign in to vote.
4.89/5 (67 votes)
30 Sep 20043 min read 223.9K   6.9K   211   74
This article presents the design and a readily usable component for scheduling events which are consumed inside a server or service application.

Introduction

Every typical server or service application need scheduling of some events inside the application. These events generally are supposed to wake up at certain determined times to do self checks, check statuses of threads, or typically refresh resources once in a while, and so on. This requires shorter and long time scheduling of events inside the application. The most common approach is to use timers for different tasks and attach them to threads. A scheduler component is presented in this article for simplifying a way to create and maintain event scheduling inside an application.

Using the code

The best example to create and use the Schedule classes is in the demo application. This piece of code inside the demo creates all the types of schedule objects implemented in the library, and also provides a delegate ScheduleCallBack() for the schedule to call back for OnTrigger event.

C#
// create and add different types of schedules
Schedule s = new IntervalSchedule("Test_Interval", 
             DateTime.Now.AddMinutes(1), 45, TimeSpan.Zero, 
             new TimeSpan(TimeSpan.TicksPerDay));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);

s = new OneTimeSchedule("Test_Onetime", DateTime.Now.AddMinutes(1.5));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);

s = new DailySchedule("Test_daily", DateTime.Now.AddMinutes(2));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);

s = new WeeklySchedule("Test_weekly", DateTime.Now.AddMinutes(2.5));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);

s = new MonthlySchedule("Test_monthly", DateTime.Now.AddMinutes(3));
s.OnTrigger += new EventScheduler.Invoke(ScheduleCallBack);
Scheduler.AddSchedule(s);

As can be seen, the three main lines are to:

  • create a Schedule instance
  • subscribe to the OnTrigger event, and
  • add the Schedule to the Scheduler's list

That's it! To see this in action, run the demo application shown below and click on Test Code button. This will create an instance of each schedule and gives a feeling of what the scheduler can do.

Image 1

Design and Code details

The following class diagram shows the different classes in the scheduler library and their relationship.

Image 2

At the root of the library is the Schedule class which implements IComparable interface. The IComparable interface provides the CompareTo(Object) method which is used to compare two Schedule objects for sorting the list of schedules to determine the sequence of invocation times for the timer.

The library already provides general Schedules like OneTimeSchedule (used for raising an event only once), IntervalSchedule (used to raise an event at regular intervals), DailySchedule, WeeklySchedule and MonthlySchedule, which are self explanatory. The Schedule base class has generic properties like Name, Type, NextInvokeTime etc. used by all derived objects, and some specific properties like Interval which is used in this case only by IntervalSchedule.

These are the steps through which the Scheduler goes through for scheduling an event:

  • A static timer (Scheduler.Timer) with a call back method (DispatchEvents) is created and initially put to sleep.
  • Schedules are created either by code or by GUI. Individual conditions are checked in the Schedule's constructor.
  • When ever a Schedule object is added to the Scheduler using AddSchedule(Schedule s) method:
    • the list is sorted. The list which is an ArrayList in turn uses the Schedule.CompareTo(Object) method to determine the sorting order.
    • the NextInvokeTime of the first element in the list is used by the Timer to decide when to wake up next.
  • When the Timer wakes up:
    • it calls DispatchEvents call back
    • which calls TriggerEvents() on the first Schedule object in the list

Image 3

A Schedule's view GUI is also provided with the library to manage the schedules through a GUI as a singleton class (ScheduleUI) which is created by the static method ScheduleUI.ShowSchedules(). This GUI uses the Scheduler.OnSchedulerEvent event provided by Scheduler to track when a Schedule is created, deleted, or invoked.

Image 4

Typically, this GUI can be used for administration of schedules and also to create or delete them easily. Once a schedule is created, it can be accessed programmatically using Scheduler events or the name of the Schedule itself.

For e.g., the following code inside ScheduleUI class shows how the Scheduler events are used to refresh the list of Schedules.

C#
public void OnSchedulerEvent(SchedulerEventType type, string scheduleName)
{
    switch(type)
    {
        case SchedulerEventType.CREATED:
            ListViewItem lv = SchedulesView.Items.Add(scheduleName);
            Schedule s = Scheduler.GetSchedule(scheduleName);
            lv.SubItems.Add(s.Type.ToString());
            lv.SubItems.Add(s.NextInvokeTime.ToString("MM/dd/yyyy hh:mm:ss tt"));
            break;
        case SchedulerEventType.DELETED:
            for (int i=0; i<SchedulesView.Items.Count; i++)
                if (SchedulesView.Items[i].Text == scheduleName)
                    SchedulesView.Items.RemoveAt(i);
            break;
        case SchedulerEventType.INVOKED:
            for (int i=0; i<SchedulesView.Items.Count; i++)
                if (SchedulesView.Items[i].Text == scheduleName)
                {
                    Schedule si = Scheduler.GetSchedule(scheduleName);
                    SchedulesView.Items[i].SubItems[2].Text =
                        si.NextInvokeTime.ToString("MM/dd/yyyy hh:mm:ss tt");
                }
            break;
    }
    SchedulesView.Refresh();
}

Another critical piece of code is the bool[] m_workingWeekDays array in the Schedule base class, in which the active week days are stored. The bool NoFreeWeekDay() and bool CanInvokeOnNextWeekDay() use this array to determine if a schedule can run on a week day.

Similarly, the bool IsInvokeTimeInTimeRange() determines if a schedule can run in a time range on any given day. Please refer to comments in the code if you are interested to get into the details.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSave schedules and loading at run time Pin
wtoast10-Dec-14 10:40
wtoast10-Dec-14 10:40 
QuestionOne little bug and one inconsistency Pin
Member 797473426-May-14 11:09
Member 797473426-May-14 11:09 
QuestionLicence?? Pin
pippo pioppo30-Aug-12 3:24
pippo pioppo30-Aug-12 3:24 
Questionquery Pin
Apurva Kunkulol9-May-12 21:41
professionalApurva Kunkulol9-May-12 21:41 
GeneralMy vote of 3 Pin
programmerdon22-Mar-12 9:18
programmerdon22-Mar-12 9:18 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey12-Feb-12 20:37
professionalManoj Kumar Choubey12-Feb-12 20:37 
Generaltake a look at Quartz Pin
Chris Tracy17-May-08 9:30
Chris Tracy17-May-08 9:30 
GeneralRe: take a look at Quartz Pin
J0J0_28-Jun-08 3:18
J0J0_28-Jun-08 3:18 
GeneralVS 2008 problem: Cross-thread operation not valid Pin
Mr_AndersonNET5-May-08 23:14
Mr_AndersonNET5-May-08 23:14 
GeneralRe: VS 2008 problem: Cross-thread operation not valid Pin
Peeter15-Jan-10 9:59
Peeter15-Jan-10 9:59 
AnswerRe: VS 2008 problem: Cross-thread operation not valid Pin
Prabakar Samiyappan20-Feb-11 7:23
professionalPrabakar Samiyappan20-Feb-11 7:23 
GeneralRe: VS 2008 problem: Cross-thread operation not valid Pin
JonFrost17-Mar-16 23:36
JonFrost17-Mar-16 23:36 
Questionnew version for schedle? Pin
qq3444863510-Apr-08 2:59
qq3444863510-Apr-08 2:59 
QuestionPersist in a database Pin
camlopes21-Jan-08 4:33
camlopes21-Jan-08 4:33 
GeneralThanks for the idea Pin
Joseph Wee27-Nov-07 12:57
Joseph Wee27-Nov-07 12:57 
GeneralRe: Thanks for the idea Pin
Sriram Chitturi28-Nov-07 0:06
Sriram Chitturi28-Nov-07 0:06 
GeneralRunning without GUI Pin
roseen25-Sep-07 21:51
roseen25-Sep-07 21:51 
GeneralRe: Running without GUI Pin
samphil26-Sep-07 2:42
samphil26-Sep-07 2:42 
GeneralNew version Pin
dima polyakov21-Sep-07 10:16
dima polyakov21-Sep-07 10:16 
After using these classes in couple projects, I found tons of mistakes.
Here is my version that I use (it is also not perfect, but IMHO more usefull). There are changes basically in every class:

Schedule.cs
<br />
using System;<br />
using System.Threading;<br />
<br />
namespace EventScheduler<br />
{<br />
	// delegate for OnTrigger() event<br />
	public delegate void Invoke(string scheduleName);<br />
<br />
	// enumeration for schedule types<br />
	public enum ScheduleType { ONETIME, INTERVAL, DAILY, WEEKLY, MONTHLY };<br />
<br />
	// base class for all schedules<br />
	abstract public class Schedule : IComparable<br />
	{<br />
		public event Invoke OnTrigger;<br />
		protected string m_name;		// name of the schedule<br />
		protected ScheduleType m_type;	// type of schedule<br />
		protected bool m_active;		// is schedule active ?<br />
<br />
		protected DateTime m_nextTime;	// time when this schedule is invoked next, used by scheduler<br />
<br />
		// m_fromTime and m_toTime are used to defined a time range during the day<br />
		// between which the schedule can run.<br />
		// This is useful to define a range of working hours during which a schedule can run<br />
		protected TimeSpan m_fromTime;<br />
		protected TimeSpan m_toTime;<br />
<br />
		// Array containing the 7 weekdays and their status<br />
		// Using DayOfWeek enumeration for index of this array<br />
		bool[] m_workingWeekDays = new bool[]{true, true, true, true, true, true, true};<br />
<br />
		// time interval used by schedules like IntervalSchedule<br />
		protected TimeSpan m_interval = new TimeSpan();<br />
<br />
		public void SetWorkingDaysOnly()<br />
		{<br />
			SetWeekDay(DayOfWeek.Monday, true);<br />
			SetWeekDay(DayOfWeek.Tuesday, true);<br />
			SetWeekDay(DayOfWeek.Wednesday, true);<br />
			SetWeekDay(DayOfWeek.Thursday, true);<br />
			SetWeekDay(DayOfWeek.Friday, true);<br />
			SetWeekDay(DayOfWeek.Saturday, false);<br />
			SetWeekDay(DayOfWeek.Sunday, false);<br />
		}<br />
<br />
		// Accessor for type of schedule<br />
		public ScheduleType Type<br />
		{<br />
			get { return m_type; }<br />
		}<br />
<br />
		// Accessor for name of schedule<br />
		// Name is set in constructor only and cannot be changed<br />
		public string Name <br />
		{<br />
			get { return m_name; }<br />
		}<br />
<br />
		public bool Active<br />
		{<br />
			get { return m_active; }<br />
			set { m_active = value; }<br />
		}<br />
<br />
		// check if no week days are active<br />
		protected bool NoFreeWeekDay()<br />
		{<br />
			bool check = false;<br />
			for (int index=0; index<7; check = check|m_workingWeekDays[index], index++);<br />
			return check;<br />
		}<br />
<br />
		// Setting the status of a week day<br />
		public void SetWeekDay(DayOfWeek day, bool On)<br />
		{<br />
			m_workingWeekDays[(int)day] = On;<br />
			Active = true; // assuming<br />
<br />
			// Make schedule inactive if all weekdays are inactive<br />
			// If a schedule is not using the weekdays the array should not be touched<br />
			if (NoFreeWeekDay())<br />
				Active = false;<br />
		}<br />
<br />
		// Return if the week day is set active<br />
		public bool WeekDayActive(DayOfWeek day)<br />
		{<br />
			return m_workingWeekDays[(int)day];<br />
		}<br />
<br />
		// Method which will return when the Schedule has to be invoked next<br />
		// This method is used by Scheduler for sorting Schedule objects in the list<br />
		public DateTime NextInvokeTime<br />
		{<br />
			get { return m_nextTime; }<br />
		}<br />
<br />
		// Accessor for m_interval in seconds<br />
		// Put a lower limit on the interval to reduce burden on resources<br />
		// I am using a lower limit of 30 seconds<br />
		public TimeSpan Interval<br />
		{<br />
			get { return m_interval; }<br />
			set {<br />
				if (value.TotalSeconds < 1)<br />
					throw new SchedulerException("Interval cannot be less than 1 second");<br />
				m_interval = value;<br />
			}<br />
		}<br />
<br />
		// Constructor<br />
		public Schedule(string name, ScheduleType type)<br />
		{<br />
			m_type = type;<br />
			m_name = name;<br />
			m_nextTime = DateTime.Now;<br />
		}<br />
<br />
		// Sets the next time this Schedule is kicked off and kicks off events on<br />
		// a seperate thread, freeing the Scheduler to continue<br />
		public void TriggerEvents()<br />
		{<br />
			CalculateNextInvokeTime(); // first set next invoke time to continue with rescheduling<br />
			ThreadStart ts = new ThreadStart(KickOffEvents);<br />
			Thread t = new Thread(ts);<br />
			t.Start();<br />
		}<br />
<br />
		// Implementation of ThreadStart delegate.<br />
		// Used by Scheduler to kick off events on a seperate thread<br />
		private void KickOffEvents()<br />
		{<br />
			if (OnTrigger != null)<br />
				OnTrigger(Name);<br />
		}<br />
<br />
		// To be implemented by specific schedule objects when to invoke the schedule next<br />
		internal abstract void CalculateNextInvokeTime();<br />
<br />
		// check to see if the Schedule can be invoked on the week day it is next scheduled <br />
		protected bool CanInvokeOnNextWeekDay()<br />
		{<br />
			return m_workingWeekDays[(int)m_nextTime.DayOfWeek];<br />
		}<br />
<br />
		// Check to see if the next time calculated is within the time range<br />
		// given by m_fromTime and m_toTime<br />
		// The ranges can be during a day, for eg. 9 AM to 6 PM on same day<br />
		// or overlapping 2 different days like 10 PM to 5 AM (i.e over the night)<br />
		protected bool IsInvokeTimeInTimeRange()<br />
		{<br />
			if (m_fromTime < m_toTime) // eg. like 9 AM to 6 PM<br />
				return (m_nextTime.TimeOfDay >= m_fromTime && m_nextTime.TimeOfDay <= m_toTime);<br />
			else // eg. like 10 PM to 5 AM<br />
				return (m_nextTime.TimeOfDay >= m_toTime && m_nextTime.TimeOfDay <= m_fromTime);<br />
		}<br />
<br />
		// IComparable interface implementation is used to sort the array of Schedules<br />
		// by the Scheduler<br />
		public int CompareTo(object obj)<br />
		{<br />
			if (obj is Schedule)<br />
			{<br />
				return m_nextTime.CompareTo(((Schedule)obj).m_nextTime);<br />
			}<br />
			throw new Exception("Not a Schedule object");<br />
		}<br />
	}<br />
}<br />


Scheduler.cs
using System;<br />
using System.Threading;<br />
using System.Collections;<br />
<br />
namespace EventScheduler<br />
{<br />
	// enumeration of Scheduler events used by the delegate<br />
	public enum SchedulerEventType { CREATED, DELETED, INVOKED };<br />
<br />
	// delegate for Scheduler events<br />
	public delegate void SchedulerEventDelegate(SchedulerEventType type, string scheduleName);<br />
<br />
	// This is the main class which will maintain the list of Schedules<br />
	// and also manage them, like rescheduling, deleting schedules etc.<br />
	public sealed class Scheduler<br />
	{<br />
		// Event raised when for any event inside the scheduler<br />
		static public event SchedulerEventDelegate OnSchedulerEvent;<br />
<br />
		// next event which needs to be kicked off,<br />
		// this is set when a new Schedule is added or after invoking a Schedule<br />
		static Schedule m_nextSchedule = null;<br />
		static ArrayList m_schedulesList = new ArrayList(); // list of schedules<br />
		static Timer m_timer = new Timer(new TimerCallback(DispatchEvents), // main timer<br />
											null,<br />
											Timeout.Infinite,<br />
											Timeout.Infinite);<br />
<br />
		// Get schedule at a particular index in the array list<br />
		public static Schedule GetScheduleAt(int index)<br />
		{<br />
			if (index < 0 || index >= m_schedulesList.Count)<br />
				return null;<br />
			return (Schedule)m_schedulesList[index];<br />
		}<br />
<br />
		// Number of schedules in the list<br />
		public static int Count()<br />
		{<br />
			return m_schedulesList.Count;<br />
		}<br />
<br />
		// Indexer to access a Schedule object by name<br />
		public static Schedule GetSchedule(string scheduleName)<br />
		{<br />
			for (int index=0; index < m_schedulesList.Count; index++)<br />
				if (((Schedule)m_schedulesList[index]).Name == scheduleName)<br />
					return (Schedule)m_schedulesList[index];<br />
			return null;<br />
		}<br />
<br />
		// call back for the timer function<br />
		static void DispatchEvents(object obj) // obj ignored<br />
		{<br />
			if (m_nextSchedule == null)<br />
				return;<br />
			m_nextSchedule.TriggerEvents(); // make this happen on a thread to let this thread continue<br />
			if (m_nextSchedule.Type == ScheduleType.ONETIME)<br />
			{<br />
				RemoveSchedule(m_nextSchedule); // remove the schedule from the list<br />
			}<br />
			else<br />
			{<br />
				if (OnSchedulerEvent != null)<br />
					OnSchedulerEvent(SchedulerEventType.INVOKED, m_nextSchedule.Name);<br />
				m_schedulesList.Sort();<br />
				SetNextEventTime();<br />
			}<br />
		}<br />
<br />
		// method to set the time when the timer should wake up to invoke the next schedule<br />
		static void SetNextEventTime()<br />
		{<br />
			if (m_schedulesList.Count == 0)<br />
			{<br />
				m_timer.Change(Timeout.Infinite, Timeout.Infinite); // this will put the timer to sleep<br />
				return;<br />
			}<br />
			m_nextSchedule = (Schedule)m_schedulesList[0];<br />
			TimeSpan ts = m_nextSchedule.NextInvokeTime.Subtract(DateTime.Now);<br />
			if (ts < TimeSpan.Zero)<br />
					ts = TimeSpan.Zero; // time already past, set timer to 0 to execute callback<br />
			m_timer.Change((int)ts.TotalMilliseconds, Timeout.Infinite); // invoke after the timespan<br />
		}<br />
<br />
		// add a new schedule<br />
		public static void AddSchedule(Schedule s)<br />
		{<br />
			if (GetSchedule(s.Name) != null)<br />
				throw new SchedulerException("Schedule with the same name already exists");<br />
			m_schedulesList.Add(s);<br />
			m_schedulesList.Sort();<br />
			// adjust the next event time if schedule is added at the top of the list<br />
			if (m_schedulesList[0] == s)<br />
				SetNextEventTime();<br />
			if (OnSchedulerEvent != null)<br />
				OnSchedulerEvent(SchedulerEventType.CREATED, s.Name);<br />
		}<br />
<br />
		// remove a schedule object from the list<br />
		public static void RemoveSchedule(Schedule s)<br />
		{<br />
			m_schedulesList.Remove(s);<br />
			SetNextEventTime();<br />
			if (OnSchedulerEvent != null)<br />
				OnSchedulerEvent(SchedulerEventType.DELETED, s.Name);<br />
		}<br />
<br />
		// remove all schedules<br />
		public static void RemoveAllSchedules()<br />
		{<br />
			while(m_schedulesList.Count > 0)<br />
			{<br />
				String name = ((Schedule)m_schedulesList[0]).Name;<br />
				m_schedulesList.Remove((Schedule)m_schedulesList[0]);<br />
				SetNextEventTime();<br />
				if(OnSchedulerEvent != null)<br />
					OnSchedulerEvent(SchedulerEventType.DELETED, name);<br />
			}<br />
		}<br />
<br />
		// remove schedule by name<br />
		public static void RemoveSchedule(string name)<br />
		{<br />
			RemoveSchedule(GetSchedule(name));<br />
		}<br />
	}<br />
}<br />


SchedulerExceptions.cs
using System;<br />
<br />
namespace EventScheduler<br />
{<br />
	/// <summary><br />
	/// Summary description for SchedulerException.<br />
	/// </summary><br />
	public class SchedulerException : Exception<br />
	{<br />
		public SchedulerException(string msg) : base(msg)<br />
		{<br />
		}<br />
	}<br />
}<br />


ScheduleTypes.cs
using System;<br />
<br />
namespace EventScheduler<br />
{<br />
	// OneTimeSchedule is used to schedule an event to run only once<br />
	// Used by specific tasks to check self status<br />
	public class OneTimeSchedule : Schedule<br />
	{<br />
		public OneTimeSchedule(String name, DateTime startTime) <br />
			: base(name, ScheduleType.ONETIME)<br />
		{<br />
			m_nextTime = startTime;<br />
		}<br />
		internal override void CalculateNextInvokeTime()<br />
		{<br />
			// it does not matter, since this is a one time schedule<br />
			m_nextTime = DateTime.MaxValue;<br />
		}<br />
	}<br />
<br />
	// IntervalSchedule is used to schedule an event to be invoked at regular intervals<br />
	// the interval is specified in seconds. Useful mainly in checking status of threads<br />
	// and connections. Use an interval of 60 hours for an hourly schedule<br />
	public class IntervalSchedule : Schedule<br />
	{<br />
		public IntervalSchedule(String name, TimeSpan TimeInterval,<br />
				TimeSpan fromTime, TimeSpan toTime) // time range for the day<br />
			: base(name, ScheduleType.INTERVAL)<br />
		{<br />
			if(TimeInterval.Ticks >= TimeSpan.TicksPerDay)<br />
				throw new SchedulerException("TimeInterval > max ticks per day");<br />
			if(fromTime.Ticks >= TimeSpan.TicksPerDay)<br />
				throw new SchedulerException("fromTime > max ticks per day");<br />
			if(toTime.Ticks >= TimeSpan.TicksPerDay)<br />
				throw new SchedulerException("toTime > max ticks per day");<br />
<br />
			m_fromTime = fromTime;<br />
			m_toTime = toTime;<br />
			Interval = TimeInterval;<br />
<br />
			DateTime now = DateTime.Now;<br />
<br />
			DateTime start = new DateTime(now.Year, now.Month, now.Day, m_fromTime.Hours, m_fromTime.Minutes, m_fromTime.Seconds, m_fromTime.Milliseconds);<br />
			DateTime end = new DateTime(now.Year, now.Month, now.Day, m_toTime.Hours, m_toTime.Minutes, m_toTime.Seconds, m_toTime.Milliseconds);<br />
<br />
			m_nextTime = start;<br />
<br />
			if(fromTime < toTime)<br />
			{<br />
				do<br />
				{<br />
					if(now <= start)<br />
						break;<br />
<br />
					if(now >= end || now + TimeInterval >= end)<br />
					{<br />
						m_nextTime = start.AddDays(1);<br />
						break;<br />
					}<br />
<br />
					long ticks_from_start = now.TimeOfDay.Ticks - start.TimeOfDay.Ticks;<br />
					long whole_past_ticks_intervals = ticks_from_start / TimeInterval.Ticks;<br />
<br />
					start = start.AddTicks(TimeInterval.Ticks * whole_past_ticks_intervals + TimeInterval.Ticks);<br />
				} while(false);<br />
			}<br />
			else<br />
				throw new SchedulerException("Not supported time range");<br />
<br />
			m_nextTime = start;<br />
		}<br />
<br />
		internal override void CalculateNextInvokeTime()<br />
		{<br />
			// add the interval of m_seconds<br />
			m_nextTime = m_nextTime.AddTicks(Interval.Ticks);<br />
<br />
			// if next invoke time is not within the time range, then set it to next start time<br />
			if (! IsInvokeTimeInTimeRange())<br />
			{<br />
				Boolean bAddDay = false;<br />
				if(m_nextTime.TimeOfDay > m_toTime)<br />
					bAddDay = true;<br />
<br />
				// set m_nextTime to the beginning of a new day<br />
				m_nextTime = new DateTime(m_nextTime.Year, m_nextTime.Month, m_nextTime.Day, m_fromTime.Hours, m_fromTime.Minutes, m_fromTime.Seconds, m_fromTime.Milliseconds);<br />
				<br />
				if(bAddDay)<br />
					m_nextTime = m_nextTime.AddDays(1);<br />
			}<br />
<br />
			// check to see if the next invoke time is on a working day<br />
			while(!CanInvokeOnNextWeekDay())<br />
				m_nextTime = m_nextTime.AddDays(1); // start checking on the next day<br />
		}<br />
	}<br />
<br />
	// Daily schedule is used set off to the event every day<br />
	// Mainly useful in maintanance, recovery, logging and report generation<br />
	// Restictions can be imposed on the week days on which to run the schedule<br />
	public class DailySchedule : Schedule<br />
	{<br />
		public DailySchedule(String name, TimeSpan startTime)<br />
			: base(name, ScheduleType.DAILY)<br />
		{<br />
			if(startTime.Ticks >= TimeSpan.TicksPerDay)<br />
				throw new SchedulerException("startTime > max ticks per day");<br />
<br />
			DateTime current = DateTime.Now;<br />
<br />
			DateTime today_start = new DateTime(current.Year, current.Month, current.Day, startTime.Hours, startTime.Minutes, startTime.Seconds, startTime.Milliseconds);<br />
<br />
			int res = DateTime.Compare(current, today_start);<br />
			if(res > 0)// current > start_time<br />
				today_start = today_start.AddDays(1);<br />
<br />
			m_nextTime = today_start;<br />
		}<br />
<br />
		internal override void CalculateNextInvokeTime()<br />
		{<br />
			// add a day, and check for any weekday restrictions and keep adding a day<br />
			m_nextTime = m_nextTime.AddDays(1);<br />
<br />
			while(!CanInvokeOnNextWeekDay())<br />
				m_nextTime = m_nextTime.AddDays(1);<br />
		}<br />
	}<br />
<br />
	// Weekly schedules<br />
	public class WeeklySchedule : Schedule<br />
	{<br />
		public WeeklySchedule(String name, DateTime startTime)<br />
			: base(name, ScheduleType.WEEKLY)<br />
		{<br />
			m_nextTime = startTime;<br />
		}<br />
<br />
		public WeeklySchedule(String name, DayOfWeek day, TimeSpan time)<br />
			: base(name, ScheduleType.WEEKLY)<br />
		{<br />
			DateTime now = DateTime.Now;<br />
			DateTime this_week = now.Date.AddDays(-(int)now.DayOfWeek);<br />
<br />
			m_nextTime = this_week.Add(time);<br />
<br />
			if(m_nextTime <= DateTime.Now)<br />
				CalculateNextInvokeTime();<br />
		}<br />
		<br />
		// add a week (or 7 days) to the date<br />
		internal override void CalculateNextInvokeTime()<br />
		{<br />
			m_nextTime = m_nextTime.AddDays(7);<br />
		}<br />
	}<br />
<br />
	// Monthly schedule - used to kick off an event every month on the same day as scheduled<br />
	// and also at the same hour and minute as given in start time<br />
	public class MonthlySchedule : Schedule<br />
	{<br />
		public MonthlySchedule(String name, DateTime startTime)<br />
			: base(name, ScheduleType.MONTHLY)<br />
		{<br />
			m_nextTime = startTime;<br />
		}<br />
		public MonthlySchedule(String name, TimeSpan startDateTimeFromTheBeginningOfTheMonth)<br />
			: base(name, ScheduleType.MONTHLY)<br />
		{<br />
			DateTime now = DateTime.Now;<br />
<br />
			DateTime dt = new DateTime(now.Year, now.Month, 1) + startDateTimeFromTheBeginningOfTheMonth;<br />
<br />
			dt = dt.AddDays(-1);<br />
<br />
			if(dt < now)<br />
				dt = dt.AddMonths(1);<br />
<br />
			m_nextTime = dt;<br />
		}<br />
		// add a month to the present time<br />
		internal override void CalculateNextInvokeTime()<br />
		{<br />
			m_nextTime = m_nextTime.AddMonths(1);<br />
		}<br />
	}<br />
}<br />


Dima
http://www.mmexplorer.com[^]
GeneralRe: New version Pin
ingos200722-Sep-07 11:23
ingos200722-Sep-07 11:23 
GeneralRe: New version Pin
dima polyakov23-Sep-07 10:56
dima polyakov23-Sep-07 10:56 
GeneralRe: New version Pin
samphil26-Sep-07 2:40
samphil26-Sep-07 2:40 
GeneralRe: New version Pin
dima polyakov26-Sep-07 5:07
dima polyakov26-Sep-07 5:07 
GeneralPerhaps you should read up on 'lock' Pin
robvon18-Sep-07 23:20
robvon18-Sep-07 23:20 
GeneralFIX - Intervals were not expiring. Pin
r3b123-Aug-07 11:22
r3b123-Aug-07 11:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.