Page 1 of 1

GetDSDT() [UDF]

Posted: 28 Jul 2017, 03:47
by BoBo
Zu folgender anfrage: https://autohotkey.com/boards/viewtopic.php?f=5&t=28355
... hat 'just me' einen satz funktionen bereitgestellt, welcher eine lokale systemabfrage zur 'sommer/winterzeit' ermöglicht! :thumbup:

Dazu wird aus der registry die lokale zeitzone ermittelt - und darauf basierend, ob ein zeitraum für 'sommer/winterzeit' (DSDT/STDT) definiert ist.
Nun ist es einfach festzustellen, ob die aktuelle systemzeit innerhalb dieses zeitraums liegt. Dazu benutzen wir eine funktion von 'VxE'.

Code: Select all

; https://autohotkey.com/boards/viewtopic.php?p=161785#p161785                                        by 'just me'      TZI2TIMEZONEINFORMATION() DateTimeFromSYSTEMTIME() GetWDayInMonth()
; https://autohotkey.com/board/topic/70962-need-program-to-determine-if-date-is-in-a-certain-range    by '[VxE] nli'    DateBetween()

#NoEnv
#SingleInstance, Force
#Include DateBetween().ahk

MsgBox % GetDSDT("start")		 ; retourniert den zeitstempel zum beginn der lokalen sommerzeit
MsgBox % GetDSDT("e")			 ; retourniert den zeitstempel zum ende der lokalen sommerzeit
MsgBox % GetDSDT("end")			 ; retourniert den zeitstempel zum ende der lokalen sommerzeit
MsgBox % GetDSDT("period")		 ; retourniert den zeitstempel zur dauer/periode der lokalen sommerzeit
MsgBox % GetDSDT()				 ; retourniert ob die lokale systemzeit innerhalb der lokalen sommerzeit liegt
MsgBox % GetDSDT(20171029030000) ; retourniert das der 29.10.2017 03:00:00 (noch) innerhalb der lokalen sommerzeit liegt
MsgBox % GetDSDT(20171029030001) ; retourniert das der 29.10.2017 03:00:01 (schon) außerhalb der lokalen sommerzeit liegt
MsgBox % GetDSDT(20170330)		 ; retourniert das der 30.03.2017 bereits innerhalb der lokalen sommerzeit liegt

GetDSDT(req="") {
		RegRead, TZ     , HKLM,% "SYSTEM\CurrentControlSet\Control\TimeZoneInformation"         , TimeZoneKeyName ; Ermitteln der lokalen zeitzone/timezone aus der registry
		RegRead, REGTZI , HKLM,% "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\" TZ  , TZI             ; Ermitteln der dazugehörigen 'time zone information' (binärer wert)

		TZI2TIMEZONEINFORMATION(REGTZI, TIMEZONEINFORMATION)
		DSDT := DateTimeFromSYSTEMTIME(&TIMEZONEINFORMATION + 152)                                                ; Ausgabe lokale sommerzeit (beginn) aus der TZI im format YYYYMMDD24HHMMSS
		STDT := DateTimeFromSYSTEMTIME(&TIMEZONEINFORMATION + 68)                                                 ; Ausgabe lokale sommerzeit (ende) aus der TZI im format   YYYYMMDD24HHMMSS
		If req Is Date
			Return % IsDateBetween(req,DSDT,STDT) = 0 ? "STDT" : "DSDT"
		res	 :=	(req = "start" || req = "s")	? DSDT
			 :	(req = "end"   || req = "e")	? STDT
			 :	(req = "period"|| req = "p")	? DSDT "-" STDT
			 :	(req = "") 						? IsDateBetween(A_Now,DSDT,STDT) = 0 ? "STDT" : "DSDT"            ; Ermitteln ob das aktuelle systemdatum innerhalb des definierten zeitraum liegt (lokale sommerzeit)
			 :  "ERROR"
		Return % res
		}


TZI2TIMEZONEINFORMATION(TZI, ByRef TIMEZONEINFORMATION) {                                                       ; Konvertiert 'time zone information'-werte in ein 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)
          }
;     MsgBox % (Month = 0 ? "" : Format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", Year, Month, Day, Hour, Min, Sec))          
;     Return   (Month = 0 ? "" : Format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", Year, Month, Day, Hour, Min, Sec))   ; YYYY-MM-DD HH:MM:SS
      Return   (Month = 0 ? "" : Format("{:04}{:02}{:02}{:02}{:02}{:02}", Year, Month, Day, Hour, Min, Sec))        ; YYYYMMDDHHMMSS
      }


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
      }
8-)

Re: GetDSDT() [UDF]

Posted: 28 Jul 2017, 04:19
by BoBo
Frage an 'just me' ...
Ich habe nicht explizit nach der für die MESZ/CEST resultierenden zeitdifferenz geschaut (UTC+02:00).
Wäre diese jedoch noch in deinem funktionsatz gebunkert, ließe sich damit doch ggf (nach formatierung) der nackte UTC output deiner GetNetworkTime()-UDF bespaßen?!
Return % DateBetween(GetNetworkTime()+zeitdifferenz,DSDT,STDT) = 0 ? "STDT" : "DSDT"

Re: GetDSDT() [UDF]

Posted: 28 Jul 2017, 05:41
by just me
Ich denke mal darüber nach.

Re: GetDSDT() [UDF]

Posted: 28 Jul 2017, 06:26
by just me
Sodele, die Funktion TZI2TIMEZONEINFORMATION()versorgt auch die Zeitdifferenzen (Bias) in der TIME_ZONE_INFORMATION Struktur. Diese Differenzen sind in Minuten abgelegt. Du kannst sie Dir von dort wie folgt holen:

Code: Select all

; Bias: Abweichung UTC <> lokale Zeit in Minuten
Bias := NumGet(TIMEZONEINFORMATION, 0,   "Int")
; StandardBias: zusätzliche Abweichung während der Standardzeit in Minuten - bisher immer 0
StdB := NumGet(TIMEZONEINFORMATION, 84,  "Int") 
; DaylightBias: zusätzliche Abweichung während der Sommerzeit in Minuten
DltB := NumGet(TIMEZONEINFORMATION, 168, "Int")
Die Umrechnung UTC <> lokale Zeit ist zunächst einmal so definiert:

Code: Select all

UTC := LokaleZeit + Bias + StandardBias
; bzw.
LokaleZeit := UTC - Bias - StandardBias
Während der Sommerzeit kommt noch ein +/- DaylightBias dazu.

Re: GetDSDT() [UDF]

Posted: 28 Jul 2017, 06:32
by BoBo
just me wrote:Sodele, die Funktion TZI2TIMEZONEINFORMATION()versorgt auch die Zeitdifferenzen (Bias) in der TIME_ZONE_INFORMATION Struktur. Diese Differenzen sind in Minuten abgelegt. Du kannst sie Dir von dort wie folgt holen:

[...]

Während der Sommerzeit kommt noch ein +/- DaylightBias dazu.
Als hätte ich es geahnt :lol: :thumbup: Wo doch in der US politik aktuell sehr oft der begriff "biased" auftaucht :wtf:
Thx für die recherche zur erweiterten funktionalität! M(u..) a(p..) !! ;)

(Fast) letzte frage, welche aufgabe hat die funktion GetWDayInMonth(), heißt, in welchem kontext wird diese information benötigt ??
Merci fürs schlaumachen :)

Re: GetDSDT() [UDF]

Posted: 28 Jul 2017, 08:28
by just me
Das ist der 'trickreiche' Teil. Die Schaltzeiten für die Sommer- und Winterzeit können entweder auf einem festen Datum liegen (immer am 01.04.) oder variabel sein (letzter Sonntag im März).

Bei festen Daten enthält die Registry die Information Jahr, Monat, Tag, Stunde, Minute, Sekunde. Zur Bestimmung des Datums muss dann nur das Jahr gegen das gewünschte ausgetauscht werden.

Bei variablen Daten ist das Jahr 0 und Tag enthält das Vorkommen des gewünschten Wochentags (WDay, 1 - 5, 5 = letzter), der hier ebenfalls angegeben sein muss (DayOfWeek). Für die Berechnung müssen dann das gewünschte Jahr eingesetzt und der zugehörige Tag ermittelt werden. Das macht GetWDayInMonth().

Das Verfahren hat Microsoft in der Beschreibung zu TIME_ZONE_INFORMATION dokumentiert.