Introduction
This article explains how to pick timezone from Windows registry and sort it for displaying purposes.
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 of the earth's spherical shape and the rotation of the earth, generally there are 24 regions with each region having a difference of 1 hour. But some occasions arise in some areas whereby it has a different time from that which its region should have.
Background
When I designed my analog control, I decided to add timezone feature which made my control. A time zone dialog which works exactly like Windows time would be good enough. That's when I started to observe where Windows stores this information. After digging several sources, I found it stored in the registry, but then I realized that it would not be as easy as just reading the data. The process of making the timezone dialog is just as complex as creating the analog control itself.
While 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 its functionality.
Making the Code
The main object of this code is TimeZones
structure. The structure will have data from each timezone, including name and timespan. The processing will be done by TimeZoneForm
.
Preparation
Our first preparation is to know where Windows stores this time zone list. It is stored in the registry, at "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\". In that key, there are many sub keys which provide information about each time zone. So, to retrieve the time zone list, we will look at these sub keys and read their values.
Design Basic Fundamentals
The first thing we should do is create 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 a generic collection to store all time zone structures.
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 are in the registry where Windows stored them. As I told earlier, Windows stores time zone data in "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\". Each time zone data is stored on a sub key. In sub key, there are 4 string
values, a dword values and a binary value. Basically, we need only one key which is a display value. 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 be needed to parse this information, so we add it in Time Zone structure.
Private Sub ValidateTimeSpan()
Try
Select Case Mid(_Display, 5, 1)
Case ")"
_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
_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 generating time zones.
Collect and Process Data for Display
What we have to do now is to initialize a collection variable that contains time zones. We will use System.Collections.Generic.List
for generating a list of TimeZones
. In the next step, we will fill the collection in our list from Windows registry. To access the registry, we will need Registry
library.
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 filled with Time Zones information. Unfortunately, the list is not ordered like it should be. Also, the time zone ordering is not done alphabetically. It is ordered by time span. Since standard collection class doesn't provide us with that kind of sorting, we have to make one by ourselves.
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
The last one is to add a feature that will enable you to decide your own time. The modification code looks like the one described below:
Public ReadOnly Property NewSpan() As TimeSpan
Get
If chkOwn.Checked = False Then
Return -TimeZone.CurrentTimeZone.GetUtcOffset(Now) + _
c.Item(cmbTimezone.SelectedIndex).Span
Else
Dim ts As TimeSpan
If rAscend.Checked = True Then
ts = New TimeSpan(CInt(Mid(mtbValue.Text, 1, 2)), _
CInt(Mid(mtbValue.Text, 4, 2)), 0)
ElseIf rDecend.Checked = True Then
ts = New TimeSpan(CInt(Mid(mtbValue.Text, 1, 2)) * -1, _
CInt(Mid(mtbValue.Text, 4, 2)) * -1, 0)
End If
Return ts
End If
End Get
End Property
Using the Code
The TimeZoneForm
should act as a dialog form. It will return the latest time after you setup your current time with the new timezone
settings. Doing so, the TimeZoneForm
will calculate and do the processing and return the expected value.
Points of Interest
There's something that's still on my mind, that is Daylight Saving Time. Since I do not live in a country with Daylight Saving Time, I stripped that feature because I still don't know exactly how it works. There's a subkey in each region that has DST settings and I don't have any idea of how to use it. If anyone could make some modifications or give me some information, I will gladly update my code.
History
- Version 1.0 - Initial release
Got his BA in Information and Technology from University of Surabaya, Indonesia in 2004. Finished his post graduate at Magistrate and Management Institute of Technology, Surabaya in 2009.
He has developed several middle to large scale enterprise application, mostly on windows based architecture.
Currently working as IT Manager on a company based on Sidoarjo.