Hasan A Yousef Hasan A Yousef - 1 month ago 7
C# Question

Timer to exact time by seconds

I'm trying to run an infinite loop in parallel with the other code, that is executed once a specific time acheived by SECOND.
In the below code I'm trying to touch the exact midnight time, that is: 00 hh: 00 mm: 00 sec

using System; // for Date/Time
using System.Threading.Tasks; // for Parallel

public class Program
{
public static void Main(string[] args)
{
Parallel.Invoke(
() =>
{
Console.WriteLine("Begin first task...");
}, // close first Action

async () =>
{
Console.WriteLine("Begin second task...");
var midNight = "00:00:00";
while (true)
{
TimeSpan duration = DateTime.Parse(midNight).Subtract(DateTime.Now);
Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds",
duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

if(duration.Days >= 1)
await Task.Delay(8640000);
else if(duration.Hours >= 1)
await Task.Delay(360000);
else if(duration.Minutes >= 1)
await Task.Delay(60000);
else
await Task.Delay(1000);

if(duration == TimeSpan.Zero) {
Console.WriteLine("It is time... midnight is {0}", DateTime.Now);
} // close second Action `the async task`
) // end of Parallel.Invoke
} // End of Main
} // End of Program


I was able to the
await
statement work correctly to reach the required point, but the lock the condition
if(duration == TimeSpan.Zero)
never become
true

enter image description here

Answer

First of all your program can't work as you showed because your TimeSpan duration = DateTime.Parse(midNight).Subtract(DateTime.Now); never will be positive. Second you main function will return almost immediately. Anyway you can try my solution for your problem:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{

    public static void Main(string[] args)
    {
        Parallel.Invoke(
            () =>{Console.WriteLine("Begin first task...");},
            async () =>
            {
                Console.WriteLine("Begin second task...");
                DateTime startDate = DateTime.Today;
                while (true)
                {
                    TimeSpan duration = DateTime.Now.Subtract(startDate);
                    Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds", 
                                      duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

                    int delay = (int)(DateTime.Today.AddDays(1.0).Subtract(DateTime.Now).TotalMilliseconds/2);
                    await Task.Delay(delay>0?delay:0);

                    if(duration.Days >= 1) 
                    { 

                        Console.WriteLine("It is time... midnight is {0}", DateTime.Now);

                        startDate = DateTime.Today;
                    }

                }
            }
        );
    }
}

This will not provide you super precision as on normal PC you can't get it (you could use IRQ to get better accuracy but solution like that is far more complex).

Also as prof of concept link to 10s version.

At time version:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{

    public static void Main(string[] args)
    {
        Parallel.Invoke(
            async () =>
            {
                Console.WriteLine("Begin second task...");
                TimeSpan eventTime = new TimeSpan(0,18,16,53,123); //set when run event (ex. 18:16:53.123)
                DateTime endDate = DateTime.Today.Add(eventTime);
                if(endDate<DateTime.Now) endDate = endDate.AddDays(1.0);
                while (true)
                {
                    TimeSpan duration = endDate.Subtract(DateTime.Now);
                    Console.WriteLine("looping at: {0:00} Days, {1:00} Hours, {2:00} Minutes, {3:00} Seconds, {4:00} Milliseconds", duration.Days, duration.Hours, duration.Minutes, duration.Seconds, duration.Milliseconds);

                    if(duration.TotalMilliseconds <= 0.0)
                    { 
                        Console.WriteLine("It is time... {0}", DateTime.Now);

                        endDate = endDate.AddDays(1.0);
                        continue;
                    }

                    int delay = (int)(duration.TotalMilliseconds/2);
                    await Task.Delay(delay>0?delay:0);

                }
            }
        );
        Thread.Sleep(6000);
    }
}