2

I'm trying to create an action when a given time of a given day of week rolls around.

Here's what I have so far:

private void wbnotifier_CheckedChanged(object sender, EventArgs e)
{            
    if (DateTime.UtcNow.DayOfWeek == DayOfWeek.Friday)
        MessageBox.Show ("It's Friday");

    if (DateTime.UtcNow.DayOfWeek == DayOfWeek.Thursday)
        MessageBox.Show("It's Thursday");                
}

How can I arrange it so that 1:00 PM on Friday and 5:00 PM on Thursday the action will trigger?

Would it be similar for multiple times on the same date? (Friday, 1:00 PM, 3:00 PM, 6:00 PM)

PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
EyeSeeSharp
  • 604
  • 1
  • 8
  • 21

4 Answers4

1

If you want to schedule things in .NET, you should check out Quartz.net. Here is an example of a CronTrigger:

// Build a trigger that will fire on Wednesdays at 10:42 am, in a TimeZone other // than the system's default:

trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithSchedule(CronScheduleBuilder
        .WeeklyOnDayAndHourAndMinute(DayOfWeek.Wednesday, 10, 42)
        .InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
    .ForJob(myJobKey)
    .Build();
Rebecca
  • 13,914
  • 10
  • 95
  • 136
  • @L-Three "I'm trying to create an action when a given time of a given day of week rolls around.". Seems like Quartz is a good fit for this. – Rebecca Jan 22 '15 at 15:36
0

Have a look at ncrontab project. (You can either manually add a DLL reference to your solution or simply install it with NuGet.)

ncrontab allows for easy configuration of task like yours. In your code or App.config you simply define a string that consists of 5 parts:

* * * * *
- - - - -
| | | | |
| | | | +----- day of week (0 - 6) (Sunday=0)
| | | +------- month (1 - 12)
| | +--------- day of month (1 - 31)
| +----------- hour (0 - 23)
+------------- min (0 - 59)

To make it a little simpler: Would it be possible to start both tasks at the same time? AFAIK it is not possible to confige individual times for each day within the same pattern.

string pattern = "* 17 * * 4,5";

If this is not practical for you, you could define two different patterns and then check which one of them occurs sooner.

In your case this might look like this:

// Friday 1 PM
string fridayPattern = "* 13 * * 5";

or

// Thursday 5 PM
string thursdayPattern = "* 17 * * 4";

The code to interpret the patterns could look like this:

CrontabSchedule schedule = CrontabSchedule.Parse(pattern);
DateTime nextOccurrence = this.schedule.GetNextOccurrence(DateTime.Now);

TimeSpan interval = nextOccurrence.Subtract(now);

System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = interval.TotalMilliseconds;

timer.Elapsed -= this.OnScheduleTimerElapsed;
timer.Elapsed += this.OnScheduleTimerElapsed;
timer.Start();

Note: If you are using the 2 pattern idea, you would CrontabSchedule.Parse() both strings (fridayPattern and thursdayPattern) here and then find which GetNextOccurrence() would occur sooner.

Either way, then, of course, you would need to handle the event:

private void OnScheduleTimerElapsed(object sender, ElapsedEventArgs e)
{
  this.timer.Stop();
  this.timer.Elapsed -= this.OnScheduleTimerElapsed;

  // Start your work here.
}

Your second question:

To configure multiple runs on the same day would look like this:

// Friday, 1:00 PM, 3:00 PM, 6:00 PM
string multipleRunsOnSameDayPattern = "* 13,15,18 * * 5"
Jens H
  • 4,590
  • 2
  • 25
  • 35
-1

For weekly recurring notification you can have something like below

Dictionary<DayOfWeek, Dictionary<TimeSpan, Action>> recurringNotifications = new Dictionary<DayOfWeek, Dictionary<TimeSpan, Action>>();

    recurringNotifications.Add(DayOfWeek.Friday, new Dictionary<TimeSpan, Action>(){ {TimeSpan.FromHours(1), () => Console.WriteLine("1 AM") }});

And on your timer you can do some thing like

var todaysEvents = recurringNotifications[DateTime.UtcNow.DayOfWeek];

    todaysEvents[TimeSpan.FromHours(1)].BeginInvoke();

Or You can pick the action and put it in some Queue, and using seperate thread execute them either synchronously or asynchronously. In that case there will be no chance of missing second.

Rajnikant
  • 2,176
  • 24
  • 23
  • _"on your timer"_ - yeah, and if the timer misses a second or when the application simply isn't running, no code will be executed and nobody will notice. Don't reinvent the wheel; use Windows Task Scheduler or a scheduling library instead of rolling your own. – CodeCaster Jan 22 '15 at 15:41
  • Well I have answered the Way, user can just take action and put that in queue, I have explained cleaner way of storing the task. – Rajnikant Jan 22 '15 at 16:48
  • @CodeCaster Also there is nothing mentioned in the question about synchronizing or missing seconds. Question is not about trigger its about how to arrange the actions. If you know any schedular which doesn't work on timer tick? – Rajnikant Jan 22 '15 at 16:54
  • OP want to schedule the execution of certain code. This is a relatively complex problem, which has been solved by others before. Your answer provides a poor implementation of a troublesome solution, of which I pointed out a few flaws. You can say _"OP didn't ask for a complete solution"_, but that doesn't keep me from pointing out the flaws in your design. – CodeCaster Jan 22 '15 at 17:00
  • @codeCaster I gave a sample code how efficiently we can store and get Notifications. In my answer I have written that something like ... – Rajnikant Jan 22 '15 at 17:06
  • The problem is not to store a timestamp. The problem is to **reliably execute code at a predetermined time**, which your answer doesn't address properly. – CodeCaster Jan 22 '15 at 17:15
-2

This type of statement is difficult to automate, you have to either do a switch case for each day, and in each day do a switch case for each hour. The other way is to store a List of DateTime and if (list.Any(x => x.DayOfWeek == DateTime.UtcNow.DayOfWeek && x.Hour == DateTime.UtcNow.Hour) doStuff() you need to run this in a thread or a timer checking every x seconds.

Jonathan Camilleri
  • 611
  • 1
  • 6
  • 17