Damien Flury
Damien Flury

Reputation: 778

Get current time on time change

I'm having following code to show the current time:

static void Main(string[] args)
{
    while (true)
    {
        ShowDate(GetTime(),GetDate());
        //Here's my issue:
        System.Threading.Thread.Sleep(1000);
    }
}

//Clear Console and write current Date again.
static void ShowDate(String time, String date)
{
    Console.Clear();
    Console.WriteLine(date);
    Console.WriteLine(time);
}
static string GetTime()
{
    string timeNow = DateTime.Now.ToString("HH:mm:ss");
    return timeNow;
}
static string GetDate()
{
    string dateNow = DateTime.Now.ToString("dd.MM.yy");
    return dateNow;
}

Well, in my understanding, Thread.Sleep(1000) only shows the time every second measured after start of the program. So it doesn't show the perfectly correct time. I could try a lower value for Thread.Sleep(), but that's still kinda inaccurate and probably becomes kind of inefficient. Is there any possible way to, for example, update the time every time the system itself updates its time? Something like an event listener maybe?

Upvotes: 0

Views: 1891

Answers (3)

spender
spender

Reputation: 120450

I'd approach this with a custom clock class that emits events every second. By measuring the remaining time before the next second is due to elapse, we can wait until that moment, then fire an event.

Leveraging async/await for the benefit it brings in code clarity and IDisposable for the clean-up, this might look something like this:

void Main()
{
    using(var clock = new Clock())
    {
        clock.Tick += dt => Console.WriteLine(dt);
        Thread.Sleep(20000);

    }

}
//sealed so we don't need to bother with full IDisposable pattern
public sealed class Clock:IDisposable
{
    public event Action<DateTime> Tick;
    private CancellationTokenSource tokenSource;
    public Clock()
    {
        tokenSource = new CancellationTokenSource();
        Loop();
    }
    private async void Loop()
    {
        while(!tokenSource.IsCancellationRequested)
        {
            var now = DateTime.UtcNow;
            var nowMs = now.Millisecond;
            var timeToNextSecInMs = 1000 - nowMs;
            try
            {
                await Task.Delay(timeToNextSecInMs, tokenSource.Token);

            }
            catch(TaskCanceledException)
            {
                break;
            }
            var tick = Tick;
            if(tick != null)
            {
                tick(DateTime.UtcNow);
            }
        }

    }
    public void Dispose()
    {
        tokenSource.Cancel();
    }
}

Upvotes: 3

Tatranskymedved
Tatranskymedved

Reputation: 4371

As You have written, this would refresh (write) time every second - 0, 1, 2, 3 .. But if the program would start in some middle time, it would go like 1.3, 2.3, 3.3

This is not always expected behaviour, but an Event for each time change, would be also consuming - You might maybe know there is existing some "system time", which is counted in the Ticks.

Tick happens each time processor jumps to next instruction and as it has some frequency, it can re-calculate the current time from it.

However .NET allows You to use some pre-build Timers, which can be run with precission of milisecond.

Example code as in here:

using System;
using System.Threading;

public static class Program
{
    public static void Main()
    {
        // Create a Timer object that knows to call our TimerCallback
        // method once every 2000 milliseconds.
        Timer t = new Timer(TimerCallback, null, 0, 2000);
        // Wait for the user to hit <Enter>
        Console.ReadLine();
    }

    private static void TimerCallback(Object o)
    {
        // Display the date/time when this method got called.
        Console.WriteLine("In TimerCallback: " + DateTime.Now);
        // Force a garbage collection to occur for this demo.
        GC.Collect();
    }
}

Important: You are using Thread.Sleep(), which will cause Thread to stopp all its work, this is really unsufficient way of delaying some activity and should be used minimally. There are some special ocasions, where it is really useable, but this is surely not the one.

Upvotes: 1

NicoRiff
NicoRiff

Reputation: 4883

Instead of using a Thread.Sleep to stop the Thread execution, I think you´ll be better using a conventional Timer:

static void Main(string[] args)
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);
    timer.Elapsed += Tim_Elapsed;
    timer.Start();
}

private void Tim_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    ShowDate();
}

Upvotes: 1

Related Questions