I'm using WinForm's. I have a label in my form that should count down from 0:20 seconds. to 0:00 seconds. I'm trying to do this here, but the compiler gives me an error.
Error: Cannot convert from 'int' to 'System.TimeSpan'
Why cant I use timespan.Subtract()? and how could I count down from 0:20 to 0:00 seconds?
private void timer1_Tick(object sender, EventArgs e)
{
TimeSpan timespan = TimeSpan.FromSeconds(20);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Time_label.Text = timespan.Subtract(stopwatch.Elapsed.Seconds);
}
A better approach for a simple second counter would be to make use of the Timer itself.
private readonly Timer _timer;
private TimeSpan _timespan;
private readonly TimeSpan _oneSecond;
public Form1()
{
InitializeComponent();
_timer = new Timer();
_timer.Tick += timer1_Tick;
_timer.Interval = 1000;
_timespan = TimeSpan.FromSeconds(20);
_oneSecond = new TimeSpan(0, 0, 0, 1);
_timer.Start();
}
private void timer1_Tick(object sender, EventArgs eventArgs)
{
if (_timespan >= TimeSpan.Zero)
{
Time_label.Text = _timespan.ToString(#"m\:ss");
_timespan = _timespan.Subtract(_oneSecond);
}
else
{
_timer.Stop();
}
}
stopwatch.Elapsed.Seconds returns and int, specifically, the number of seconds. timespan.Subtract(TimeSpan) accepts a TimeSpan object.
You can try:
Time_label.Text = 20 - stopwatch.Elapsed.Seconds;
or
Time_label.Text = timespan.Subtract(stopwatch.Elapsed).Seconds;
Please note there is a flaw in your logic. You restart a new stopwatch everytime you fire the tick event, so everytime it fires you have a new 0:00 stopwatch and you will get 19 or 20 in the textbox.
Instantiate your stopwatch elsewhere so it is the same between ticks.
EDIT:
as suggested by a Quantic's comment, if you plan on having more than a minute worth of seconds
Time_label.Text = (int)timespan.Subtract(stopwatch.Elapsed).TotalSeconds;
TimeSpan.Subtract expects another TimeSpan struct. The Stopwatch.Elapsed.Seconds is an Int32 struct. There isn't any built in implicit conversion to convert an Int32 to TimeSpan. You could try this
Time_label.Text = timespan.Subtract(TimeSpan.FromSeconds(stopwatch.Elapsed.seconds)).ToString();
TimeSpan.Subtract expects that you subtract another instance of TimeSpan from it (TimeSpan in itself is not bound to a specific time unit, so by subtracting say "15" it doesn't "know" what unit you have in mind).
What you want is either
Time_label.Text = Timespan.Subtract(TimeSpan.FromSeconds(stopwatch.Elapsed.Seconds)));
which produces a rather nifty preformatted
00:00:20
Or (taking advantage of the fact that Stopwatch's Elapsed is a TimeSpan itself)
Time_label.Text = timespan.Subtract(stopwatch.Elapsed);
But that produces
00:00:19.9999765
which is probably too precise to display to an end user (it's caused by Stopwatch being precise down-to-a-tick).
Related
My C# application has to execute a task every few seconds. It is very important that the execution happens at exactly this interval; give or take a few milliseconds.
I tried using a Timer but the time gradually shifts after a few minutes.
The code used by me is as follows:
System.Timers.Timer timerObj = new System.Timers.Timer(10 * 1000);
timerObj.Elapsed += timerObj_Elapsed;
timerObj.AutoReset = true;
timerObj.Start();
static void timerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
DateTime currentTime = DateTime.Now;
Console.WriteLine(currentTime.ToString("HH:mm:ss.fff"));
}
Is there a better way to do this kind of activity?
If it's really important to be that precise, set your timer's interval to something smaller than the max number of milliseconds by which you can be off. (Hopefully this will be greater than 15ms, as that's the resolution of System.Timers.Timer.) Then, in the tick handler, check whether the appropriate amount of time has passed and, if so, call the "real" handler. If your goal is to avoid drift, your test of whether it's time to fire should be based on the time elapsed since starting the timer, not the time elapsed since the last "tick."
You could try to always schedule a singelshot timer via AutoReset = false and calculate the delta on which the timer should fire. This should compensate your skew as it calculates the delta from the absolute time. Here a rough example:
// member variables
DateTime firstSchedule = DateTime.UtcNow;
var numElapsed = 1;
constructor()
{
this.timerObj = new System.Timers.Timer();
timerObj.Interval = CalcDelta();
timerObj.Elapsed += timerObj_Elapsed;
timerObj.AutoReset = false;
timerObj.Start();
}
void timerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.numElapses++;
this.timerObj.Interval = CalcDelta();
this.timerObj.Start();
DateTime currentTime = DateTime.Now;
Console.WriteLine(currentTime.ToString("HH:mm:ss.fff"));
}
private long CalcDelta()
{
DateTime nextSchedule = firstSchedule + TimeSpan.FromSeconds(numElapses * 10);
return (nextSchedule - DateTime.UtcNow).TotalMilliseconds;
}
private void setTimerRepeat(object sender, DoWorkEventArgs e){
DateTime begin = DateTime.Now;
bool isRunning = true;
int sleep=500;
while(isRunning){
int milliSeconds = DateTime.Now.Subtract(begin).TotalMilliSeconds;
if(milliSeconds > 9000){
sleep=10;
}else{
sleep=500;
}
if(milliSeconds=>10000){//if you get drift here, it should be consistent - adjust firing time downward to offset drift (change sleep to a multiple such that sleep%yourNumber==0)
begin = DateTime.Now;
Task.Run(()=>fireEvent());
}
Thread.Sleep(sleep);
}
}
}
I'm using a DispatcherTimer to call a void in C#:
counter = new System.Windows.Threading.DispatcherTimer();
counter.Tick += new EventHandler(counter_Tick);
counter.Interval = new TimeSpan(0, 0, 1);
counter.Start();
However I would like the interval to be a few milliseconds, TimeSpan's parameters are hours,minutes,seconds and only accepts integers (as far as I can see). Is there a work around for DispatcherTimer? I've tried Timer but I cannot use it (missing reference apparently)
Thanks in advance.
Another way
counter.Interval = TimeSpan.FromMilliseconds(1);
The TimeSpan object has a constructor that takes milliseconds as a parameter:
MSDN has the details: https://msdn.microsoft.com/en-us/library/6c7z43tw(v=vs.110).aspx
Basically, replace new TimeSpan(0, 0, 1) with new TimeSpan(0, 0, 0, 0, millisecondTimeout).
Although #EBrown's answer already tells you how to use milliseconds with a DispatcherTimer, its precision is supposedly around 15-20 ms, and the errors add up at each iteration, which makes it a bit impractical.
However it is possible to work around this issue by starting a System.Diagnostics.StopWatch at the same time as the DispatcherTimer and checking the StopWatch.ElapsedMilliseconds inside the Tick Event with a smaller time step, and that seems to give more precise results. It also works with a System.Timers.Timer.
Here's a short example based on a WPF program I made.
private void StartTimers(object sender, RoutedEventArgs e)
{
dtimer = new DispatcherTimer();
dtimer.Tick += new EventHandler(dTimer_Tick);
dtimer.Interval = TimeSpan.FromMilliseconds(1); // the small time step
stopWatch = new StopWatch();
dTimer.Start();
stopWatch.Start();
}
private void dTimer_Tick(object sender, EventArgs e)
{
currentTime = stopWatch.ElapsedMilliseconds;
if (currentTime - oldTime > interval)
{
oldTime = currentTime;
DoStuff();
}
}
Using StopWatch.Restart in the Tick Event to do iterations will have the same error problem the DispatchTimer causes, because it's going to restart when the DispatchTimer event is fired.
I'm trying to develop simple timer which can save its last value and continue from it at the new app start.
Stopwatch class is not serializable and even cannot be initialized in order to be started from specific time. But it works great. Benchmark showed that stopwatch's 1 minute is really 1 minute.
I tried to use TimeSpan in the following way:
private TimeSpan timerNew = new TimeSpan();
private DispatcherTimer dispatcherTimer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
dispatcherTimer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
timerNew += new TimeSpan(0, 0, 0, 1);
TbTimer.Text = String.Format("{0:00}:{1:00}:{2:00}",
timerNew.Hours, timerNew.Minutes, timerNew.Seconds);
}
private void ButtonStart_OnClick(object sender, RoutedEventArgs e)
{
dispatcherTimer.Start();
}
private void ButtonStop_OnClick(object sender, RoutedEventArgs e)
{
dispatcherTimer.Stop();
}
private void ButtonReset_OnClick(object sender, RoutedEventArgs e)
{
timerNew = new TimeSpan();
TbTimer.Text = "00:00:00";
}
When I checked it against real stopwatch, I found out that this timer implementation lost 2 seconds per minute.
I also tried my own Timer implementation which is simple class with ulong field, which is incremented on each dispatcherTimer tick. And UI shows results after transformation of seconds to hours, minutes and so on. But it also loses 2 seconds per minute comparing to real stopwatch.
Why these 2 seconds are lost? What is an alternative to Stopwatch for usage in a customizable timer?
The Windows thread scheduler is not a "real-time" scheduler, as Windows is not a "real-time OS". In other words, all timing and scheduling is done on a "best effort" basis, without any guarantee of exact precision. In addition, this always results in lost time, because the one guarantee you do have is that scheduling will not happen early. So when there's an imprecision, it's always in the direction of "late".
The Stopwatch class works because it uses CPU-supported performance counters, which doesn't rely on the OS scheduler. The hardware itself tracks the elapsed time and provides the information you need.
I recommend against the use of DateTime.UtcNow for measuring elapsed time, for two reasons: first, the clock DateTime uses is adjustable, and so even using UTC time (which at least would compensate for automatic adjustments due to Daylight Saving Time) is not guaranteed to be accurate. Second, your specific scenario seems to involve an issue where you want to serialize the current state and restore it, which DateTime.UtcNow doesn't address anyway.
Instead, you should make your own serializable stopwatch class, which uses Stopwatch itself as the basis, but which stores a base elapsed value that you add to the Stopwatch's elapsed value.
For example:
class SerializableStopwatch
{
public TimeSpan BaseElapsed { get; set; }
public TimeSpan Elapsed { get { return _stopwatch.Elapsed + BaseElapsed; } }
private Stopwatch _stopwatch = new Stopwatch();
// add whatever other members you want/need from the Stopwatch class,
// simply delegating the operation to the _stopwatch member. For example:
public void Start() { _stopwatch.Start(); }
public void Stop() { _stopwatch.Stop(); }
// etc.
}
How exactly you would serialize the above is up to you. In the simplest scenario, you can just format the Elapsed property as a string to save the value, and then when you want to restore the object, parse that value, create a new instance of the above class, and then assign the value to the BaseElapsed property.
For additional discussion on the topic, you might find Eric Lippert's blog article Precision and accuracy of DateTime useful and interesting.
In my previos question I asked about rounding time value to nearest third-minute.
Well now I have some issues with my System.Threading.Timer that must work when is third-minute time is come. I Do following:
private System.Timers.Timer WorkTimer;
//...
public void StartProccessing()
{
WorkTimer = new System.Timers.Timer();
WorkTimer.AutoReset = false;
WorkTimer.Elapsed += new ElapsedEventHandler(WorkTimer_Elapsed);
StartWorkTimer();
}
//...
private void StartWorkTimer()
{
WorkTimer.Interval = (CurrentTime.AddMinutes(3) - DateTime.Now).TotalMilliseconds;
WorkTimer.Start();
}
void WorkTimer_Elapsed(object sender, ElapsedEventArgs e)
{
WorkTimer.Stop();
this.ProcessData(this.CurrentTime);
StartWorkTimer();
}
Problem is that the when timer started - it is not work in first third-minute time, its begin working after second third-minute time. For example:
Timer is started at 15.02.2012 12:20:32.871, in 15.02.2012 12:21:00.000 it must work WorkTimer_Elapsed, but its begin from 15.02.2012 12:24:01.871. How to fix this?
I fix my CurrentTime time (from previos question):
private DateTime CurrentTime
{
get
{
DateTime now = DateTime.Now.AddSeconds(30);
DateTime val;
val = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0)
.AddMinutes(((now.Minute) / 3) * 3 - now.Minute);
return val;
}
}
The problem here is that you round to the nearest 3rd minute, but for your timer to work you have to use the most recent one.
So, if you remove the AddSeconds(30), your timer should work as expected.
On a side note: your rounding algorithm should add 90 seconds (as that's the half of 3 minutes) rather than 30. If you're adding 30 seconds to 12:31:50, you'd end up with 12:30:00, but it should be 12:33:00 instead.
Use the System.Threading.Timer class.
DateTime dt = ... // here you assign the start time, so you can calculate
// the first time you want to start it.
var timer = new Timer(callback, null, dt, TimeSpan.FromMinutes(3));
Callback is:
public delegate void TimerCallback(Object state)
I want to do stuff every minute on the minute (by the clock) in a windows forms app using c#. I'm just wondering whats the best way to go about it ?
I could use a timer and set its interval to 60000, but to get it to run on the minute, I would have to enable it on the minute precisely, not really viable.
I could use a timer and set its interval to 1000. Then within its tick event, I could check the clocks current minute against a variable that I set, if the minute has changed then run my code. This worries me because I am making my computer do a check every 1 second in order to carry out work every 1 minutes. Surely this is ugly ?
I'm using windows forms and .Net 2.0 so do not want to use the DispatchTimer that comes with .Net 3.5
This must be a fairly common problem. Have any of you a better way to do this?
Building on the answer from aquinas which can drift and which doesn't tick exactly on the minute just within one second of the minute:
static System.Timers.Timer t;
static void Main(string[] args)
{
t = new System.Timers.Timer();
t.AutoReset = false;
t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
t.Interval = GetInterval();
t.Start();
Console.ReadLine();
}
static double GetInterval()
{
DateTime now = DateTime.Now;
return ((60 - now.Second) * 1000 - now.Millisecond);
}
static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine(DateTime.Now.ToString("o"));
t.Interval = GetInterval();
t.Start();
}
On my box this code ticks consistently within .02s of each minute:
2010-01-15T16:42:00.0040001-05:00
2010-01-15T16:43:00.0014318-05:00
2010-01-15T16:44:00.0128643-05:00
2010-01-15T16:45:00.0132961-05:00
How about:
int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"),
null, startin * 1000, 60000);
Creating a Timer control that fires every 1 second (and usually does nothing but a simple check) will add negligible overhead to your application.
Simply compare the value of Environment.TickCount or DateTime.Now to the last stored time (the previous 'minute tick'), and you should have a reasonably precise solution. The resolution of these two time values is about 15ms, which should be sufficient for your purposes.
Do note however that the interval of the Timer control is not guaranteed to be that precise or even anywhere now, since it runs on the Windows message loop, which is tied in with the responsiveness of the UI. Never rely on it for even moderately precise timing - though it is good enough for firing repeating events where you can check the time using a more sensitive method such as one of the two given above.
You can nail this with reactive extensions which will take care of lots of timer related problems for you (clock changes, app hibernation etc). Use Nuget package Rx-Main and code like this:
Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString());
Scheduler.Default.Schedule(
// start in so many seconds
TimeSpan.FromSeconds(60 - DateTime.Now.Second),
// then run every minute
() => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work));
Console.WriteLine("Press return.");
Console.ReadLine();
Read here (search for "Introducing ISchedulerPeriodic") to see all the issues this is taking care of: http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0-release-candidate-available-now.aspx
I jsut wrote this class using the WPF DispatcherTimer but you can swap the dispatcher for any timer that supports changing when it's woken from sleep state.
The class is constructed with a fixed time step and supprts Start/Stop/Reset, Start/Stop/Start works like a resume operation. The timer is like a stopwatch in that regard.
A clock implementation would simply create the class with a interval of 1 second and listen to the event. Be wary though that this is a real-time clock, if the tick event takes longer than the interval to finish you'll notice that the clock will try and catch up to real-time this will cause a burst of tick events being raised.
public class FixedStepDispatcherTimer
{
/// <summary>
/// Occurs when the timer interval has elapsed.
/// </summary>
public event EventHandler Tick;
DispatcherTimer timer;
public bool IsRunning { get { return timer.IsEnabled; } }
long step, nextTick, n;
public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }
public FixedStepDispatcherTimer(TimeSpan interval)
{
if (interval < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("interval");
}
this.timer = new DispatcherTimer();
this.timer.Tick += new EventHandler(OnTimerTick);
this.step = interval.Ticks;
}
TimeSpan GetTimerInterval()
{
var interval = nextTick - DateTime.Now.Ticks;
if (interval > 0)
{
return new TimeSpan(interval);
}
return TimeSpan.Zero; // yield
}
void OnTimerTick(object sender, EventArgs e)
{
if (DateTime.Now.Ticks >= nextTick)
{
n++;
if (Tick != null)
{
Tick(this, EventArgs.Empty);
}
nextTick += step;
}
var interval = GetTimerInterval();
Trace.WriteLine(interval);
timer.Interval = interval;
}
public void Reset()
{
n = 0;
nextTick = 0;
}
public void Start()
{
var now = DateTime.Now.Ticks;
nextTick = now + (step - (nextTick % step));
timer.Interval = GetTimerInterval();
timer.Start();
}
public void Stop()
{
timer.Stop();
nextTick = DateTime.Now.Ticks % step;
}
}
Create a method or put this code where you want the timer to start:
int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
refreshTimer.Interval = time * 1000;
refreshTimer.Start();
And then on your tick event set the interval to 60000:
private void refreshTimer_Tick(object sender, EventArgs e)
{
refreshTimer.Interval = 60000; // Sets interval to 60 seconds
// Insert Refresh logic
}
By making use of ReactiveExtensions you could use the following code if you were interested in doing something as simple as printing to the console.
using System;
using System.Reactive.Linq;
namespace ConsoleApplicationExample
{
class Program
{
static void Main()
{
Observable.Interval(TimeSpan.FromMinutes(1))
.Subscribe(_ =>
{
Console.WriteLine(DateTime.Now.ToString());
});
Console.WriteLine(DateTime.Now.ToString());
Console.ReadLine();
}
}
}
Running a bit of code to see if the minute has changed once per second should not require much CPU time, and should be acceptable.
What about Quartz.NET? I think its a good framework to do timed actions.
You could set up two timers. An initial short interval timer (perhaps to fire every second, but dependent on how presice the second timer must fire on the minute).
You would fire the short interval timer only until the desired start time of the main interval timer is reached. Once the initial time is reached, the second main interval timer can be activated, and the short interval timer can be deactivated.
void StartTimer()
{
shortIntervalTimer.Interval = 1000;
mainIntervalTimer.Interval = 60000;
shortIntervalTimer.Tick +=
new System.EventHandler(this.shortIntervalTimer_Tick);
mainIntervalTimer.Tick +=
new System.EventHandler(mainIntervalTimer_Tick);
shortIntervalTimer.Start();
}
private void shortIntervalTimer_Tick(object sender, System.EventArgs e)
{
if (DateTime.Now.Second == 0)
{
mainIntervalTimer.Start();
shortIntervalTimer.Stop();
}
}
private void mainIntervalTimer_Tick(object sender, System.EventArgs e)
{
// do what you need here //
}
Alternatively, you could sleep to pause execution until it times out which should be close to your desired time. This will only wake the computer when the sleep finishes so it'll save you CPU time and let the CPU power down between processing events.
This has the advantage of modifying the timeout so that it will not drift.
int timeout = 0;
while (true) {
timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond;
Thread.Sleep(timeout);
// do your stuff here
}
Use a timer set to run every second (or millisecond, whatever your accuracy threshold is), and then code the method to run your functionality if and only if the current time is within that threshold past the "on the minute" point.
What I'm using for scheduled tasks is a System.Threading.Timer(System.Threading.TimerCallback, object, int, int) with the callback set to the code I want to execute based on the interval which is supplied in milliseconds for the period value.
What about a combination of aquinas' answer and 'polling': (apologies for the mixture of languages)
def waitForNearlyAMinute:
secsNow = DateTime.Now.Second;
waitFor = 55 - secsNow;
setupTimer(waitFor, pollForMinuteEdge)
def pollForMinuteEdge:
if (DateTime.Now.Second == 0):
print "Hello, World!";
waitForNearlyAMinute();
else:
setupTimer(0.5, pollForMinuteEdge)
I have a solution based on Environment.TickCount
static void Main(string[] args)
{
//constatnt total miliseconds to one minute
const Int32 minuteMilisecond = 60 * 1000;
//get actual datetime
DateTime actualDateTime = DateTime.UtcNow;
//compenzation to one minute
Int32 nexTimer = Environment.TickCount + ((59 - actualDateTime.Second) * 1000) + (999 - actualDateTime.Millisecond);
//random fuction to simulate different delays on thread
Random rnd = new Random();
//main loop
while (true)
{
if (Environment.TickCount > nexTimer)
{
nexTimer += minuteMilisecond;
//execute your code here every minute
Console.WriteLine($"actual DateTime: {DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss:ffff")}");
}
//random sleep between 100 - 200 ms
Thread.Sleep(rnd.Next(100, 200));
}
}