AlHal2
asked on
Populating an array of locale_time_info
I have an array set up like this.
Public LocTZI() As LOCALE_TIME_ZONE_INFORMATI ON
I want to loop through a collection like this and populate the array.
Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo) = TimeZoneInfo.GetSystemTime Zones()
For Each
...
next
Locale_Time_Zone_Informati on is a structure as follows:
Public Structure LOCALE_TIME_ZONE_INFORMATI ON
Dim Bias As Integer
Dim StandardBias As Integer
Dim DaylightBias As Integer
Dim StandardDate As SYSTEMTIME
Dim DaylightDate As SYSTEMTIME
Dim DisplayName As String
Dim StandardName As String
Dim DaylightName As String
Dim MapID As String
End Structure
How do I populate the arrary with this collection? I'm using visual studio 2010 for a windows (rather than web) program.
The program will run on a server with Windows 2012 R2. I'm developing it on a machine with Windows 7.
Public LocTZI() As LOCALE_TIME_ZONE_INFORMATI
I want to loop through a collection like this and populate the array.
Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo) = TimeZoneInfo.GetSystemTime
For Each
...
next
Locale_Time_Zone_Informati
Public Structure LOCALE_TIME_ZONE_INFORMATI
Dim Bias As Integer
Dim StandardBias As Integer
Dim DaylightBias As Integer
Dim StandardDate As SYSTEMTIME
Dim DaylightDate As SYSTEMTIME
Dim DisplayName As String
Dim StandardName As String
Dim DaylightName As String
Dim MapID As String
End Structure
How do I populate the arrary with this collection? I'm using visual studio 2010 for a windows (rather than web) program.
The program will run on a server with Windows 2012 R2. I'm developing it on a machine with Windows 7.
@it_sage. Nice job! Though it doesn't reflect to current solution, you need add/subtract 1600 years when convering filetime to/from DateTime.
dt = New DateTime(ft.Value, DateTimeKind.Utc).AddYears(1600)
ASKER
I'm getting an error message
'localTZ' is not declared. It may be inaccessible due to its protection level.
'localTZ' is not declared. It may be inaccessible due to its protection level.
ASKER
This line also gives a warning message.
Dim LocalTZI = (From tz In TimeZoneInfo.GetSystemTime Zones() Select CType(tz, LOCAL_TIME_ZONE_INFORMATIO N)).ToArra y()
Variable defined without an AS clause. Type of Object assumed
I corrected the first error by amending as follows:
For Each localTZ As TimeZoneInfo In LocalTZI
When I run the program I get another message "Public member 'singleordefault' on type AdjustmentRule() not found"
Dim LocalTZI = (From tz In TimeZoneInfo.GetSystemTime
Variable defined without an AS clause. Type of Object assumed
I corrected the first error by amending as follows:
For Each localTZ As TimeZoneInfo In LocalTZI
When I run the program I get another message "Public member 'singleordefault' on type AdjustmentRule() not found"
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
And reverse CType:
Dim ticks As Long = dt.AddYears(-1600).Ticks
Dim ft As New FILETIME() With {.dwHighDateTime = CInt(ticks >> 32),
.dwLowDateTime = CInt(ticks And &HFFFFFFFFL)}
OK. First of all - is there a reason to convert TimeZoneInfo to structure? TimeZoneInfo contains [almost] all info on TimeZone. There are some difference though:
1. Bias and StandardBias (which is usually 0) are summarized to BaseUtcOffset
2. Values of bias are negated, because .Net class use LocalTime = UTC + Bias logic while API use UTC = LocalTime + Bias
3. Structure contains only last (according windows update) DayLight rule while .Net class contains all rules for previous period.
Actually your structure is 'syntetic' structure - it's not MSDN defilned. I was surprised it's equal to this sample i wrote (or my!) 16 years ago. Novadays using Net classes make things much easy. Also note that MapId is not supported by Vista and later versions. If you still need populate structure, IMHO better use registry directly, without TimeZoneInfo class:
1. Bias and StandardBias (which is usually 0) are summarized to BaseUtcOffset
2. Values of bias are negated, because .Net class use LocalTime = UTC + Bias logic while API use UTC = LocalTime + Bias
3. Structure contains only last (according windows update) DayLight rule while .Net class contains all rules for previous period.
Actually your structure is 'syntetic' structure - it's not MSDN defilned. I was surprised it's equal to this sample i wrote (or my!) 16 years ago. Novadays using Net classes make things much easy. Also note that MapId is not supported by Vista and later versions. If you still need populate structure, IMHO better use registry directly, without TimeZoneInfo class:
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Public Class Registry_TZI
<StructLayout(LayoutKind.Sequential)> _
Public Structure SYSTEMTIME
Public wYear As UInt16
Public wMonth As UInt16
Public wDayOfWeek As UInt16
Public wDay As UInt16
Public wHour As UInt16
Public wMinute As UInt16
Public wSecond As UInt16
Public wMilliseconds As UInt16
End Structure
'44 bytes under TZI registry value
Structure REGISTRY_TIME_ZONE_INFORMATION
Public Property Bias As Integer
Public Property StandardBias As Integer
Public Property DaylightBias As Integer
Public Property StandardDate As SYSTEMTIME
Public Property DaylightDate As SYSTEMTIME
End Structure
'Structure to use with GetTimeZoneInformation API
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Public Structure TIME_ZONE_INFORMATION
<MarshalAs(UnmanagedType.I4)>
Public Bias As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)>
Public StandardName As String
Public StandardDate As SYSTEMTIME
<MarshalAs(UnmanagedType.I4)>
Public StandardBias As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)>
Public DaylightName As String
Public DaylightDate As SYSTEMTIME
<MarshalAs(UnmanagedType.I4)>
Public DaylightBias As Integer
End Structure
Public Structure LOCALE_TIME_ZONE_INFORMATION
Public Property Bias As Integer
Public Property StandardBias As Integer
Public Property DaylightBias As Integer
Public Property StandardDate As SYSTEMTIME
Public Property DaylightDate As SYSTEMTIME
Public Property DisplayName As String
Public Property StandardName As String
Public Property DaylightName As String
Public Property MapID As String
End Structure
<DllImport("kernel32.dll")> _
Private Shared Function GetTimeZoneInformation(ByRef lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Int32
End Function
Public Shared Function EnumLTZI() As List(Of LOCALE_TIME_ZONE_INFORMATION)
Dim listTZ As New List(Of LOCALE_TIME_ZONE_INFORMATION)
Using key As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones")
Dim zoneNames As String() = key.GetSubKeyNames()
For Each zoneName In zoneNames
listTZ.Add(GetLTZI(key, zoneName))
Next
End Using
Return listTZ
End Function
Public Shared Function CurrentTimeZone() As LOCALE_TIME_ZONE_INFORMATION
If Environment.OSVersion.Version.Major >= 6 Then 'Vista and later
Dim ltzi As New LOCALE_TIME_ZONE_INFORMATION
Using key As RegistryKey = Registry.LocalMachine.OpenSubKey("SYSTEM\CurrentControlSet\Control\TimeZoneInformation")
Return GetLTZI(key.GetValue("TimeZoneKeyName").ToString.TrimEnd(vbNullChar))
End Using
Else
Dim tzi As New TIME_ZONE_INFORMATION
Dim ret As Integer = GetTimeZoneInformation(tzi)
If ret = 0 Then
For Each ltzi In EnumLTZI()
If ltzi.StandardName = tzi.StandardName AndAlso
ltzi.StandardBias = tzi.StandardBias AndAlso
ltzi.StandardDate.Equals(tzi.StandardDate) Then Return ltzi
Next
End If
End If
Return Nothing
End Function
Public Shared Function GetLTZI(zoneName As String) As LOCALE_TIME_ZONE_INFORMATION
Using key As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones")
Return GetLTZI(key, zoneName)
End Using
End Function
Private Shared Function GetLTZI(tziKey As RegistryKey, zoneName As String) As LOCALE_TIME_ZONE_INFORMATION
Using subkey As RegistryKey = tziKey.OpenSubKey(zoneName)
If subkey Is Nothing Then Return Nothing
Dim rtzi As REGISTRY_TIME_ZONE_INFORMATION = GetBinaryTZI(CType(subkey.GetValue("TZI"), Byte()))
Dim mapId As Object = subkey.GetValue("MapId") 'Vista and later doesn't support MapId
Return New LOCALE_TIME_ZONE_INFORMATION With {.Bias = rtzi.Bias,
.StandardBias = rtzi.StandardBias,
.DaylightBias = rtzi.DaylightBias,
.StandardDate = rtzi.StandardDate,
.DaylightDate = rtzi.DaylightDate,
.DisplayName = subkey.GetValue("Display").ToString,
.StandardName = subkey.GetValue("Std").ToString,
.DaylightName = subkey.GetValue("Dlt").ToString,
.MapID = If(mapId Is Nothing, "", mapId.ToString)}
End Using
End Function
Private Shared Function GetBinaryTZI(bytes As Byte()) As REGISTRY_TIME_ZONE_INFORMATION
Dim rtzi As New REGISTRY_TIME_ZONE_INFORMATION
If Marshal.SizeOf(rtzi) = bytes.Length Then
Dim h As GCHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned)
rtzi = Marshal.PtrToStructure(h.AddrOfPinnedObject, rtzi.GetType)
h.Free()
End If
Return rtzi
End Function
End Class
Using:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim ltzi = Registry_TZI.CurrentTimeZone
MsgBox("Current time zone is: " & ltzi.DisplayName)
For Each ltzi In Registry_TZI.EnumLTZI
Debug.Print(ltzi.DisplayName)
Next
End Sub
ASKER
Thanks.
Code -
Open in new window
Produces the following output --saige-