Run once a day in C#
Asked Answered
B

12

19

Is there any clever method out there to make my executeEveryDayMethod() execute once a day, without having to involve the Windows TaskScheduler?

Biometrics answered 11/11, 2008 at 10:44 Comment(1)
Is your application constantly running? Is it a service? Could you give a bit more info about these pointsApache
A
10

Take a look at quartz.net. It is a scheduling library for .net.

More specifically take a look here.

Amabel answered 11/11, 2008 at 12:40 Comment(2)
"After more than two years of development, bug fixing and new features Quartz.NET has finally matured to version 1.0." I knew scheduling is a lot harder than it looks at first sight. Two years and for a clone!Foresight
Everyone forgot to mention that it is totally free with no GPL restrictions (Apache license :o)Fording
M
18

I achieved this by doing the following...

  1. Set up a timer that fires every 20 minutes (although the actual timing is up to you - I needed to run on several occasions throughout the day).
  2. on each Tick event, check the system time. Compare the time to the scheduled run time for your method.
  3. If the current time is less than the scheduled time, check a in some persistent storage to get the datetime value of the last time the method ran.
  4. If the method last ran more than 24 hours ago, run the method, and stash the datetime of this run back to your data store
  5. If the method last ran within the last 24 hours, ignore it.

HTH

*edit - code sample in C# :: Note : untested...

using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Timer t1 = new Timer();
            t1.Interval = (1000 * 60 * 20); // 20 minutes...
            t1.Elapsed += new ElapsedEventHandler(t1_Elapsed);
            t1.AutoReset = true;
            t1.Start();

            Console.ReadLine();
        }

        static void t1_Elapsed(object sender, ElapsedEventArgs e)
        {
            DateTime scheduledRun = DateTime.Today.AddHours(3);  // runs today at 3am.
            System.IO.FileInfo lastTime = new System.IO.FileInfo(@"C:\lastRunTime.txt");
            DateTime lastRan = lastTime.LastWriteTime;
            if (DateTime.Now > scheduledRun)
            {
                TimeSpan sinceLastRun = DateTime.Now - lastRan;
                if (sinceLastRun.Hours > 23)
                {
                    doStuff();
                    // Don't forget to update the file modification date here!!!
                }
            }
        }

        static void doStuff()
        {
            Console.WriteLine("Running the method!");
        }
    }
}
Manuel answered 11/11, 2008 at 11:4 Comment(2)
Clbluttic problem with Timer and elapsed... The check to see when it should first run has been excluded, or there is no invocation of the doStuff() method before the timer starts ticking. If you have a 24 hour wait, you will see the programs as 'dead' for a day.Chopper
Good point! As I said, it wasn't tested and I had completely forgotten to implement the 'first run' behaviour. My bad!Manuel
A
10

Take a look at quartz.net. It is a scheduling library for .net.

More specifically take a look here.

Amabel answered 11/11, 2008 at 12:40 Comment(2)
"After more than two years of development, bug fixing and new features Quartz.NET has finally matured to version 1.0." I knew scheduling is a lot harder than it looks at first sight. Two years and for a clone!Foresight
Everyone forgot to mention that it is totally free with no GPL restrictions (Apache license :o)Fording
F
4

If the time when it is run is not relevant and can be reset each time the program starts you can just set a timer, which is the easiest thing to do. If that's not acceptable it starts getting more complex, like the solution presented here and which still doesn't solve the persistence problem, you need to tackle that separately if you truly wish to do what Scheduled Tasks would. I'd really consider again if it's worth going through all the trouble to replicate a perfectly good existing functionality.

Here's a related question (Example taken from there).

using System;
using System.Timers;

public class Timer1
{
    private static Timer aTimer = new System.Timers.Timer(24*60*60*1000);

    public static void Main()
    {
        aTimer.Elapsed += new ElapsedEventHandler(ExecuteEveryDayMethod);
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program.");
        Console.ReadLine();
    }

    // Specify what you want to happen when the Elapsed event is 
    // raised.
    private static void ExecuteEveryDayMethod(object source, ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
}
Foresight answered 11/11, 2008 at 10:55 Comment(0)
R
1

public partial class Main : Form {
public Main( ) // Windows Form is called Main { InitializeComponent( ); }

    private void Main_Load( object sender, EventArgs e )
    {
        /*
         This example uses a System.Windows.Forms Timer

         This code allows you to schedule an event at any given time in one day.
         In this example the timer will tick at 3AM.

         */
        Int32 alarm = GetAlarmInMilliseconds( 3, 0, 0 ); // Milliseconds until 3:00 am.
        timer_MessageCount.Interval = alarm; // Timer will tick at 3:00am.

        timer_MessageCount.Start( );                    
    }

    private Int32 GetAlarmInMilliseconds(Int32 eventHour, Int32 eventMinute, Int32 eventSecond )
    {
        DateTime now = DateTime.Now;
        DateTime eventTime = new DateTime( now.Year, now.Month, now.Day, eventHour, eventMinute, eventSecond );

        TimeSpan ts;

        if ( eventTime > now )
        {
            ts = eventTime - now;
        }
        else
        {
            eventTime = eventTime.AddDays( 1 );
            ts = eventTime - now;
        }

        Console.WriteLine("Next alarm in: {0}", ts );

        return ( Int32 ) ts.TotalMilliseconds;
    }        

    static void DoSomething( )
    {
        Console.WriteLine( "Run your code here." );
    }      

    private void timer_MessageCount_Tick( object sender, EventArgs e )
    {
        DoSomething( );

        Int32 alarm = GetAlarmInMilliseconds( 3, 0, 0 ); // Next alarm time = 3AM
        timer_MessageCount.Interval = alarm;            
    }
}
Rennold answered 8/6, 2020 at 22:49 Comment(0)
C
1

Suppose you have the daily time in _Settings.DataCleanupTime in Format "hh:mm:ss"

  //note the namespace, there are 4 different timers in .NET
  System.Threading.Timer _Timer;

  DateTime now = DateTime.Now;
  //convert "hh:mm:ss" to three integers
  var dateparts = _Settings.DataCleanupTime.Split(new char[] { ':' }).Select(p => Convert.ToInt32(p)).ToArray();
  DateTime firstTime = new DateTime(now.Year, now.Month, now.Day, dateparts[0], dateparts[1], dateparts[2]);
  
  //e.g. firsttime is today at 2am and it is already 6am
  if(firstTime < now)
  {
    //first run will be tomorrow
    firstTime = firstTime.AddDays(1);
  }

  int delay = Convert.ToInt32((firstTime - now).TotalMilliseconds);

  _Timer = new Timer(DoWork, state:null, delay, 3600 * 24 * 1000);

The signature of DoWork is:

public void DoWork(Object state)

To stop the timer just call:

_Timer.Dispose();
Chartulary answered 14/11, 2022 at 9:7 Comment(1)
Note that this (and other solutions using a fixed 24h) will not consistently trigger at the same time during a DST change. But I like the simplicity of this solution, executing the job only when needed and not polling every 20min or so. I've slightly adapted this so time until the next job get recalculated upon each execution. To avoid these DST issue's.Pagepageant
A
0

You could query time and run if your within some time frame, that way even if the machine goes off you'll call the method or use a timer like Vinko's suggestion.

But the better solution (akin to older CRON versions, so its a proven pattern) is to have some persistent data, with the cheapest solution I can think of right now being a blank file, check its last modified attribute, and if it hasn't been modified within the last 24 hours you touch it and run your method. This way you assure the method gets run first thing in the case the application is out for the weekend for example.

I've done this in C# before, but its was a year ago at another Job, so I don't have the code but it was about 20 lines (with comments and all) or so.

Apache answered 11/11, 2008 at 10:55 Comment(0)
T
0

To run the job once daily between 7 and 8pm, i set up a timer with interval = 3600000 ms and then just execute the following code for timer tick.

private void timer1_Tick(object sender, EventArgs e)
{
    //ensure that it is running between 7-8pm daily.
    if (DateTime.Now.Hour == 19)
    { 
        RunJob(); 
    }
 }

An hour window is fine for me. Extra granularity on time will require a smaller interval on the timer (60000 for a minute) and including minutes on the if.

eg

{
    //ensure that it is running at 7:30pm daily.
    if (DateTime.Now.Hour == 19 && DateTime.Now.Minute == 30)
    { 
        RunJob(); 
    }
 }
Terryl answered 29/10, 2014 at 3:28 Comment(1)
You have to add a Thread.Sleep(60 * 1000) in RunJob() ortherwise you will have several instances of RunJob(), once every second or moreTaratarabar
C
0

If you only want to run it once a day and don't care when, this will work (will run just after midnight).

Declare a DateTime variable:

DateTime _DateLastRun;

In your startup, set the initial date value:

_DateLastRun = DateTime.Now.Date;

In the logic area where you want to check whether to perform the action:

if (_DateLastRun < DateTime.Now.Date) 
{
    // Perform your action
    _DateLastRun= DateTime.Now.Date;
}
Cullet answered 17/7, 2018 at 20:44 Comment(0)
M
0

You can try this solution.

    public Main()
    {
        StartService();
    }

    public async Task StartService(CancellationToken token = default(CancellationToken))
    {
        while (!token.IsCancellationRequested)
        {
            ExecuteFunction();
            try
            {
                await Task.Delay(TimeSpan.FromDays(1), token);
            }
            catch (TaskCanceledException)
            {
                break;
            }
        }
    }

    public async Task ExecuteFunction()
    {
        ...
    }
Maurine answered 22/6, 2020 at 11:10 Comment(0)
W
0

Here is how you can do it if you're running a Windows Forms Application. But you need to configure a setting so that you can store the last date the event was fired. If you never intend to close the app you can just store the date as a static value.

Im using a timer to fire the event, as following:

        private void tmrAutoBAK_Tick(object sender, EventArgs e)
        {
            if (BakDB.Properties.Settings.Default.lastFireDate != DateTime.Now.ToString("yyyy-MM-dd"))
            {
                 tmrAutoBAK.Stop(); //STOPS THE TIMER IN CASE OF EVENTUAL MESSAGEBOXES.
                 createBakup(); //EVENT
                 BakDB.Properties.Settings.Default.lastFireDate = DateTime.Now.ToString("yyyy-MM-dd"); //STORING CURRENT DATE TO SETTINGS FILE.
                 BakDB.Properties.Settings.Default.Save(); //SAVING THE SETTING FILE.
                 tmrAutoBAK.Start(); //RESTARTING TIMER
            }
        }
Wagoner answered 19/11, 2020 at 12:42 Comment(1)
You should use DateTime.UtcNow to avoid daylight saving time issues.Alleman
S
0

This is my very simple solution to execute a method once once a day:

private static DateTime _LastAccessedTime;
    
private static void OnceADayCode()  // method that you want access once a day
{
    _LastAccessedTime = DateTime.Today;
}

public static void PublicMethod()  // this can be a method called from outside
{
    if (_LastAccessedTime != DateTime.Today) 
    { 
        OnceADayCode(); 
    }
}

with the same logic you can also use:

private static DateTime _LastAccessedTime;
    
private static void OnceADayCode()
{
    if (_LastAccessedTime.Today != DateTime.Today) 
    { 
        // code that you want access once a day
        _LastAccessedTime = DateTime.Today; 
    }
}
Splinter answered 16/12, 2022 at 11:44 Comment(0)
F
0

Just for reference, here's how to run your executeEveryDayMethod() once a day at noon by calculating the hours left from the current hour and using Task.Delay in the context of a BackgroundService:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    do
    {
        // wait until it's noon                
        int timeToWait = 12 - DateTime.Now.Hour;

        // DateTime.Now.Hour is in 24h. 
        int numberOfHours = (timeToWait < 0) ? 24 - Math.Abs(timeToWait) : timeToWait;

        if (timeToWait == 0)
        {
            executeEveryDayMethod();
            numberOfHours = 24;
        }

        await Task.Delay(TimeSpan.FromHours(numberOfHours), stoppingToken);
    }
    while (!stoppingToken.IsCancellationRequested);
}
Fino answered 29/10, 2023 at 22:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.