Wednesday, November 7, 2007

Generating Windows Standard Time zones

A time zone, is a region of the earth that has the same standard time. All areas within that region will apply the same time. Because earth sphere shape, and the rotation of the earth, generally there are 24 regions with each region has a difference of 1 hour. But some occasions appeared in some areas resulting it has different time from it region should have.

Windows Time Zone

If you set your time, you will see a time zone tab in the dialog. Alternatively, you can see windows standard time zones in regional setting in control panel. In that dialog, you'll see a list of standard time zones. Our primary goal in this article, is to retrieve the values and mimic it's functionality. And since I'm not lived in Daylight Saving countries, I'll skip that feature because I'm still don't know exactly how it work.

Preparation

Our first preparation is to know where Windows store this time zone list. It stored on registry, at "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\". In that key, there are many sub keys which provide information on each time zones. So, to retrieve the time zone list, we will look at these sub keys and read their values.

Designing Basic Fundamentals

The first time we should do is creating a Time Zone structure that will contain time zone data. We don't need a class because it's only for displaying data and light processing. Besides, we will use generic collection to store all time zones structure.

Code
Private Structure TimeZones
Private _Display As String
Private _Name As String
Private _TimeSpan As TimeSpan

Public Property DisplayName() As String
Get
Return _Display
End Get
Set(ByVal value As String)
_Display
= value
End Set
End Property

Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name
= value
End Set
End Property

Public ReadOnly Property Span() As TimeSpan
Get
Return _TimeSpan
End Get
End Property
End Structure

Next, we will analyze how time zone structures in registry where windows stored them. As I told earlier, windows stored time zone data in "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\". Each time zone data stored on a sub key. In sub key, there are 4 string values, a dword values and a binary values. Basically, we need only one key which is display values. The display key will be added in our time zone structures. From this display data, we will set time span for the time zone as well. To validate a time zone's time span, we will parse directly from display value. Display value will have data like this : "(GMT+07:00) Bangkok, Hanoi, Jakarta". In order to get time span, we will parse "(GMT+07:00)" in order to get 07:00 value. A new function will needed to parse this information, so we add it in Time Zone structure.







Code

Private Sub ValidateTimeSpan()
'Example: (GMT+07:00)
Try
Select Case Mid(_Display, 5, 1)
Case ")" 'GMT
_TimeSpan = New TimeSpan(0, 0, 0)
Case "+"
_TimeSpan
= New TimeSpan(CInt(Mid _
(_Display,
6, 2)), CInt(Mid(_Display, 9, 2)), 0)
Case "-"
_TimeSpan
= New TimeSpan(CInt(Mid_
(_Display,
6, 2)) * -1, CInt(Mid(_Display, 9, 2)) * -1, 0)
End Select
Catch ex As Exception
'GMT will raise an error. So we place it here.
_TimeSpan = New TimeSpan(0, 0, 0)
End Try
End Sub

Now our base structure is ready to use. It's time to implement it in our class. For this sample, I'll use a form and a combo box for generate time zones.


First step is to initialize a collection variable that contain time zones. Next we will fill the collection to our list from windows registry. To access registry, we will need Win32 library.







Code

Private c As New _
System.Collections.Generic.List(
Of TimeZones)

Private Sub GenerateTimezone()
Dim rk As RegistryKey = _
Registry.LocalMachine.OpenSubKey( _
"SOFTWARE\" & _
"Microsoft\Windows NT\" & _
"CurrentVersion\Time Zones\", _
True)

For Each sName As String In _
rk.GetSubKeyNames

Dim tempKey As _
RegistryKey
= _
rk.OpenSubKey(sName,
True)

Dim _tz As New TimeZones
_tz.DisplayName
= _

CType(tempKey.GetValue(_
"Display"), String)

_tz.Name
= _
CType(tempKey.GetValue("Std"), String)

c.Add(_tz)
Next
End Sub

Now we have our list fill with Time Zones information. Unfortunately, the list is not ordered like it should have. Also, time zone ordering is not based on alphabetical. It ordered by time span. Since standard collection class doesn't provide us with that kind of sorting, we have to make by our self.







Code

Private Function TimeZoneConvert(ByVal str As String) _
As String
If Mid(str, 5, 1) = ")" Then
Return "0"
Else
Return Mid(str, 5, 3) & Mid(str, 9, 2)
End If
End Function

Private Function CompareTimezones(_
ByVal tz1 As TimeZones, _
ByVal tz2 As TimeZones) As Integer
If IsNothing(tz1) Then
If IsNothing(tz2) Then
Return 0
Else
Return -1
End If
Else
If IsNothing(tz2) Then
Return 1
Else
Dim int1 As Integer = _
CInt(TimeZoneConvert(tz1.DisplayName))

Dim int2 As Integer = _
CInt(TimeZoneConvert(tz2.DisplayName))

If int1 > int2 Then
Return 1
ElseIf int1 < int2 Then
Return -1
Else
Return 0
End If
End If
End If
End Function

Private Sub DisplayTimeZones()
cmbTimezone.Items.Clear()

c.Sort(
AddressOf CompareTimezones)

For Each tz As TimeZones In c
cmbTimezone.Items.Add(tz.DisplayName)
If TimeZone.CurrentTimeZone.StandardName = _
tz.Name
Then
cmbTimezone.Text
= tz.DisplayName
End If
Next
End Sub

That's all. Our Time Zone form is finished. The form will look like this.


Now, it can display as Windows Time Zone does. You can download the source sample here.