Accurate method to track time - c#

For my application, I've to track the time change, to "smooth" the time change.
A system time change can occurs for several reasons:
The user change its system time
The OS NTP Server updates the local time
...
So actually we have a "TimeProvider", which provide to the whole application the current time.
The goal is to detect if a time shift occurs and correct our local time smoothly(like, if we have a "time jump" of one hour, correct 100ms every second until this is fully corrected).
Here is basically what I've to provide the time(please note that currently I absolutely don't smooth the time change, but not my current issue)
internal class TimeChange : IDisposable
{
private readonly Timer _timer;
private readonly Stopwatch _watch = new Stopwatch();
private DateTime _currentTime;
public DateTime CurrentTime
{
get { return _currentTime + _watch.Elapsed; }
}
public TimeChange()
{
_timer = new Timer(1000);
_timer.Elapsed += OnTimerElapsed;
_timer.Start();
_watch.Start();
_currentTime = DateTime.UtcNow;
}
public void Dispose()
{
_timer.Stop();
_timer.Elapsed -= OnTimerElapsed;
}
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
DateTime currentTime = DateTime.UtcNow;
TimeSpan timeDerivation = currentTime - _currentTime - _watch.Elapsed;
_watch.Restart();
_currentTime = currentTime;
Console.WriteLine("Derivation: " + timeDerivation.TotalMilliseconds + "ms");
}
}
But when doing some test, I noticed that I've differences even without doing anything on my local time. Not huge differences(<1ms), but still:
Press enter to stop
Derivation: -0.1367ms
Derivation: 0.9423ms
Derivation: 0.0437ms
Derivation: 0.0617ms
Derivation: 0.0095ms
Derivation: 0.0646ms
Derivation: -0.0149ms
And this is derivation for 1 second, if I just replace the 1000ms by 10000ms, I quickly have a time derivation between 1ms and 0.5ms.
So my question(Finally :P):
Why between two Utc.DateTime gave me so much differences? They are both based on clock Tick no?
Isn't there a way to get this time shift more precisely ?

No they are not both based on clock tick. Stopwatch maybe either high or low res. If low res, then it uses DateTime.UtcNow underneith. Unfortuantely you cannot choose if it's high or low, so:
Create own "Stopwatch" that always uses DateTime.UtcNow underneith.
EDIT
That's a stupid suggestion in (2.), you obviously need to avoid DateTime.UtcNow as that's what you are trying to correct. I suggest you look at working in ticks, by which I mean 1/10000 of a second, to match high-res Stopwatch. This is because TimeSpan is only accurate to 1/1000 of a second.
Number 1. in more detail:
Stopwatch uses this method:
public static long GetTimestamp()
{
if (!Stopwatch.IsHighResolution)
{
DateTime utcNow = DateTime.UtcNow;
return utcNow.Ticks; //There are 10,000 of these ticks in a second
}
else
{
long num = (long)0;
SafeNativeMethods.QueryPerformanceCounter(out num);
return num; //These ticks depend on the processor, and
//later will be converted to 1/10000 of a second
}
}
But like I say, IsHighResolution appears to be not settable and as a static applies system wide anyway, so write your own.

Related

Can I change a timer's interval without resetting it?

I have a timer that is supposed to generate a Console.Beep at the rate of my Heart Rate in beats/minute. I am using the following simple formula to calculate the Timer.Interval in my C# timer:
ServerVarValues = new ws ServerVarValues();
this.timerHeartBeatSound = new System.Windows.Forms.Timer(this.components);
public class ServerVarValues
{
public int HR;
public DateTime LastHRUpdate;
public int SpO2;
public DateTime LastO2Update;
public double Temperature;
public DateTime LastTempUpdate;
}
//... I plug in my heart rate to calculate my new timer interval...
int tVal = (int)Math.Round((ws.HR / 60.0) * 1000,0);
timerHeartBeatSound.Interval = tVal;
// and with the new interval, the timer generates a Tick Event that runs this
private void PlayHeartBeatSound(object sender, EventArgs e)
{
Console.Beep();
}
I am reading my heart rate from a wearable. The problem is that every time that my HR changes, the timer is reset when I change the timer's interval. Thus, I hear hiccups often instead of a smooth changing heart rate sound.
Any ideas on how to avoid resetting the timer every time the heart rate changes?
Yes, this is by design. When you change the Interval property, and it is not the same, then the Timer resets itself. You cannot alter this behavior.
So what you must do is not update Interval. Not until the next Tick happens. Which is fine, the heart-beats happen quickly enough. You'll need another variable:
public class ServerVarValues {
public int NextInterval;
// etc...
}
And initialize it when you start the timer or update the heart-rate value:
...
int tVal = (int)Math.Round((ws.HR / 60.0) * 1000,0);
if (timerHeartBeatSound.Enabled) ws.NextInterval = tval;
else {
ws.NextInterval = 0;
timerHeartBeatSound.Interval = tval;
}
And in the Tick event handler you need to check if you have to make the new interval effective:
private void PlayHeartBeatSound(object sender, EventArgs e)
{
if (ws.NextInterval != 0) timerHeartBeatSound.Interval = ws.NextInterval;
ws.NextInterval = 0;
Console.Beep();
}
Try the following, except instead of "ASecond" use the number of milliseconds corresponding to the current heart rate. This will recalculate the number of milliseconds until the next beat, based on the current rate and do it for every beat. I hope it is smoother.
DateTime LastTime = DateTime.Now;
...
DateTime NextTime = LastTime + ASecond;
// Just in case we have gone past the next time (perhaps you put the system to sleep?)
if (DateTime.Now > NextTime)
NextTime = DateTime.Now + ASecond;
TimeSpan Duration = NextTime - DateTime.Now;
LastTime = NextTime;
timer1.Interval = (int)Duration.TotalMilliseconds;

How to ignore the passage of time while debugger has code paused?

I have calculations I'm performing which I need to timeout if it runs too long. I might set a timeout of 5 seconds, and do periodic polls throughout my code. Actual code is much more complex, and has heavy recursion and is spread over many classes, but this should give a general idea of how it works. Basically any time something calls recursion or performs something that might take time, it calls AssertTimeout().
private DateTime endTime;
public void PerpareTimeout(int timeoutMilliseconds)
{
endTime = DateTime.UtcNow.AddMilliseconds(timeoutMilliseconds);
}
public void AssertTimeout()
{
if (DateTime.UtcNow > endTime)
throw new TimeoutException();
}
public void DoWork1()
{
foreach (var item in workItems)
{
AssertTimeout();
DoWork2(item)
}
}
public void DoWork2(WorkItem item)
{
foreach (var item in workItems)
{
AssertTimeout();
// ...
}
}
So the problem comes in when I have a debugger attached and I pause execution. I want to somehow disable the timeout for the amount of time paused. So if it runs for 2 seconds, I hit a breakpoint and wait 5 minutes, and then resume, it will run 3 more seconds after resuming execution before timing out.
I could use something like this:
public void PrepareTimeout(int timeoutMilliseconds)
{
if (System.Diagnostics.Debugger.IsDebuggerAttached)
endTime = DateTime.MaxValue;
else
endTime = DateTime.UtcNow.AddMilliseconds(timeoutMilliseconds);
}
But that's basically giving an infinite timeout whenever the program is running in a debug environment, and I want it to time out normally if I don't pause it.
Is there any way to measure time elapsed without counting the time spent paused by the debugger?
After seeing Matias Cicero's answer, I had an idea:
If debugger is attached, start a watchdog thread which does nothing but sleep. Maybe in 100 millisecond increments. If the program becomes paused, the call to Thread.Sleep will take longer than expected, and it can increment the end time by the difference.
public class DebugWatchDog : IDisposable
{
private bool Terminated;
private Action<DateTime> updateEndTime;
private DateTime endTime;
public DebugWatchDog(Action<DateTime> updateEndTime, DateTime endTime)
{
if (!System.Diagnostics.Debugger.IsDebuggerAttached)
return;
this.updateEndTime = updateEndTime;
this.endTime = endTime;
updateEndTime(DateTime.MaxValue);
var thread = new Thread(Watch);
thread.Start();
}
public void Dispose()
{
lock (this)
Terminated = true;
}
private void Watch()
{
DateTime priorTime = DateTime.UtcNow;
while (true)
{
lock (this)
if (Terminated)
return;
Thread.Sleep(100);
DateTime nextTime = DateTime.UtcNow;
var diff = nextTime - priorTime;
if (diff.TotalMilliseconds > 115)
{
endTime += diff;
}
if (DateTime.UtcNow > endTime)
{
updateEndTime(endTime);
return;
}
priorTime = nextTime;
}
}
}
There is a not so nice workaround.
You have to use Stopwatch instead of calculating the difference between present time and start time.
Let's say you have the following piece of code:
Stopwatch watch = Stopwatch.StartNew();
DoSomething();
DoAnotherThing(); <-- You want to add a breakpoint here
DoFinalThings();
watch.Stop();
You can achieve what you want adding three lines of code before the line you want to breakpoint:
Stopwatch watch = Stopwatch.StartNew();
DoSomething();
watch.Stop(); // Stops measuring time
Debugger.Break(); // Programatically breaks the program at this point
watch.Start(); // Resumes measuring time
DoAnotherThing();
DoFinalThings();
watch.Stop();
Ideally you would want some events to be fired when the code is paused. Since the Debugger doesn't fire an event then a workaround is needed as mentioned by Matias.
His solution requires that you know the exact point at which you should start/stop a Stopwatch and I will build on that. The downside to this solution is that it requires constant polling at a high enough resolution for your needs (which could be detrimental to the rest of your application running thereby affecting whatever numbers you are trying to retrieve out of the timer).
The ultimate aim is that we will do the calculation:
"Running time paused" = "Total time taken" - "Total time paused"
We will try to keep track of when the last time the program was actively running, and then find out when the program next actively runs after it resumes from debug.
public static class ProgramTimer
{
public static int resolution = 1000; //resolution of time needed in ms
private static System.Diagnostics.Stopwatch stopwatch;
private static System.Timers.Timer pollingTimer;
private static System.TimeSpan lastMeasuredDebugTimespan;
public static void Start()
{
pollingTimer = new System.Timers.Timer();
pollingTimer.Interval = resolution;
pollingTimer.Elapsed += timerEvent;
}
private static void timerEvent()
{
if (System.Diagnostics.Debugger.IsDebuggerAttached) {
if (stopwatch == null) {
stopwatch = System.Diagnostics.Stopwatch.StartNew();
}
} else {
if (stopwatch != null) {
stopwatch.Stop();
lastMeasuredDebugTime = stopwatch.Elapsed.TotalMilliseconds;
stopwatch = null;
}
}
}
public static System.TimeSpan getTimespanInDebug()
{
if (lastMeasuredDebugTimespan) {
return lastMeasuredDebugTimespan;
}
return null;
}
}
In PrepareTimeout you would call:
ProgramTimer.Start()
And in AssertTimeout:
public void AssertTimeout()
{
DateTime newEndTime;
newEndTime = endTime;
if (ProgramTimer.getTimespanInDebug()) {
newEndTime = endTime.Subtract(ProgramTimer.getTimespanInDebug());
}
if (DateTime.UtcNow > newEndTime)
throw new TimeoutException();
}
Hope that helps!

Timer resets after 60 seconds

Below is the code that I'm attempting to use as an elapsed timer on a desktop task timer that we're building. Right now when this runs it only counts to 60 seconds and then resets and doesn't ever add to the minutes.
//tick timer that checks to see how long the agent has been sitting in the misc timer status, reminds them after 5 mintues to ensure correct status is used
private void statusTime_Tick(object sender, EventArgs e)
{
counter++;
//The timespan will handle the push from the elapsed time in seconds to the label so we can update the user
//This shouldn't require a background worker since it's a fairly small app and nothing is resource heavy
var timespan = TimeSpan.FromSeconds(actualTimer.Elapsed.Seconds);
//convert the time in seconds to the format requested by the user
displaycounter.Text=("Elapsed Time in " + statusName+" "+ timespan.ToString(#"mm\:ss"));
//pull the thread into updating the UI
Application.DoEvents();
}
Quick Fix
I believe the problem is that you are using Seconds which is 0-59. You want to use TotalSeconds with your existing code:
var timespan = TimeSpan.FromSeconds(actualTimer.Elapsed.TotalSeconds);
Comments
However, this doesn't make a lot of sense as you could just use the TimeSpan object directly:
var timespan = actualTimer.Elapsed;
Also, I can't see all your application, but I would expect you do not need to call Application.DoEvents();. As the UI should update automatically when it has the chance... if it doesn't then you want to look at moving whatever code is blocking the UI to a different thread.
Recommendation
With all that said, I would recommend you don't use a timer to track elapsed time at all. Timers can lose accuracy over time. The best approach is to store the current system time when you start the process, then when you need to display the 'timer' do an on-demand calculation at that point.
A very simple example to help explain what I mean:
DateTime start;
void StartTimer()
{
start = DateTime.Now;
}
void UpdateDisplay()
{
var timespan = DateTime.Now.Subtract(start);
displaycounter.Text = "Elapsed Time in " + statusName + " " + timespan.ToString(#"mm\:ss"));
}
You could then use a timer to call your UpdateDisplay method at regular intervals:
void statusTime_Tick(object sender, EventArgs e)
{
UpdateDisplay();
}

reset the timer on second call

I am bit New here ...and learning Threading.Timer ... I have a timer in Window Service which fires a function at 16:48:00 ..I just want to Fire the Timer Again at 21:00:00 PM ...then 22:00:00 ...there is no fix timer interval between the timeslots
here is My Code what I have Tried:
public partial class ASMSService1 : ServiceBase
{
private Timer myTimer;
private DateTime inputDate;
private DateTime yesterday;
private DateTime today;
public ASMSService1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
inputDate = DateTime.Today;
today = DateTime.Today;
yesterday = today.AddDays(-1);
//time is Set here
SetTimer(16, 48, 00);
}
private void SetTimer(int hours, int minutes, int seconds)
{
inputDate = DateTime.Today.AddHours(hours).AddMinutes(minutes).AddSeconds(seconds);
String date1 = inputDate.TimeOfDay.ToString();
if (DateTime.Now > inputDate)
{
inputDate = inputDate.AddDays(1);
}
if (date1.CompareTo("16:48:00") == 0)
{
myTimer = new System.Threading.Timer(new TimerCallback(FirstSlotOperations));
myTimer.Change((int)(inputDate - DateTime.Now).TotalMilliseconds, Timeout.Infinite);
}
}
private void FirstSlotOperations(object e)
{
//some operation
//Need to reset the Timer at 21:00:00
}
}
I have found the timer.change From MSDN article ..But I am not sure How to implement it in my case
I have Tried Thread.sleep()..But I am looking for some alternative also if possible ..
any suggestion would be Helpful
I think Artyom Kharlamov is right, if you provide more details about what specific functionality do you intend to achieve, there is a better chance to get an answer that is closer to what you need.
My take on what you have currently presented:
If you want the service to perform a specific operation whenever the time of the day is equal to a predefined value (for example 16:48 as per your provided code), i think your approach is kind of complicated since you could achieve this easier by using Scheduled Tasks, which will also cover the case in which you're trying to perform an operation within your service not on a specific time of the day but every x amount of time.
The timer object doesn't seem to expose any property or method that will tell you how much time has ellapsed since it first started ticking, for that you can use properties and get the current time whenever SetTimer is called, set a property in the ASMSService1 class and get the difference with the current time when FirstSlotOperations is called.
Maybe this isn't exactly what you want to do but unless you get a little more specific about your expectations it's hard to address them efficiently.
You don't need any object to get current time. Just use:
private void FirstSlotOperations(object e)
{
DateTime current = DateTime.Now;
}
An object passed as an argument in callback function is timer object so you can't get data from it. You can use it only to operate on timer object for example.
private void FirstSlotOperations(object e)
{
Timer t = e as Timer;
t.Change(1000, 0);
}

Synchronizing a service with a timer

I'm trying to write a service in c# that should be run on a given interval (a timeout) from a given date. If the date is in the future the service should wait to start until the date time is reached.
Example:
If I set a timeout to be 1 hour from 21:00:00 I want the program to run every hour
If I set a timeout to be 1 hour from 3999.01.01 21:00:00 I want the program to until date and from then run each hour
I have sort of achieved that with the following code, but it has some problems!
When I install the service (with installutil) the service is marked as starting because of the 'Thread.Sleep()'. This service appears to be hanging and is "installing" until started.
The code inside 'ServiceTimer_Tick()' might take longer than the expected timeout. How can I prevent the timer stack from increasing if that happens?
Alternatives I've thought of :
include using the 'timeout.Interval' first time and then resetting it subsequent calls, but it doesn't feel right.
I've also considered ditching the entire service idea and compile it as a executable and set up a scheduled tasks.
Shortened example:
public Service()
{
_timeout = new TimeSpan(0,1,0,0);
_timer = new System.Timers.Timer();
_timer.Interval = _timeout.TotalMilliseconds;
_timer.Elapsed += new ElapsedEventHandler(ServiceTimer_Tick);
}
private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
lock (_obj)
{
// Stuff that could take a lot of time
}
}
public static void Main()
{
Run(new Service());
}
protected override void OnStart(string[] args)
{
long current = DateTime.Now.Ticks;
long start = new DateTime(2010,9,15,21,0,0).Ticks;
long timeout = _timeout.Ticks;
long sleep;
if (current > start)
sleep = timeout - ((current % timeout)) + (start % timeout);
else
sleep = start - current;
Thread.Sleep(new TimeSpan(sleep));
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Start();
}
This is easier with a System.Threading.Timer. You can tell it how long to wait before the first tick, and then how often to tick after that.
So, if you wanted to wait 2 days before starting, and then do something once per hour, you'd write:
Timer MyTimer = new Timer(TimerCallback, null, TimeSpan.FromHours(48), TimeSpan.FromHours(1));
That said, if this is something that only has to run once per hour, then it sounds like what you really want is an executable that you then schedule with Windows Task Scheduler.
You can use a System.Threading.Timer. It supports both a dueTime and a period which is just what you need.
you have to move the timer logic to a separate thread that you spawn from your OnStart routine. Then your logic cannot interfere with the SCM and the service will start normally.
Edit: Just to elaborate - for this task I don't think timers work very well, since you are not taking clock corrections into account which could lead to a skew (or even be incorrect if the user manually changes the clock time). That's why comparing to the clock time in small intervals is imo preferred.
The Run routine of that thread could look like this:
public void run()
{
while (processing)
{
//initiate action on every full hour
if (DateTime.Now.Second == 0 && DateTime.Now.Minute == 0)
{
//Do something here
DoSomething();
//Make sure we sleep long enough that datetime.now.second > 0
Thread.Sleep(1000);
}
Thread.Sleep(100);
}
}

Categories

Resources