Sunday, September 24, 2006

Time Keeps On Ticking (or Does It?)

One thing that many server applications (like Windows Services) have in common is some sort of timer loop that causes the application to "wake up", do some processing, and go back to sleep for a certain amount of time. For example, you may have a website that allows users to enter orders into a system. However, in this system order processing is a complex process that takes a fair amount of time, so instead of requiring the users to wait for the order to be processed and placed into the system, the orders go into a processing queue. Another server application pulls the order off of the queue and processes it. Depending on how quickly orders need to be entered into a system, this server application’s order processing logic may run as often as every few seconds or it may only need to run once a day.

A common technique that is used to implement this timer loop is to have a fairly fast Timer control (typically 10 or 20 Hz) raise an event. (There are actually other techniques for doing this same thing, but for this example I am using a Timer control.) The event handler for this fast Timer control will compare the current time with the last time the application executed its processing loop. If the difference between these two times exceeds the application’s timer loop frequency setting, the application will begin its processing logic. If the difference between these two times is less than the application’s timer loop, the application will go back to sleep and wait for the next Timer event to fire.

Here is some sample VB.NET code that implements this:

Private Sub Timer1_Tick( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Timer1.Tick

    Const cRunEverySeconds As Integer = 120
    Static sLastRunDateTime As Date

    If Date.Now >= sLastRunDateTime.AddSeconds(cRunEverySeconds) Then

        ' Remember Last Run Date & Time
        sLastRunDateTime = Date.Now

        ' Perform Processing

    End If

End Sub

This technique works great except for the day daylight saving time starts or ends. In the example above assume sLastRunDateTime contains a time of 1:59:58 AM. Normally the processing would execute two minutes later at 2:01:58 AM, but today is the day daylight saving time ends. When the system clock hits 2:00 AM, it will automatically go back to 1:00 AM. Therefore instead of waiting 2 minutes between processing cycles, the application will wait 1 hour and 2 minutes. Depending on the application this may or may not be a problem, but it is not ideal. (This same problem plagues applications that are running on laptops that may move from one time zone to another.)

The way to solve this problem is actually very easy. In .NET framework languages all you have to do is replace the Date.Now function call with Date.UtcNow. Coordinated Universal Time (UTC) is not affected by silly things like daylight savings time or time zone changes. Time never moves backward or skips an hour in UTC.

The following is the same code listed above with the Date.Now function calls replaced with Date.UtcNow function calls:

Private Sub Timer1_Tick( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Timer1.Tick

    Const cRunEverySeconds As Integer = 120
    Static sLastRunDateTime As Date

    If Date.UtcNow >= sLastRunDateTime.AddSeconds(cRunEverySeconds) Then

        ' Remember Last Run Date & Time
        sLastRunDateTime = Date.UtcNow

        ' Perform Processing

    End If

End Sub

No comments: