GetTimeZoneInformation Issue
Hello everybody,
I have used the link below from the excellent site vbnet to calculate past, current and future daylight savings time. I am interested in only current and future daylight savings time. However when I run the code (for US/Canada daylight savings time) it calculates correctly the first Sunday of November. However it is supposed to calculate the 2nd Sunday of March but instead calculates the first Sunday of March. Anybody have any ideas? Thanks
GetTimeZoneInformation: Past, Current and Future Daylight/Standard Dates
I think the issue is with this section of the code:
Code:
With tziDate
Select Case .wDay 'week in month
Case 1 To 4: 'week 1 to week 4
'Calculate the first day in the month,
'and then calculate the appropriate day
'that the time zone change will occur
MonthFirstWeekday = Weekday(DateSerial(tziYear, .wMonth, 1)) - 1
tmp = DateSerial(tziYear, _
.wMonth, _
(.wDayOfWeek - MonthFirstWeekday + _
.wDay * 7) Mod 7 + 1)
Case 5: 'last week in month
'Calculate the month's last day,
'then work back to the appropriate
'weekday
tmp = DateSerial(tziYear, .wMonth + 1, 0)
tmp = DateAdd("d", tmp, _
-(Weekday(tmp) - .wDayOfWeek + 7 - 1) Mod 7)
End Select
Re: GetTimeZoneInformation Issue
i agree the function is incorrect, i had already modified it, but have now found my solution also is incorrect
edit: try this, for case 1 to 4, i tested a bit, but needs more
vb Code:
MonthFirstWeekday = Weekday(DateSerial(tziYear, .wMonth, 1))
iDay = (.wday - 1) * 7 + (2 - MonthFirstWeekday) + .wdayofweek
tmp = DateSerial(tziYear, .wMonth, iDay)
Re: GetTimeZoneInformation Issue
ahhh... without looking at the code... errr... if it correctly calculates the first sunday and you want the second sunday... then... DateAdd????
Just a though from left field and beyond...
Good Luck
Re: GetTimeZoneInformation Issue
Here's a similar function I used to use:
Code:
Public Function WouldBeDST(ByRef DateTime As Date) As Boolean
Dim iYear As Integer
Dim iStartDST As Integer
Dim iEndDST As Integer
Dim tStartDST As Date
Dim tEndDST As Date
iYear = Year(DateTime)
Select Case iYear
Case Is < 2007 '' DST starts on the first Sunday in April and ends on the last Sunday in October
iStartDST = Weekday(CDate("4/1/" & CStr(iYear)))
iStartDST = 9 - iStartDST
If iStartDST = 8 Then iStartDST = 1
tStartDST = CDate("4/" & CStr(iStartDST) & "/" & CStr(iYear) & " 2:00:00")
iEndDST = Weekday(CDate("10/31/" & CStr(iYear)))
iEndDST = 32 - iEndDST
tEndDST = CDate("10/" & CStr(iEndDST) & "/" & CStr(iYear) & " 2:00:00")
Case Else '' DST starts on the second Sunday in March and ends on the first Sunday in November
iStartDST = Weekday(CDate("3/1/" & CStr(iYear)))
iStartDST = 16 - iStartDST
If iStartDST = 15 Then iStartDST = 8
tStartDST = CDate("3/" & CStr(iStartDST) & "/" & CStr(iYear) & " 2:00:00")
iEndDST = Weekday(CDate("11/1/" & CStr(iYear)))
iEndDST = 9 - iEndDST
If iEndDST = 8 Then iEndDST = 1
tEndDST = CDate("11/" & CStr(iEndDST) & "/" & CStr(iYear) & " 2:00:00")
End Select
If DateDiff("s", tStartDST, DateTime) >= 0 Then
If DateDiff("s", DateTime, tEndDST) > 0 Then
WouldBeDST = True
End If
End If
End Function
If you're working with a TZI structure, however, you might consider using the SystemTimeToTzSpecificLocalTime API.
Code:
Private Declare Function SystemTimeToTzSpecificLocalTime Lib "kernel32" (lpTimeZoneInformation As TIME_ZONE_INFORMATION, lpUniversalTime As SYSTEM_TIME, lpLocalTime As SYSTEM_TIME) As Long
Everything I've written more recently deals with unix-style time expressions. It's actually a rather convenient format for many reasons, but that code would probably confuse the issue. I will gladly post it, however, if requested.
Re: GetTimeZoneInformation Issue
Thanks for everybody's assistance. I need a solution that works with the Time zone structure as the code can be used in different time zones (including outside US/Canada). So I cannot hard-code second Sunday, or use code that specifies 1st or 2nd Sunday. The code must read the time zone structure under the users windows and with this determine the two dates of daylight savings time. The example given is with the US/Canada and it returns incorrect values for March (1st Sunday instead of 2nd Sunday).
Thanks westconn, will this code work in every situation?
Re: GetTimeZoneInformation Issue
The SYSTEM_TIME values returned in the TZI from GetTimeZoneInformation are overridden to some extent. The wDay members actually give the occurence of wDayOfWeek on which the change occurs (See: http://msdn.microsoft.com/en-us/libr...81(VS.85).aspx).
This code seems to work for me, although I have not subjected it to rigorous testing for other years and time zones.
Code:
Private Type SYSTEM_TIME
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
Public Type TIME_ZONE_INFORMATION
Bias As Long
StandardName(31) As Integer
StandardDate As SYSTEM_TIME
StandardBias As Long
DaylightName(31) As Integer
DaylightDate As SYSTEM_TIME
DaylightBias As Long
End Type
Private Declare Function GetTimeZoneInformation Lib "kernel32" (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
Public Function GetTimeZoneDates(ByRef StartDST As Date, ByRef EndDST As Date, Optional ForYear As Integer = 0) As Boolean
Dim uTZI As TIME_ZONE_INFORMATION
Dim lRetVal As Long
If ForYear = 0 Then ForYear = Year(Now)
lRetVal = GetTimeZoneInformation(uTZI)
If CBool(lRetVal) = True Then
StartDST = TimeZoneDate(uTZI.DaylightDate, ForYear)
EndDST = TimeZoneDate(uTZI.StandardDate, ForYear)
GetTimeZoneDates = True
End If
End Function
Private Function TimeZoneDate(ByRef TZDate As SYSTEM_TIME, ByVal ForYear As Integer) As Date
Dim iFirstDay As Integer
Dim iDay As Integer
With TZDate
iFirstDay = Weekday(DateSerial(ForYear, .wMonth, 1))
iDay = .wDayOfWeek + 1
If iFirstDay > iDay Then iDay = 9 - iFirstDay
iDay = iDay + (7 * (.wDay - 1))
TimeZoneDate = DateSerial(ForYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
End With
End Function
Re: GetTimeZoneInformation Issue
Quote:
Thanks westconn, will this code work in every situation?
i tested some variations, but it was getting late (took longer to work out a solution than expected), so i would suggest more testing, to confirm it is correct in all cases
Re: GetTimeZoneInformation Issue
Respectfully, westconn1's code may not work if you are retrieving the TIME_ZONE_INFORMATION structure by calling GetTimeZoneInformation. As the link I provided explains:
"If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that occurs yearly."
On my machine, the wYear member was indeed zero, indicating a relative date occurring yearly. I believe this would cause westconn1's code to always use the first weekday of the month as it happened in 2000 and return a date in the year 2000.
Re: GetTimeZoneInformation Issue
the part function posted above, does not show, but uses a passed year (tziyear), so the year should always have a value
Code:
Private Function GetTimezoneChangeDate(tziDate As SYSTEMTIME, _
ByVal tziyear As Long, _
ByVal dwType As DateFormats) As String
'thanks to Mathias Schiffer for this routine
any code, using TZI, will only show past or future years change dates, based on the current settings, not when they did or will actually occur
Re: GetTimeZoneInformation Issue
Hello everybdoy, thanks for the assistance. I tested FredZones code by selecting London time zone and it didn't work.
I replaced my select statement with this:
Code:
Dim iFirstDay As Integer
Dim iDay As Integer
iFirstDay = Weekday(DateSerial(tziYear, .wMonth, 1), vbSunday)
iDay = .wDayOfWeek + 1
If iFirstDay > iDay Then iDay = 9 - iFirstDay
iDay = iDay + (7 * (.wDay - 1))
tmp = DateSerial(tziYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
The original code works for London (which is last sunday of march, and last sunday of october). However your code does work for the US canada.
I found this site which states the different daylight savings times:
http://www.webexhibits.org/daylightsaving/g.html
I need something that can always work in all timezones. Thanks.
Re: GetTimeZoneInformation Issue
Aha! I just took a virtual trip to London, and indeed, the last Sunday of October presents a case I did not test. The .wDay member = 5 since it is possible to have 5 Sundays in one month.
Here is a fix to my previous code:
Code:
Private Function TimeZoneDate(ByRef TZDate As SYSTEM_TIME, ByVal ForYear As Integer) As Date
Dim iFirstDay As Integer
Dim iDay As Integer
With TZDate
iFirstDay = Weekday(DateSerial(ForYear, .wMonth, 1))
iDay = .wDayOfWeek + 1
If iFirstDay > iDay Then iDay = 9 - iFirstDay
iDay = iDay + (7 * (.wDay - 1))
TimeZoneDate = DateSerial(ForYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
If Month(TimeZoneDate) <> .wMonth Then
iDay = iDay - 7
TimeZoneDate = DateSerial(ForYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
End If
End With
End Function
Re: GetTimeZoneInformation Issue
Quote:
Originally Posted by
FredZone
Aha! I just took a virtual trip to London, and indeed, the last Sunday of October presents a case I did not test. The .wDay member = 5 since it is possible to have 5 Sundays in one month.
Thanks FredZone for your virtual travelling, this is great you are two for two now. Both US, Canada and Europe work good.
I found another site with the detailed daylight savings time for all countries:
http://home.tiscali.nl/~t876506/TZworld.html
I tried virtually travelling to Egypt, but then again it doesn't work. Egypt's daylight savings is
April Last Friday
September Last Thursday
Is there a way to make the code more universal? So that whatever it is given it works? Or must we tweak the code and try each country seperately as well as all the countries together? Whatever the outcome thanks for your help.
Re: GetTimeZoneInformation Issue
no one code should certainly do all
i have not yet done the code for the 5th week, ran out of time yesterday but it is quite simple to do the extra, once the 1 to 4 weeks work correctly
my testing for different time zones so far was all correct, did you find any it failed to give the correct result?
Re: GetTimeZoneInformation Issue
both my code and fredzone failed for 1st sunday in july 09
also first sunday in april 09
i was testing for any month, not just valid tzi dates, as in a different year it could give same result
Re: GetTimeZoneInformation Issue
Quote:
Originally Posted by
westconn1
both my code and fredzone failed for 1st sunday in july 09
also first sunday in april 09
i was testing for any month, not just valid tzi dates, as in a different year it could give same result
Thanks westconn, I don't understand why microsoft made it so difficult to determine the daylight savings time. I need something that will work from all results returned from GetTimeZoneInformation, since the application uses the timezones extensivly.
Re: GetTimeZoneInformation Issue
i found with a small fix i could alway return the first sunday (or any day) of the month
very easy to get any other one once you have the first
here is the procedure i use to test, no error so far, only tried for 2009 & 2010 so far
vb Code:
Sub getdate()
iday = 0
iwk = 1
imth = 4
iyr = 2009
For iday = 0 To 6
Debug.Print wdayname(iday)
For imth = 1 To 12
fday = Weekday(DateSerial(iyr, imth, 1))
j = (iwk - 1) * 7 + (2 - fday) + iday
If j < 1 Then j = j + 7 ' fixed here
tmp = DateSerial(iyr, imth, j)
'*********** fred zone code
iFIRSTDAY = Weekday(DateSerial(iyr, imth, 1))
lDay = iday + 1
If iFIRSTDAY > l Then lDay = 9 - iFIRSTDAY
lDay = lDay + (7 * (iday - 1))
timezonedate = DateSerial(iyr, imth, iday)
' *************
Debug.Print tmp, timezonedate
Next
Next
If Not Month(tmp) = imth Then tmp = DateSerial(iyr, imth, j - 7)
End Sub
Function wdayname(iday) As String
Select Case iday
Case 0: wdayname = "Sunday"
Case 1: wdayname = "Monday"
Case 2: wdayname = "Tuesday"
Case 3: wdayname = "Wednesday"
Case 4: wdayname = "Thursday"
Case 5: wdayname = "Friday"
Case 6: wdayname = "Saturday"
End Select
End Function
as you can see i tested fredzone code at the same time, just changed to use the same variables
Re: GetTimeZoneInformation Issue
I am in Egypt at the moment, and can see that my code is still a little buggy. There is another issue, however, which we may be overlooking. When my machine is set to Cairo time (+ 2:00), both the DaylightTime and StandardTime structures return a wDayOfWeek = 4 (which is Thursday), in conflict with Hassan's understanding of their current rules.
I appreciate the need to always get this right, but DST is a moving target. The 2007 DST change in the US required a patch in Windows which may, or may not have updated the DST values for all time zones. There are still some freeware patches floating around that could be suspect in that regard as well. If you look in the Registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones you will see them all, but only some of mine have an additional Dynamic DST subkey. There is a new GetDynamicTimeZoneInformation API in Vista which purports to resolve a lot of this, but that is useless to me and probably many of our users.
As a final note, I wonder about "using time zones extensively." I do a lot of GPS/GIS work and have users all over the world. Consequently, I always work in UTC and save time values in unix format. Whenever time is displayed to the user, I simply let their machine translate it into their local time (if they wish) using the SystemTimeToTzSpecificLocalTime API. That way, their time settings are their problem and all of my time caluclations are well insulated. Just a suggestion.
As for bugs, well, we all have them. If Hassan is stepping through my code (or westconn1's) he is at least in position to identify math errors.
Here's my last stab at it (for now)...
Code:
Private Function TimeZoneDate(ByRef TZDate As SYSTEM_TIME, ByVal ForYear As Integer) As Date
Dim iFirstDay As Integer
Dim iDay As Integer
With TZDate
iFirstDay = WeekDay(DateSerial(ForYear, .wMonth, 1))
iDay = .wDayOfWeek + 1
Select Case iDay
Case Is < iFirstDay: iDay = iFirstDay - iDay + 6
Case iFirstDay: iDay = 1
Case Is > iFirstDay: iDay = iDay - iFirstDay + 1
End Select
iDay = iDay + (7 * (.wDay - 1))
TimeZoneDate = DateSerial(ForYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
If Month(TimeZoneDate) <> .wMonth Then
iDay = iDay - 7
TimeZoneDate = DateSerial(ForYear, .wMonth, iDay) + TimeSerial(.wHour, .wMinute, .wSecond)
End If
End With
End Function
Re: GetTimeZoneInformation Issue
in an application i have been using buggy code for 4 years, without realising till this thread, mostly the time difference was correct, if it was out of sync for a week at the time of daylight saving change, i never noticed
still it is better to have the code working correctly
Re: GetTimeZoneInformation Issue
Quote:
Originally Posted by
westconn1
in an application i have been using buggy code for 4 years, without realising till this thread, mostly the time difference was correct, if it was out of sync for a week at the time of daylight saving change, i never noticed
still it is better to have the code working correctly
Hello westconn, I was in the exact same situation. I had some users complain and that was how I found out. You are correct certain daylight savings time change dates were 1 week off. I have an option in the application for a manual adjustment which is what I told the users to do during that one week.
I tried your code but got incorrect results. I recreated the function format from vbnet but inserted your code. This is what it looks like:
Code:
Private Function GetTimezoneChangeDate(tziDate As SYSTEMTIME, _
ByVal tziYear As Long, _
ByVal dwType As DateFormats) As String
'thanks to Mathias Schiffer for this routine
Dim tmp As Date
Dim MonthFirstWeekday As Long
Dim fday As Integer
Dim j As Integer
With tziDate
Dim iFirstDay As Integer
Dim iDay As Integer
Dim lday As Integer
fday = Weekday(DateSerial(tziYear, .wMonth, 1))
j = (.wDayOfWeek - 1) * 7 + (2 - fday) + iDay
If j < 1 Then j = j + 7 ' fixed here
tmp = DateSerial(tziYear, .wMonth, j)
'*********** fred zone code
iFirstDay = Weekday(DateSerial(tziYear, .wMonth, 1))
lday = iDay + 1
If iFirstDay > 0 Then lday = 9 - iFirstDay
lday = lday + (7 * (iDay - 1))
GetTimezoneChangeDate = DateSerial(tziYear, .wMonth, iDay)
' *************
End With
'Now that the date has been calculated,
'return it in the string format requested
'In VB6, you can use the FormatDateTime
'function to return date in specified format
GetTimezoneChangeDate = Format$(tmp, "MMMM dd")
End Function
Re: GetTimeZoneInformation Issue
Quote:
Originally Posted by
FredZone
I am in Egypt at the moment, and can see that my code is still a little buggy. There is another issue, however, which we may be overlooking. When my machine is set to Cairo time (+ 2:00), both the DaylightTime and StandardTime structures return a wDayOfWeek = 4 (which is Thursday), in conflict with Hassan's understanding of their current rules.
I appreciate the need to always get this right, but DST is a moving target. The 2007 DST change in the US required a patch in Windows which may, or may not have updated the DST values for all time zones. There are still some freeware patches floating around that could be suspect in that regard as well. If you look in the Registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones you will see them all, but only some of mine have an additional Dynamic DST subkey. There is a new GetDynamicTimeZoneInformation API in Vista which purports to resolve a lot of this, but that is useless to me and probably many of our users.
As a final note, I wonder about "using time zones extensively." I do a lot of GPS/GIS work and have users all over the world. Consequently, I always work in UTC and save time values in unix format. Whenever time is displayed to the user, I simply let their machine translate it into their local time (if they wish) using the SystemTimeToTzSpecificLocalTime API. That way, their time settings are their problem and all of my time caluclations are well insulated. Just a suggestion.
As for bugs, well, we all have them. If Hassan is stepping through my code (or westconn1's) he is at least in position to identify math errors.
Here's my last stab at it (for now)...
Thanks FredZone for the virtual travelling. All my application wants to do is determine the change date of the daylight savings time for the current selected timezone. I decided on using the windows api instead of a fixed database of values since you are correct DST can change. The user's only responsibility is to make sure that his time settings are correct and his windows updates are up to date.
I will take a look at your suggestions, as for the current code that you have supplied (which is much appreciated) for the US/Canada eastern time zone it starts becoming incorrect in 2011.
In the end, it may be that my users must accept an icorrect timezone for 1 week every few years using the vbnet code.
Re: GetTimeZoneInformation Issue
can you give examples of incorrect results? so i can test some more
i would like to know it is right when i am finished with it
Re: GetTimeZoneInformation Issue
Quote:
Originally Posted by
westconn1
can you give examples of incorrect results? so i can test some more
i would like to know it is right when i am finished with it
Hello westconn, these are the values I tested with.
Code:
Canada / US
2010 March 14 Nov 7
2011 March 13 Nov 6
2012 March 11 Nov 4
2013 March 10 Nov 3
2014 March 09 Nov 2
2015 March 08 Nov 1
2016 March 13 Nov 6
2017 March 12 Nov 5
2018 March 11 Nov 4
2019 March 10 Nov 3
2020 March 08 Nov 1
Europe
2010 March 28 Oct 31
2011 March 27 Oct 30
2012 March 25 Oct 28
2013 March 31 Oct 27
2014 March 30 Oct 26
2015 March 29 Oct 25
2016 March 27 Oct 30
2017 March 26 Oct 29
2018 March 25 Oct 28
2019 March 31 Oct 27
2020 March 29 Oct 25
Egypt
2010 April 30 Sep 30
2011 April 29 Sep 29
2012 April 27 Sep 27
2013 April 26 Sep 26
2014 April 25 Sep 25
2015 April 24 Sep 24
2016 April 29 Sep 29
2017 April 28 Sep 28
2018 April 27 Sep 27
2019 April 26 Sep 26
2020 April 24 Sep 24
Re: GetTimeZoneInformation Issue
i tested for egypt
the 2 sites you link to above both show last thursday in september and last friday in april
my returns were 30/9/2010 and 29/4/2010, both thursdays
that fault is not in the code, but in the values in the windows registry, in both cases .wdayofweek = 4
possibly this was previously correct, but has not been updated by windows, to the friday
for central time USA & canada, my registry has setting for first sunday in novemeber and 2nd sunday of march, i was getting incorrect result for march, but have now fixed that
change
If j < 1 Then j = j + 7
to
If j < (.wDay - 1) * 7 + 1 Then j = j + 7
the same fix seems to have fixed for europe as well