Time Zones And Daylight Savings Time
This page describes code for working with Time Zones and Daylight Savings Time.
Neither VBA nor VB6 provides any native functions for working with Time Zones, Greenwich
Mean Time (GMT, also called UTC), or Daylight Savings Time. To work with these values, you must use some Windows
Application Programmatic Interface (API) functions. This page describes these API functions
and how to use them in your VBA code.
A NOTE OF CAUTION: Windows API functions are unforgiving. Any error, such as 
using the address of a variable rather than a value of the variable, may cause the entire Excel Application to crash 
immediately, and you will lose all of you unsaved work. Until you are compfortable with the various APIs, 
always save your work before running the code.
Names And Abbreviations: Although the name for the time of a official atomic clock is Universal 
Coordinated Time (UTC), we will use on this page and in the sample code, the term 'Greenwich Mean Time' 
or 'GMT'. 'GMT' and 'UTC' can be used interchangably. Daylight Savings Time is abbreviated 'DST' and 
Standard Time is abbreviated 'SST'.
You can download a bas module file containing all the code discussed
on this page.

The core of all the functions presented on this pages is the API function GetTimeZoneInformation and
the structure named TIME_ZONE_INFORMATION that the API function populates with data. You can
learn much more about GetTimeZoneInformation and the TIME_ZONE_INFORMATION 
on MSDN at GetTimeZoneInformation.

The return value of the GetTimeZoneInformation API function indicates
whether the current date and time are in Daylight Time, Standard Time, or
if Windows is unable to determine whether the time is in Daylight Time or Standard Times. The 
code is shown below:
 
    Function DaylightMode() As TIME_ZONE
    
    Dim TZI As TIME_ZONE_INFORMATION
    Dim DST As TIME_ZONE
    DST = GetTimeZoneInformation(TZI)
    DaylightMode = DST
    End Function 
The code uses the following Enum declaration to indicate Daylight Times.
    Public Enum TIME_ZONE
        TIME_ZONE_ID_INVALID = 0    
        TIME_ZONE_STANDARD = 1      
        TIME_ZONE_DAYLIGHT = 2      
    End Enum

Time Zone information is retrieved with the API functions shown below. Since the TIME_ZONE_INFORMATION 
structure and the SYSTEMTIME structure are used by the API functions, they must be declared before the
Declare Function declarations. You code module should look like the following:
    Option Explicit
    
    Private Type SYSTEMTIME
        wYear As Integer
        wMonth As Integer
        wDayOfWeek As Integer
        wDay As Integer
        wHour As Integer
        wMinute As Integer
        wSecond As Integer
        wMilliseconds As Integer
    End Type
    
    
    
    Private Type TIME_ZONE_INFORMATION
        Bias As Long
        StandardName(0 To 31) As Integer
        StandardDate As SYSTEMTIME
        StandardBias As Long
        DaylightName(0 TO 31) As Integer
        DaylightDate As SYSTEMTIME
        DaylightBias As Long
    End Type
    
    
    
    Private Enum TIME_ZONE
        TIME_ZONE_ID_INVALID = 0        
        TIME_ZONE_STANDARD = 1          
        TIME_ZONE_DAYLIGHT = 2          
    End Enum
    
    Private Declare Function GetTimeZoneInformation Lib "kernel32" _
        (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
    
    Private Declare Sub GetSystemTime Lib "kernel32" _
        (lpSystemTime As SYSTEMTIME)
Once these declarations are in place, we can move forward to the real code.

Time zone information is returned by the GetTimeZoneInformation
function. You pass the function an instance of the TIME_ZONE_INFORMATION structure
and the function populates the fields of this structure. The return value of the function indicates
whether the current time is within DST. The return value will be one of the values in the following Enum:
    Private Enum TIME_ZONE
        TIME_ZONE_ID_INVALID = 0        
        TIME_ZONE_STANDARD = 1          
        TIME_ZONE_DAYLIGHT = 2          
    End Enum
Using only the return value of GetTimeZoneInformation, you can determine whether
the current date is within DST.
Sub TestDST()
    Dim TZI As TIME_ZONE_INFORMATION
    Dim DST As TIME_ZONE
    
    DST = GetTimeZoneInformation(TZI)
    Select Case DST
        Case TIME_ZONE_ID_INVALID
            Debug.Print "Windows cannot determine DST."
        Case TIME_ZONE_STANDARD
            Debug.Print "Current date is in Standard Time"
        Case TIME_ZONE_DAYLIGHT
            Debug.Print "Current date is in Daylight Time"
        Case Else
            Debug.Print "**** ERROR: Unknown Result From GetTimeZoneInformation"
    End Select
End Sub
You can turn this into a fuctions such as shown below:
    Public Function IsDST() As Boolean
    
        Application.Volatile True
        Dim DST As TIME_ZONE
        Dim TZI As TIME_ZONE_INFORMATION
        DST = GetTimeZoneInformation(TZI)
        IsDST = (DST = TIME_ZONE_DAYLIGHT)
    End Function
The GetTimeZoneInformation function and the 
TIME_ZONE_INFORMATION structure elements provide much more 
information besides just the current time zone. First, it returns the name of
the Standard Time Zone and the Daylight Time Zone. These values do not depend
on whether the current time is within DST. The StandardName and DaylightName values
are not String type variables. Instead, they are arrays of integers that can be
translated to a string. The following function takes in an array of integers and
returns a String based on those integer values in the array.
    Function IntArrayToString(V As Variant) As String
        Dim N As Long
        Dim S As String
        For N = LBound(V) To UBound(V)
            S = S & Chr(V(N))
        Next N
        IntArrayToString = S
    End Function
You can use this function like:
    Sub AAA()
        Dim TZI As TIME_ZONE_INFORMATION
        Dim DST As TIME_ZONE
        Dim StandardName As String
        
        DST = GetTimeZoneInformation(TZI)
        StandardName = IntArrayToString(TZI.StandardName)
        Debug.Print StandardName
    End Sub
This same method can be used with DaylightName.
The following fucntions convert Local Time to GMT and GMT to Local Time.
    Function ConvertLocalToGMT(Optional LocalTime As Date) As Date
     
    Dim T As Date
    Dim TZI As TIME_ZONE_INFORMATION
    Dim DST As TIME_ZONE
    Dim GMT As Date
    
    If LocalTime <= 0 Then
        T = Now
    Else
        T = StartTime
    End If
    DST = GetTimeZoneInformation(TZI)
    GMT = T + TimeSerial(0, TZI.Bias, 0) - IIf(DST = TIME_ZONE_DAYLIGHT, TimeSerial(1, 0, 0), 0)
    ConvertLocalToGMT = GMT
    
    End Function
    Function GetLocalTimeFromGMT(Optional GMTTime As Date) As Date
    
    Dim GMT As Date
    Dim TZI As TIME_ZONE_INFORMATION
    Dim DST As TIME_ZONE
    Dim LocalTime As Date
    
    If StartTime <= 0 Then
        GMT = Now
    Else
        GMT = GMTTime
    End If
    DST = GetTimeZoneInformation(TZI)
    LocalTime = GMT - TimeSerial(0, TZI.Bias, 0) + IIf(DST = TIME_ZONE_DAYLIGHT, TimeSerial(1, 0, 0), 0)
    GetLocalTimeFromGMT = LocalTime
    
    End Function
The function below returns the number of hours or minutes that are to
be 
added to the local time to get a GMT time. Normally, this
can be done very simply with the 
Bias member
of 
TIME_ZONE_INFORMATION structure. The function below
does that but then adjusts for DST if the 
AdjustForDST
parameter is True. If 
AdjustForDST is False or omitted,
no DST conversion is performed.  For example, the absolute difference between
local time in Chicago is 6 hours behind London. If we are 
not in DST,
the result is either 360 (if 
AsHours is False) or
6 (if 
AsHours is True). If we are presently in 
DST, and 
AdjustForDST is True, the result is 
300 (if 
AsHours is True) or 5 (if 
AsHours is True). 
If 
AdjustForDST is True, the difference is the same as if we
are not in DST. To convert in the opposite direction (GMT Offst To Local) simply multiply
the result of this function.
Function LocalOffsetFromGMT(Optional AsHours As Boolean = False, _
    Optional AdjustForDST As Boolean = False) As Double
Dim TBias As Long
Dim TZI As TIME_ZONE_INFORMATION
Dim DST As TIME_ZONE
DST = GetTimeZoneInformation(TZI)
If DST = TIME_ZONE_DAYLIGHT Then
    If AdjustForDST = True Then
        TBias = TZI.Bias + TZI.DaylightBias
    Else
        TBias = TZI.Bias
    End If
Else
    TBias = TZI.Bias
End If
If AsHours = True Then
    TBias = TBias / 60
End If
LocalOffsetFromGMT = TBias
End Function
As a final note, the module contains a function named
SystemTimeToVBTime which converts a 
SYSTEMTIME structure to a normal VB/VBA
serial date and time. The code is shown below:
Function SystemTimeToVBTime(SysTime As SYSTEMTIME) As Date
With SysTime
    SystemTimeToVBTime = DateSerial(.wYear, .wMonth, .wDay) + _
            TimeSerial(.wHour, .wMinute, .wSecond)
End With
End Function
You can download a bas module file containing all the code discussed
on this page.
Last Updated: 6-May-2008