We help IT Professionals succeed at work.

System.Threading.Timer Not Using the Specified Span Time Between Call Backs

bobcann
bobcann asked
on
The problem is it does not set the spanof once a day using TimeSpan. The debug window displays the correct span of one day, but it runs every two hours:

-            Interval      {System.TimeSpan}      System.TimeSpan
            Days      1      Integer
            Hours      0      Integer
+            MaxValue      {System.TimeSpan}      System.TimeSpan
            Milliseconds      0                  Integer
            Minutes      0      Integer
+            MinValue      {System.TimeSpan}      System.TimeSpan
            Seconds      0      Integer
            Ticks      864000000000                     Long
            TicksPerDay      864000000000            Long
            TicksPerHour      36000000000            Long
            TicksPerMillisecond      10000            Long
            TicksPerMinute      600000000                  Long
            TicksPerSecond      10000000            Long
            TotalDays      1.0            Double
            TotalHours      24.0               Double
            TotalMilliseconds      86400000.0      Double
            TotalMinutes      1440.0                  Double
            TotalSeconds      86400.0                  Double
+            Zero      {System.TimeSpan}            System.TimeSpan

It works for hours but not days. The start time is perfect and I suppose one day can be specified as 24 hours, but I'd rather get this working correctly. Any ideas?

Thanks for your help.
Imports System.Threading

Module mdlTimer
    Private Timer As System.Threading.Timer = Nothing
    Dim clsPathNames As frmPathNames

    Public Sub StartTimer()
        Dim intMinutessToStartOfTimer As Integer = GetMinutessToStartOfTimer()
        Dim StartTime As New TimeSpan(0, intMinutessToStartOfTimer, 0)
        Dim Interval As TimeSpan

        Select Case strFrequency
            Case "Once a Day"
                Interval = New TimeSpan(1, 0, 0, 0) 'one day

            Case "Even Few Hours"
                Interval = New TimeSpan(byteIntervalTime, 0, 0) 'hours

            Case "Every Few Days"
                Interval = New TimeSpan(byteIntervalTime, 0, 0, 0) 'days
        End Select

        Timer = New System.Threading.Timer(New TimerCallback(AddressOf DeleteFiles), Nothing, StartTime, Interval)
    End Sub

    Public Function GetMinutessToStartOfTimer() As Integer
        Const cMidnight As Integer = 24
        Const cNoon As Integer = 12
        Dim intCurrentHour As Integer = DatePart(DateInterval.Hour, Now)
        Dim intHourInMinutes As Integer = intCurrentHour * 60
        Dim intMinute As Integer = DatePart(DateInterval.Minute, Now)
        Dim intMinutesToStartOfTimer As Integer = intMinute

        Dim intStartTime As Integer = CInt(mdlManager.strStartTime.Remove(2)) '* trim ":00")
        intStartTime = CInt(intStartTime)

        If intStartTime = 0 Then intStartTime = cMidnight

        If intStartTime < cNoon And intCurrentHour < cMidnight Then '* if start time delay crosses midnight
            Dim intPreMidnightDelayHours As Integer = cMidnight - intCurrentHour
            Dim intPreMidnightDelayMinutes As Integer = intMinute

            Dim intPreMidnightDelay As Integer = (intPreMidnightDelayHours * 60) - intPreMidnightDelayMinutes
            Dim intPostMidnightDelay As Integer = intStartTime * 60

            intMinutesToStartOfTimer = intPostMidnightDelay + intPreMidnightDelay
        Else
            intMinutesToStartOfTimer = intHourInMinutes + intMinute
            intMinutesToStartOfTimer = CLng((CInt(intStartTime) * 60) - intMinutesToStartOfTimer)
            intMinutesToStartOfTimer = IIf(intMinutesToStartOfTimer >= 0, intMinutesToStartOfTimer, -intMinutesToStartOfTimer)
        End If

        Return intMinutesToStartOfTimer
    End Function

    Private Sub DeleteFiles(ByVal state As Object)
        Console.WriteLine("Timer Callback: " & Now.ToString)
        clsFilePaths.BuildFilePaths()
        mdlManager.DeletedFiles()
    End Sub
End Module

Open in new window

Comment
Watch Question

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
I don't see anything wrong in StartTimer() but your GetMinutessToStartOfTimer() is really bothering me.

Is "mdlManager.strStartTime" a String representing the time of day the timer should start?  Can you give examples of what it may contain?

I'm 100% certain we can replace that huge function with 2 or 3 lines of code...
High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009
Commented:
For instance if you wanted something to start at 9:00 PM then you could represent that with a TimeSpan using:

    Dim StartTime As New TimeSpan(21, 0, 0) ' Start at 9:00 PM

Next, you use todays Date (with no time component) via "Date.Today" and add "StartTime" to it like this:

    Dim TargetDT As DateTime = Date.Today.Add(StartTime)

So now we have 9:00 PM today in "TargetDT".  If it is already past 9:00 pm then we need to schedule it for tomorrow by adding one day:

    If TargetDT < DateTime.Now Then ' Is the Start Time in the Past?
        TargetDT = TargetDT.AddDays(1) ' move it to same time tomorrow
    End If

Finally, to get a TimeSpan representing the amount of time between now and "TargetDT" you just do a subtraction:

    Dim DurationToStartTime As TimeSpan = TargetDT.Subtract(DateTime.Now)

Thus you can pass "DurationToStartTime" to the "dueTime" parameter (third one) of the Timer constructor and it will cause it to fire the next time 9:00 pm rolls around.

Here it is all put together with an Interval of one day:


        Dim StartTime As New TimeSpan(21, 0, 0) ' Start at 9:00 PM

        Dim TargetDT As DateTime = Date.Today.Add(StartTime)
        If TargetDT < DateTime.Now Then ' Is the Start Time in the Past?
            TargetDT = TargetDT.AddDays(1) ' move it to the same time tomorrow
        End If

        Dim DurationToStartTime As TimeSpan = TargetDT.Subtract(DateTime.Now)
        Timer = New System.Threading.Timer(New TimerCallback(AddressOf DeleteFiles), Nothing, DurationToStartTime, New TimeSpan(1, 0, 0, 0))

Open in new window

Author

Commented:
Very impressive to write this in so few lines. I certainly need to learn more about the DatTime class.

However, I'm not understanding this:

Dim StartTime As New TimeSpan(21, 0, 0) ' Start at 9:00 PM

Is TimeSpan reading this as 24 hour (23:00 = 9.00 PM) time or is it simply using "21" as an offset in hours from current time--in this case it would be Midnight?

Thanks Idle, what I wrote works perfectly, but if I can use a simpler function I will.

-Bob
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
It's an absolute time (using 24 hour military time) with no Date involved and no offset from any time.

10 = 10:00 am
11 = 11:00 am
12 = 12:00 pm
13 =  1:00 pm
14 =  2:00 pm
15 =  3:00 pm
etc...

For things after noon, you just add 12.  Thus 1:00 pm becomes (1 + 12) = 13 and 9:00 pm is (9 + 12) = 21.

In this line:

    Dim TargetDT As DateTime = Date.Today.Add(StartTime)

"Date.Today" returns todays date with no time (or the time at 0 hours, 0 minutes, 0 seconds if you want to think of it that way) so when we add "StartTime" we get that absolute time on that date.

If we are past the time already then we add one day making it the correct time on the next day.

When you subtract two DateTime instances you get a TimeSpan holding the amount of time in-between them:

    Dim DurationToStartTime As TimeSpan = TargetDT.Subtract(DateTime.Now)

This automatically takes into account the "spanning midnight" issue because we correctly set up the DATE portions of the DateTime instances.

Author

Commented:
Idle, thanks for the info on Date & Time. Although my huge sub with tons of variables actually works, I tested and used your more elegant solution.

Once again, thank you for all the .NET and programming instruction. It helped me through this project, for which I had no training or to train myself in .NET.

Well, I'm off to write the related service for this app. No doubt there will be more questions :)

-Bob
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Glad it was helpful!  =)

Author

Commented:
Idle, if you have the time, please take a look at this:

"Compiler Looks for Assembly File that Does Not Exist and Should Not"

http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_26275429.html

Thank you

-Bob