0

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?

weston
  • 54,145
  • 21
  • 145
  • 203
Damien Flury
  • 769
  • 10
  • 23
  • 3
    how do you mean its 'kinda inaccurate'? – Simon Price Feb 20 '17 at 12:40
  • 1
    What is it what you need to accomplish?. – NicoRiff Feb 20 '17 at 12:40
  • It's not clear what you are trying to achieve. Do you simply want to display current datetime in 1 second intervals? – J. Tuc Feb 20 '17 at 12:42
  • 1
    Your problem lies in that sleeping for 1000, plus running some code isnt always spot on.. sleep(1000) isnt guaranteed as 1000, its around 1000.. plus, lets say you start at 995 of that 1000, you will always stay at 995 ms through that second. – BugFinder Feb 20 '17 at 12:42
  • @SimonPrice it's only perfectly accurate when the value of Thread.Sleep is 0. With 1000ms refresh rate there's a deviation between 0 and 1 seconds. – Damien Flury Feb 20 '17 at 12:43
  • Why dont you just use `Timer` object? – mrogal.ski Feb 20 '17 at 12:43
  • 1
    Why do you need this to be THAT exact? The code shown is enough for 99% of use cases – Pikoh Feb 20 '17 at 12:44
  • 1
    Are you familiar with the [Nyquist–Shannon sampling theorem](https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem)? Slightly paraphrased for this particular case: Define what the minimal sampling rate is, that you would consider "precise enough", then use twice that. – Manfred Radlwimmer Feb 20 '17 at 12:45
  • It isn't *perfectly* accurate with 0 either. Since you're only printing out whole seconds, you'll never get better accuracy than half a second (so you can see a "skipped" second once in a while). How accurate do you need it to be and why? – Luaan Feb 20 '17 at 12:46

3 Answers3

3

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();
    }
}
spender
  • 117,338
  • 33
  • 229
  • 351
1

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();
}
NicoRiff
  • 4,803
  • 3
  • 25
  • 54
  • Well, thanks. Timer is more accurate right? And doesn't interrupt the current thread? – Damien Flury Feb 20 '17 at 12:49
  • System.Timers.Timer (don´t confuse with other Timer classes) is run on system thread pool, so it won´t block your main thread. It always fire in the elapsed time that you declared, starting from the specific time that the Start() method is call. Example: if you start the method at 22:31:15.332 it will run again at 22:31:16.332, not at 22:31:16.000 – NicoRiff Feb 20 '17 at 12:54
1

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.

Community
  • 1
  • 1
Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47