get DST (daylight-saving time) start/end dates/times in UTC

Get help with using AutoHotkey and its commands and hotkeys
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

get DST (daylight-saving time) start/end dates/times in UTC

20 Feb 2017, 01:54

I'm looking for a way, perhaps via the Winapi, it doesn't have to be,
to get the DST (daylight-saving time) start/end dates/times
for my local time zone (or time zones generally) in UTC.
I have written a function that does this for my local time zone,
but I want to confirm that it is accurate.

Note:
GetTimeZoneInformationForYear looked like it might be promising,
however, what it puts into the SYSTEMTIME structure appears
not to be a date, but a rule, possibly copied from the registry:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation

I could use such information to calculate the dates,
but what I want to do is check that my existing function works correctly,
not create another function that might also have errors in it.

TYFR
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
just me
Posts: 6551
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: get DST (daylight-saving time) start/end dates/times in UTC

23 Feb 2017, 17:03

As far as I understand the rules:

Code: Select all

; ----------------------------------------------------------------------------------------------------------------------------------
; Requires Win Vista+
; ----------------------------------------------------------------------------------------------------------------------------------
#NoEnv
Year := 2015
TZI := TZI_GetFromYear(Year)
Gui, Add, ListView, w1000 r9, Field|Value|Description
LV_Add("", "Bias", TZI.Bias, "Minutes (UTC = Local Time + Bias).")
LV_Add("", "StandardName", TZI.StandardName
         , "A description for standard time.")
LV_Add("", "StandardDate", TZI.StandardDate
         , "A date and local time when the transition from daylight saving time to standard time occurs on this operating system.")
LV_Add("", "StandardDate (UTC)", TZI.StandardDateUTC
         , "Calculated UTC date and time.")
LV_Add("", "StandardBias", TZI.StandardBias
         , "This value is added to the value of the Bias member to form the bias used during standard time.")
LV_Add("", "DaylightName", TZI.DaylightName
         , "A description for daylight saving time.")
LV_Add("", "DaylightDate", TZI.DaylightDate
         , "A date and local time when the transition from standard time to daylight saving time occurs on this operating system.")
LV_Add("", "DaylightDate (UTC)", TZI.DaylightDateUTC
         , "Calculated UTC date and time.")
LV_Add("", "DaylightBias", TZI.DaylightBias
         , "This value is added to the value of the Bias member to form the bias used during daylight saving time.")
Loop, 3
   LV_ModifyCol(A_Index, "AutoHdr")
Gui, Show, , TZI for %Year%
Return
GuiClose:
GuiEscape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------------------
; GetTimeZoneInformationForYear -> msdn.microsoft.com/en-us/library/bb540851(v=vs.85).aspx (Win Vista+)
; ----------------------------------------------------------------------------------------------------------------------------------
TZI_GetFromYear(Year) {
   If Year Is Not Integer
      Return 0
   If Year Is Not Date
      Return 0
   Year := SubStr(Year, 1, 4)
   VarSetCapacity(TZI, 172, 0)
   If !DllCall("GetTimeZoneInformationForYear", "UShort", Year, "Ptr", 0, "Ptr", &TZI, "Int")
      Return 0
   R := {}
   R.Bias := NumGet(TZI, "Int")
   R.StandardName := StrGet(&TZI + 4, 32, "UTF-16")
   ST := New TZI_SYSTEMTIME(&TZI + 68)
   ; If ST.Year is not zero the date is fix. Otherwise, the date is variable and must be calculated.
   ; ST.WDay contains the weekday and ST.Day the occurrence within ST.Month (5 = last) in this case.
   If (ST.Year = 0) { 
      ST.Year := Year
      ST.Day := TZI_GetWDayInMonth(ST.Year, ST.Month, ST.WDay, ST.Day)
   }
   R.StandardDate := ST.TimeStamp
   R.StandardBias := NumGet(TZI, 84, "Int")
   R.DaylightName := StrGet(&TZI + 88, 32, "UTF-16")
   ST := New TZI_SYSTEMTIME(&TZI + 152)
   If (ST.Year = 0) {
      ST.Year := Year
      ST.Day := TZI_GetWDayInMonth(ST.Year, ST.Month, ST.WDay, ST.Day)
   }
   R.DaylightDate := ST.TimeStamp
   R.DaylightBias := NumGet(TZI, 168, "Int")
   ; Calculate the UTC values for StandardDate and DaylightDate
   UTCBias := R.Bias + R.DaylightBias ; StandardDate
   UTCDate := R.StandardDate
   UTCDate += UTCBias, M
   R.StandardDateUTC := UTCDate
   UTCBias := R.Bias + R.StandardBias ; DaylightDate
   UTCDate := R.DaylightDate
   UTCDate += UTCBias, M
   R.DaylightDateUTC := UTCDate
   Return R
}
; ----------------------------------------------------------------------------------------------------------------------------------
TZI_GetWDayInMonth(Year, Month, WDay, Occurence) {
   YearMonth := Format("{:04}{:02}01", Year, Month) ; bugfix
   If YearMonth Is Not Date
      Return 0
   If WDay Not Between 1 And 7
      Return 0
   If Occurence Not Between 1 And 5 ; 5 = last occurence
      Return 0
   FormatTime, WD, %YearMonth%, WDay
   While (WD <> WDay) {
      YearMonth += 1, D
      FormatTime, WD, %YearMonth%, WDay
   }
   While (A_Index <= Occurence) && (SubStr(YearMonth, 5, 2) = Month) {
      Day := SubStr(YearMonth, 7, 2)
      YearMonth += 7, D
   }
   Return Day
}
; ----------------------------------------------------------------------------------------------------------------------------------
Class TZI_SYSTEMTIME {
   __New(Pointer) { ; a pointer to a SYSTEMTIME structure
      This.Year  := NumGet(Pointer + 0, "Short")
      This.Month := NumGet(Pointer + 2, "Short")
      This.WDay  := NumGet(Pointer + 4, "Short") + 1 ; DayOfWeek is 0 (Sunday) thru 6 (Saturday) in the SYSTEMTIME structure
      This.Day   := NumGet(Pointer + 6, "Short")
      This.Hour  := NumGet(Pointer + 8, "Short")
      This.Min   := NumGet(Pointer + 10, "Short")
      This.Sec   := NumGet(Pointer + 12, "Short")
      This.MSec  := NumGet(Pointer + 14, "Short")
   }
   TimeStamp[] { ; TimeStamp YYYYMMDDHH24MISS
      Get {
         Return Format("{:04}{:02}{:02}{:02}{:02}{:02}", This.Year, This.Month, This.Day, This.Hour, This.Min, This.Sec)
      }
      Set {
         Return ""
      }
   }
}
I couldn't find out how to set the values of PDYNAMIC_TIME_ZONE_INFORMATION to retrieve the information for other than the local time zone as yet.

Edit: Fixed a bug in TZI_GetWDayInMonth()
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: get DST (daylight-saving time) start/end dates/times in UTC

23 Feb 2017, 17:49

Well done, you've done an excellent job here, thank you.

You'll most likely know that you can use data from here in the meantime for other time zones:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
and here for the local one:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation

Yes, you had the same problem as me, it puts a rule not a date into the SYSTEMTIME structure, and you've worked out from that what the dates should be. This is nice, it gives me another source not written by me for date checking.

I'm still interested in *any* methods for getting the DST start/end dates/times for my local time zone (or time zones generally) in UTC. Any software or websites might be interesting also. Cheers.

I will post some related code after further checking.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: get DST (daylight-saving time) start/end dates/times in UTC

27 Jul 2017, 02:03

To anyone who can help. I'm still interested in getting the time now for other countries/time zones, via DllCall or RegRead, and also via the Internet. And also, the dates at which DST begins/ends, for a particular country/time zone, via similar methods. Thanks.

Note: the items in the registry re. dates, the DST begin/end dates, can be a bit hard to decipher.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation
HKEY_CURRENT_USER\Control Panel\International
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Language
Last edited by jeeswg on 27 Jul 2017, 03:38, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: get DST (daylight-saving time) start/end dates/times in UTC

27 Jul 2017, 05:36

@Bobo: Can you access that in AHK? (I did try.)

I have since found out, that for dealing with the registry directly, the key to the problem is the _REG_TZI_FORMAT struct, listed towards the bottom of this link:
TIME_ZONE_INFORMATION structure (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

I should be able to use the registry to get the time zone information and thus create a script later, although DllCall (e.g. specify a time zone by name and retrieve a struct containing its info) and Internet solutions (e.g. a website that returns JSON for a city) would still be worthwhile.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
just me
Posts: 6551
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: get DST (daylight-saving time) start/end dates/times in UTC

27 Jul 2017, 07:18

This will show you some of the current time zone information stored in the registry:

Code: Select all

#NoEnv
; ----------------------------------------------------------------------------------------------------------------------
RegRoot := "HKEY_LOCAL_MACHINE"
RegKey  := "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
RegTimeZones := {}
; ----------------------------------------------------------------------------------------------------------------------
; Read the registry values
Loop, %RegRoot%, %RegKey%, 2
{
   RegRead, DSP, %RegRoot%, %RegKey%\%A_LoopRegName%, Display
   RegRead, REGTZI, %RegRoot%, %RegKey%\%A_LoopRegName%, TZI
   TZI2TIMEZONEINFORMATION(REGTZI, TIMEZONEINFORMATION)
   DSDT := DateTimeFromSYSTEMTIME(&TIMEZONEINFORMATION + 152)
   STDT := DateTimeFromSYSTEMTIME(&TIMEZONEINFORMATION + 68)
   RegTimeZones[A_LoopRegName] := [DSP, DSDT, STDT]
}
; ----------------------------------------------------------------------------------------------------------------------
Gui, Margin, 20, 20
Gui, Add, ListView, xm Grid -Multi w800 r20 vLV hwndHLV, RegName|DisplayName|DST Start|DST End
Gui, Add, Statusbar
For Key, Value In RegTimeZones
   LV_Add("", Key, Value*)
Loop, 4
   LV_ModifyCol(A_Index, "AutoHdr")
Gui, Show, , Timezones - DST
Return
; ----------------------------------------------------------------------------------------------------------------------
GuiClose:
GuiEscape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
TZI2TIMEZONEINFORMATION(TZI, ByRef TIMEZONEINFORMATION) {
   ; Convert registry TZI values into TIMEZONEINFORMATION struct
   Bias := 0      ; Bias
   StdD := 68     ; StandardDate
   StdB := 84     ; StandardBias
   DltD := 152    ; DaylightDate
   DltB := 168    ; DaylightBias
   VarSetCapacity(TIMEZONEINFORMATION, 172, 0)
   IR := -1
   Addr := &TIMEZONEINFORMATION + Bias ; Bias
   Loop, 4
      Addr := NumPut("0x" . SubStr(TZI, IR += 2, 2), Addr + 0, "UChar")
   Addr := &TIMEZONEINFORMATION + StdB ; StandardBias
   Loop, 4
      Addr := NumPut("0x" . SubStr(TZI, IR += 2, 2), Addr + 0, "UChar")
   Addr := &TIMEZONEINFORMATION + DltB ; DaylightBias
   Loop, 4
      Addr := NumPut("0x" . SubStr(TZI, IR += 2, 2), Addr + 0, "UChar")
   Addr := &TIMEZONEINFORMATION + StdD ; StandardDate
   Loop, 16
      Addr := NumPut("0x" . SubStr(TZI, IR += 2, 2), Addr + 0, "UChar")
   Addr := &TIMEZONEINFORMATION + DltD ; DaylightDate
   Loop, 16
      Addr := NumPut("0x" . SubStr(TZI, IR += 2, 2), Addr + 0, "UChar")
}
; ----------------------------------------------------------------------------------------------------------------------
DateTimeFromSYSTEMTIME(Pointer) {
   Year  := NumGet(Pointer + 0, "Short")
   Month := NumGet(Pointer + 2, "Short")
   Day   := NumGet(Pointer + 6, "Short")
   Hour  := NumGet(Pointer + 8, "Short")
   Min   := NumGet(Pointer + 10, "Short")
   Sec   := NumGet(Pointer + 12, "Short")
   If (Year = 0) && (Month <> 0) {
      Year := A_YYYY
      Day := GetWDayInMonth(Year, Month, NumGet(Pointer + 0, "Short") + 1, Day)
   }
   Return (Month = 0 ? "" : Format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", Year, Month, Day, Hour, Min, Sec))
}
; ----------------------------------------------------------------------------------------------------------------------------------
GetWDayInMonth(Year, Month, WDay, Occurence) {
   YearMonth := Format("{:04}{:02}01", Year, Month)
   If YearMonth Is Not Date
      Return 0
   If WDay Not Between 1 And 7
      Return 0
   If Occurence Not Between 1 And 5 ; 5 = last occurence
      Return 0
   FormatTime, WD, %YearMonth%, WDay
   While (WD <> WDay) {
      YearMonth += 1, D
      FormatTime, WD, %YearMonth%, WDay
   }
   While (A_Index <= Occurence) && (SubStr(YearMonth, 5, 2) = Month) {
      Day := SubStr(YearMonth, 7, 2)
      YearMonth += 7, D
   }
   Return Day
}
Also, there's an old script which might still work.
User avatar
jNizM
Posts: 2530
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: get DST (daylight-saving time) start/end dates/times in UTC

27 Jul 2017, 07:20

Not sure if this link is also useful -> Microsoft Time Zone Index Values (msdn)
[AHK] 1.1.30.03 x64 Unicode | [WIN] 10 Pro (Version 1909) x64 | [GitHub] Profile
Donations are appreciated if I could help you
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: get DST (daylight-saving time) start/end dates/times in UTC

29 Jul 2017, 00:34

Check if it's DST in the current time zone:

Code: Select all

q:: ;is it DST in current time zone
VarSetCapacity(TIME_ZONE_INFORMATION, 172, 0)
;TIME_ZONE_ID_DAYLIGHT := 2 ;TIME_ZONE_ID_STANDARD := 1
;TIME_ZONE_ID_UNKNOWN := 0 ;'Daylight saving time is not used'
vIsDST := (DllCall("GetTimeZoneInformation", Ptr,&TIME_ZONE_INFORMATION, UInt) = 2)
MsgBox, % vIsDST
return
==================================================

A function similar to GetWDayInMonth above, which instead uses some Mod maths:
Note: it returns a date, rather than the day of the month.

Code: Select all

;where vWDay (Sun=1, Sat=7)
;where vOcc = 5 means Last
JEE_DateGetNthWDayInMonth(vYear, vMonth, vWDay, vOcc)
{
	vDate := Format("{:04}{:02}", vYear, vMonth)
	FormatTime, vWDay2, % vDate, WDay
	vDate += Mod(7+vWDay-vWDay2,7)+(vOcc-1)*7, Days
	if !(SubStr(vDate, 5, 2) = vMonth)
		vDate += -7, Days
	return vDate
}
Explanation of the maths:
Note: the Winapi uses 0=Sun to 6=Sat, AHK uses 1=Sun to 7=Sat, this does not affect the calculations.

Code: Select all

jump from first day of month, to first Sunday of month:
Sun(0) + 0 = Sun
Mon(1) + 6 = Sun
Tue(2) + 5 = Sun
Wed(3) + 4 = Sun
Thu(4) + 3 = Sun
Fri(5) + 2 = Sun
Sat(6) + 1 = Sun
n + Mod(7-n) = Sun

jump from first day of month, to first Monday of month:
Sun(0) + 1 = Mon
Mon(1) + 0 = Mon
Tue(2) + 6 = Mon
Wed(3) + 5 = Mon
Thu(4) + 4 = Mon
Fri(5) + 3 = Mon
Sat(6) + 2 = Mon
n + Mod(7+1-n,7) = Mon

jump from first day of month, to first XXXday of month:
n + Mod(7+x-n,7) = XXX

starting from vDate, the first day of the month:
and where vWDay corresponds to the weekday of vDate:
- jump from first day of month, to first Sunday of month:
vDate += Mod(7-vWDay,7), Days
- jump from first day of month, to first Monday of month:
vDate += Mod(7+1-vWDay,7), Days
- jump from first day of month, to first XXX-day of month:
vDate += Mod(7+vDayOfWeek-vWDay,7), Days
==================================================

Get information about all the time zones in the registry:

Code: Select all

q:: ;get time zone information
vYear := SubStr(A_NowUTC, 1, 4)
Loop, Reg, HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones, K
	vList .= A_LoopRegName "`n"
vList := SubStr(vList, 1, -1)
Sort, vList
Loop, Parse, vList, `n
{
	vTimeZoneKeyName := A_LoopField
	RegRead, vIsObsolete, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, IsObsolete
	;if vIsObsolete
	;	continue
	RegRead, vDisplay, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, Display
	RegRead, vDlt, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, Dlt
	RegRead, vStd, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, Std
	RegRead, vHex, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName "\Dynamic DST", % vYear
	RegRead, vFirstEntry, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName "\Dynamic DST", FirstEntry
	RegRead, vLastEntry, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName "\Dynamic DST", LastEntry
	vSpecificYear := (vHex = "") ? "_" : "Y"
	if (vHex = "")
		RegRead, vHex, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, TZI
	vOffset1 := RegExReplace(vDisplay, "\(UTC|:|\).*")
	if (vOffset1 = "")
		vOffset1 := "+0000"
	vOffset2 := Format("{:04}", 1200 + vOffset1)

	vChars := 88, vSize := 44
	;CRYPT_STRING_HEX := 0x4
	;CRYPT_STRING_HEXRAW := 0xC ;(not supported by Windows XP)
	DllCall("crypt32\CryptStringToBinary", Ptr,&vHex, UInt,vChars, UInt,0x4, Ptr,0, UIntP,vSize, Ptr,0, Ptr,0)
	VarSetCapacity(REG_TZI_FORMAT, vSize, 0)
	DllCall("crypt32\CryptStringToBinary", Ptr,&vHex, UInt,vChars, UInt,0x4, Ptr,&REG_TZI_FORMAT, UIntP,vSize, Ptr,0, Ptr,0)

	vBias := NumGet(REG_TZI_FORMAT, 0, "Int") ;Bias
	vStandardBias := NumGet(REG_TZI_FORMAT, 4, "Int") ;StandardBias
	vDaylightBias := NumGet(REG_TZI_FORMAT, 8, "Int") ;DaylightBias

	;StandardDate and DaylightDate:
	Loop, 2
	{
		vOffset := &REG_TZI_FORMAT + (A_Index=1?12:28)
		vMonth := NumGet(vOffset+0, 2, "Short") ;wMonth
		vDayOfWeek := NumGet(vOffset+0, 4, "Short") ;wDayOfWeek ;XXXday
		vDay := NumGet(vOffset+0, 6, "Short") ;wDay ;e.g. 1=first XXXday of month, 5=last XXXday of month
		vHour := NumGet(vOffset+0, 8, "Short") ;wHour
		vMinute := NumGet(vOffset+0, 10, "Short") ;wMinute

		vMonth%A_Index% := Format("{:02}", vMonth)
		vNthWDay := StrSplit("First,Second,Third,Fourth,Last", ",")[vDay]
		;10/01/1904 was a Sun (weekday index to name)
		FormatTime, vDDDD, % 1904011 vDayOfWeek, dddd
		FormatTime, vDDD, % 1904011 vDayOfWeek, ddd
		FormatTime, vMMMM, % 1900 Format("{:02}", vMonth), MMMM
		FormatTime, vMMM, % 1900 Format("{:02}", vMonth), MMM
		vDate := vNthWDay " " vDDDD " " vMMMM " " Format("{:02}:{:02}:00", vHour, vMinute)
		vDateX := vNthWDay " " vDDD " " vMMM " " Format("{:02}:{:02}", vHour, vMinute)
		if (vDate = " Sunday " " 00:00:00")
			vDate := vDateX := "(none)"
		vDate%A_Index% := vDate
		vDateX%A_Index% := vDateX
		vYears := vFirstEntry ? (vFirstEntry "-" vLastEntry) : ""
	}
	vOutput .= ""
	. vDisplay
	. "`r`n" vStd
	. "`r`n" vDlt
	. "`r`n" vDate1
	. "`r`n" vDate2
	. "`r`n`r`n"

	;M: DST during middle of year, E: DST during ends (start/end) of year
	vType := (vMonth1) > (vMonth2) ? "M" : (vMonth1) < (vMonth2) ? "E" : "_"
	vOutput2 .= vType vSpecificYear "|" vOffset2 "|" vOffset1 "|" vStd "|" vDlt "|" vDisplay "|" vDateX1 "|" vDateX2 "|" vYears "`r`n"
}
Clipboard := SubStr(vOutput, 1, -2) "`r`n" vOutput2
MsgBox, % "done"
return
@just me: Nice example.
@jNizM: Cheers.
Last edited by jeeswg on 29 Jul 2017, 00:41, edited 4 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6904
Joined: 19 Dec 2016, 01:58
Location: UK

Re: get DST (daylight-saving time) start/end dates/times in UTC

29 Jul 2017, 00:36

Multiple (digital) clocks for different countries, that update simultaneously:

Code: Select all

;show clocks for various time zones:
;UTC, local, New York, London, Paris, Munich

#SingleInstance force

vYear := SubStr(A_NowUTC, 1, 4)
vUseMui := 0
if vUseMui
{
	vPath := "C:\Windows\System32\tzres.dll"
	hModule := DllCall("kernel32\LoadLibrary", "Str",vPath, "Ptr")
	vPfx := "MUI_"
}
else
	vPfx := ""

vTemp := 0
SetTimer, UpdateTimestamps, 1000

(oTemp := {}).SetCapacity(200)
(oArray := {}).SetCapacity(200)
;note: registry loop retrieves items in reverse order
Loop Reg, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones", % "K"
{
	vTimeZoneKeyName := A_LoopRegName
	RegRead, vIsObsolete, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, IsObsolete
	if vIsObsolete
		continue
	RegRead, vDisplay, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, % vPfx "Display"

	if vUseMui
	{
		vNum := RegExReplace(vDisplay, ".*-")
		VarSetCapacity(vDisplay, 1024)
		DllCall("user32\LoadString", "Ptr",hModule, "UInt",vNum, "Str",vDisplay, "Int",1024)
	}
	else
		vNum := RegExReplace(vDisplay, "\(UTC|\+|:|\).*")

	;note: vNum will be used to sort items
	(vNum = "") && (vNum := 0)
	oTemp.Push((vNum+50000) vDisplay)

	;oArray[vDisplay] := vTimeZoneKeyName
	oArray[vDisplay] := JEE_TimeZoneGetInfo(vTimeZoneKeyName, vYear, vUseMui)
	InStr(vDisplay, "(UTC)") && (vDisplayUTC := vDisplay)
	InStr(vDisplay, "Eastern Time") && (vDisplayNewYork := vDisplay)
	InStr(vDisplay, "London") && (vDisplayLondon := vDisplay)
	InStr(vDisplay, "Paris") && (vDisplayParis := vDisplay)
	InStr(vDisplay, "Berlin") && (vDisplayMunich := vDisplay)
}
vOutput := ""
VarSetCapacity(vOutput, 10000*2)
Loop % oTemp.Length()
	vOutput .= oTemp[oTemp.Length()+1-A_Index] "|"
Sort, vOutput, D|
vOutput := RegExReplace(vOutput, "(^|\|)\K.{5}")

RegRead, vTimeZoneKeyName, HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation, TimeZoneKeyName
RegRead, vDisplay, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, Display
vOptGUI := "w700"
vFontSize1 := "s15"
vFontSize2 := "s20"

oDDL := []
oDDL.1 := StrReplace(vOutput, vDisplayUTC "|", vDisplayUTC "||")
oDDL.2 := StrReplace(vOutput, vDisplay "|", vDisplay "||")
oDDL.3 := StrReplace(vOutput, vDisplayNewYork "|", vDisplayNewYork "||")
oDDL.4 := StrReplace(vOutput, vDisplayLondon "|", vDisplayLondon "||")
oDDL.5 := StrReplace(vOutput, vDisplayParis "|", vDisplayParis "||")
oDDL.6 := StrReplace(vOutput, vDisplayMunich "|", vDisplayMunich "||")

for vKey, vValue in oDDL
{
	Gui, Font, % vFontSize1
	Gui, Add, DropDownList, % vOptGUI, % vValue
	Gui, Font, % vFontSize2
	Gui, Add, Text, % vOptGUI
}

Gui, Show,, time zones
return

;==================================================

UpdateTimestamps:
vTemp++
WinGet, hWnd, ID, time zones ahk_class AutoHotkeyGUI
vNow := A_NowUTC
if !(vYear = SubStr(vNow, 1, 4))
{
	Reload
	return
}
Loop 6
{
	vDate := vNow
	ControlGetText, vText, % "ComboBox" A_Index, % "ahk_id " hWnd
	vTemp := ((vDate < oArray[vText].date1) || (vDate >= oArray[vText].date2)) ? 1 : 2
	EnvAdd, vDate, % -oArray[vText, "offset" vTemp], Minutes
	vTimeZone := oArray[vText, "name" vTemp]
	FormatTime, vDate, % vDate, ddd yyyy-MM-dd HH:mm:ss
	ControlSetText, % "Static" A_Index, % vDate " " vTimeZone, % "ahk_id " hWnd
}
return

;==================================================

JEE_TimeZoneGetInfo(vTimeZoneKeyName, vYear:="", vUseMui:=0)
{
	local
	static vIsReady := 0, vPfx := ""
	if vUseMui & !vIsReady
	{
		vPath := "C:\Windows\System32\tzres.dll"
		hModule := DllCall("kernel32\LoadLibrary", "Str",vPath, "Ptr")
		vPfx := "MUI_"
		vIsReady := 1
	}

	(vYear = "") && (vNow := A_NowUTC, vYear := SubStr(vNow, 1, 4))
	RegRead, vDlt, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, % vPfx "Dlt"
	RegRead, vStd, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, % vPfx "Std"
	RegRead, vHex, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName "\Dynamic DST", % vYear
	if (vHex = "")
		RegRead, vHex, % "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" vTimeZoneKeyName, TZI

	if vUseMui
	{
		vNum := RegExReplace(vDlt, ".*-")
		VarSetCapacity(vDlt, 1024)
		DllCall("user32\LoadString", "Ptr",hModule, "UInt",vNum, "Str",vDlt, "Int",1024)
		vNum := RegExReplace(vStd, ".*-")
		VarSetCapacity(vStd, 1024)
		DllCall("user32\LoadString", "Ptr",hModule, "UInt",vNum, "Str",vStd, "Int",1024)
	}

	vChars := 88, vSize := 44
	;CRYPT_STRING_HEX := 0x4
	;CRYPT_STRING_HEXRAW := 0xC ;(not supported by Windows XP)
	DllCall("crypt32\CryptStringToBinary", "Ptr",&vHex, "UInt",vChars, "UInt",0x4, "Ptr",0, "UInt*",vSize, "Ptr",0, "Ptr",0)
	VarSetCapacity(REG_TZI_FORMAT, vSize, 0)
	DllCall("crypt32\CryptStringToBinary", "Ptr",&vHex, "UInt",vChars, "UInt",0x4, "Ptr",&REG_TZI_FORMAT, "UInt*",vSize, "Ptr",0, "Ptr",0)

	vBias := NumGet(&REG_TZI_FORMAT, 0, "Int") ;Bias
	vBias2 := NumGet(&REG_TZI_FORMAT, 4, "Int") ;StandardBias
	vBias1 := NumGet(&REG_TZI_FORMAT, 8, "Int") ;DaylightBias
	;StandardDate and DaylightDate:
	Loop 2
	{
		vOffset := &REG_TZI_FORMAT + (A_Index=1?12:28)
		vMonth := NumGet(vOffset+0, 2, "Short") ;wMonth
		vDayOfWeek := NumGet(vOffset+0, 4, "Short") ;wDayOfWeek ;XXXday
		vDay := NumGet(vOffset+0, 6, "Short") ;wDay ;e.g. 1=first XXXday of month, 5=last XXXday of month
		vHour := NumGet(vOffset+0, 8, "Short") ;wHour
		vMinute := NumGet(vOffset+0, 10, "Short") ;wMinute

		vDate := Format("{:04}{:02}01{:02}{:02}00", vYear, vMonth, vHour, vMinute)
		FormatTime, vWDay, % vDate, WDay
		EnvAdd, vDate, % Mod(8+vDayOfWeek-vWDay,7)+(vDay-1)*7, Days
		EnvAdd, vDate, % vBias + vBias%A_Index%, Minutes
		if !(SubStr(vDate, 5, 2) = vMonth)
			EnvAdd, vDate, -7, Days
		vDate%A_Index% := vDate

		(vMonth = 0) &&	(vBias%A_Index% := 0)
	}
	oArray := {}
	;date1 is date DST ends, date2 is date DST starts,
	;offset1 is offset when not DST, offset2 is offset when is DST
	if (vDate1 < vDate2) ;period between dates is not DST
		oArray := {date1:vDate1, date2:vDate2, offset1:vBias+vBias1, offset2:vBias+vBias2, name1:vDlt, name2:vStd}
	else ;period between dates is DST
		oArray := {date1:vDate2, date2:vDate1, offset1:vBias+vBias2, offset2:vBias+vBias1, name1:vStd, name2:vDlt}
	return oArray
}

;==================================================

;links (registry keys/structs/MUI):

;_TIME_ZONE_INFORMATION | Microsoft Docs
;https://docs.microsoft.com/en-us/windows/desktop/api/timezoneapi/ns-timezoneapi-_time_zone_information
;DYNAMIC_TIME_ZONE_INFORMATION (timezoneapi.h) | Microsoft Docs
;https://docs.microsoft.com/en-gb/windows/desktop/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information
;Dynamic daylight saving time provides support for time zones whose boundaries for daylight saving time change from year to year.
;Multilingual User Interface - Windows applications | Microsoft Docs
;https://docs.microsoft.com/en-gb/windows/desktop/Intl/multilingual-user-interface

;registry keys:

;HKLM\SYSTEM\CurrentControlSet\Control\TimeZoneInformation
;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

;structs:

;[size: 172]
;typedef struct _TIME_ZONE_INFORMATION {
;  LONG       Bias;
;  WCHAR      StandardName[32];
;  SYSTEMTIME StandardDate;
;  LONG       StandardBias;
;  WCHAR      DaylightName[32];
;  SYSTEMTIME DaylightDate;
;  LONG       DaylightBias;
;} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION, *LPTIME_ZONE_INFORMATION;

;[size: 44]
;typedef struct _REG_TZI_FORMAT
;{
;    LONG Bias;
;    LONG StandardBias;
;    LONG DaylightBias;
;    SYSTEMTIME StandardDate;
;    SYSTEMTIME DaylightDate;
;} REG_TZI_FORMAT;

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask For Help”

Who is online

Users browsing this forum: Albireo, Google [Bot], Odlanir, pn4265 and 248 guests