Zomer en Wintertijd

Bavelt

Golden Member

Een tijdje geleden kreeg ik van Arco onderstaande code om te bepalen of het zomer-of wintertijd is:

Of 't zomer of wintertijd is kun je softwarematig makkelijk vaststellen.
Deze routine kan van een Europese of USA datum/tijd afleiden of het zomertijd (DST) is of niet...

Als het zomertijd is, is bit 0 van het resultaat '1'
Als er een zomer <-> wintertijd omschakeling is geweest sinds laatste aanroep, dan is bit 1 geset.

Als pZone=1 dan Europees, als pZone=2 dan USA tijd.

pic basic code:



Dim DSTState     As Byte
    OldDSTState  As Byte

Const DSTParm As Byte[16] = (31,5,31,2,3,10,2,3,14,2,7,2,3,11,2,2)

'==============================================================================
Sub Function Is_DST(Dim pZone As Byte) As Byte               'Tests if it's DST
'==============================================================================
  Dim lBegin, lEnd, lZone As Byte
  '----------------------------------------------------------------------------
  If pZone Then
    lZone = (pZone - 1) << 3
    Result = 0
    lBegin = (((5 * tYear) div 4) - (tYear div 100) + (tYear div 400)) Mod 7
    lEnd   = DstParm[lZone+2] - (lBegin+DstParm[lZone+3]) Mod 7
    lBegin = DstParm[lZone+0] - (lBegin+DstParm[lZone+1]) Mod 7
    '--------------------------------------------------------------------------
    If (tMonth <= DstParm[lZone+4]) Or (tMonth >= DstParm[lZone+5]) Then
      If ((tMonth = DstParm[lZone+4]) And (tMonthDay < lBegin)) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay > lEnd)) Or
         ((tMonth = DstParm[lZone+4]) And (tMonthDay = lBegin) And (tHour < DstParm[lZone+6])) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay = lEnd) And (tHour >= DstParm[lZone+7])) Then
        DSTState = 1
      End If
    End If
    If (tMonth >= DstParm[lZone+4]) Or (tMonth <= DstParm[lZone+5]) Then
      If ((tMonth = DstParm[lZone+4]) And (tMonthDay > lBegin)) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay < lEnd)) Or
         ((tMonth = DstParm[lZone+4]) And (tMonthDay = lBegin) And (tHour >= DstParm[lZone+6])) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay = lEnd) And (tHour < DstParm[lZone+7]-1)) Then
        DSTState = 2
      End If
    End If
    If DSTState = 0 Then
      If tMonth = DstParm[lZone+4] Then DSTState = 2 Else DSTState = 1 End If
    End If
    If DstState = 2 Then Result.0 = 1 End If
    If DstState = 1 Then Result.0 = 0 End If

    If OldDSTState <>  DSTState Then
      Result.1 = 1
      OldDSTState = DSTState
    Else
      Result.1 = 0
    End If
  End If
End Sub

Dus dacht ik dat dit zou moeten werken

pic basic code:



Const DSTParm As Byte[16] = (31,5,31,2,3,10,2,3,14,2,7,2,3,11,2,2)

Dim     DSTState     As Byte
        OldDSTState  As Byte
        tyear        As word
        tmonth       As byte
        tmonthday    As byte
        thour        As byte

Main:
 

 lata.6 = 0
 DSTState = 0
 OldDststate = 0

 thour     = 02
 tmonth    = 01
 tmonthday = 03
 tyear     = 2021

lata.6 = Is_Dst(1).0

Led aan zou zomertijd zijn, uit = wintertijd.
Maar het werkt niet. Alle Datums geven resultaat 0 (wintertijd).

Voor Tyear heb ik zowel 21 als 2021 geprobeerd (ik weet niet precies wat de routine verlangt).

Fouten zijn het bewijs dat je het probeert..
Bavelt

Golden Member

Wellicht doe ik iets niet goed in de aanroep.

Fouten zijn het bewijs dat je het probeert..
Arco

Special Member

Jaar moet gewoon het hele jaar zijn (2021)
Is een routine uit een bestaand project wat prima werkt. Ik zou eens ernaar moeten kijken, want het is nogal een complexe berekening...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
Bavelt

Golden Member

Op 3 september 2021 12:29:27 schreef Arco:
Jaar moet gewoon het hele jaar zijn (2021)
Is een routine uit een bestaand project wat prima werkt. Ik zou eens ernaar moeten kijken, want het is nogal een complexe berekening...

Klopt. Ik heb er ook al naar lopen turen...zo van: Wat gebeurt hier allemaal?"

pic basic code:

lBegin = (((5 * tYear) div 4) - (tYear div 100) + (tYear div 400)) Mod 7
    lEnd   = DstParm[lZone+2] - (lBegin+DstParm[lZone+3]) Mod 7
    lBegin = DstParm[lZone+0] - (lBegin+DstParm[lZone+1]) Mod 7

:?

Fouten zijn het bewijs dat je het probeert..

Ben ik nou dom, of…..

Je weet toch van elk jaar wanneer het wintertijd wordt ? Voor die datum is het dus zomertijd en daarna …. wintertijd. Waarom dus zo ingewikkeld uitrekenen ?

Ik snap blijkbaar iets niet :-)

Groet, Dirk

Hoe weet je die datum dan Lampie? Googlen?

"We cannot solve our problems with the same thinking we used when we created them" - Albert Einstein

PICbasic code

;Calculation SUMMER / Winter TIME
CALC:
;SUMDAY = 26 ; In het jaar 2000 begint de zomertijd op 26 Maart
;SUMDAY = 26 + 140 ; Aangezien we ieder jaar 1 dag terug gaan starten we met een hooggetal
;WINDAY = 29 ; In het jaar 2000 begint de Wintertijd op 29 Oktober
;WINDAY = 29 + 140 ; Aangezien we ieder jaar 1 dag terug gaan starten we met een hooggetal
; Lang genoeg tot 2099, 2100 is geen schrikkeljaar ( Leap Year )

MONTH = 10 * (GPS_Info[49]-48 ) + GPS_Info[50] - 48 ; Maand van het jaar ophalen
DAY = 10 * (GPS_Info[47]-48 ) + GPS_Info[48] - 48 ; Dag van de maand ophalen

;If MONTH > 3 AND MONTH < 10 then UUR = 3 ; Zomertijd van April tot Sept UTC + 3
If Z = 0 Then
If MONTH = 3 Then
If DAY > 24 Then
X = YEAR / 4 ; dit geeft het aantal schrikkeljaren aan
Y = X + YEAR ; Dit geeft het aantal dagen aan dat er af moet.
Y = 166 - Y ; bv voor 2098 dit is 24 schrik + 98 = 122 ( SUMDAY )
LUSz: ; Aftrekken van 166 = 44
Y = Y -7 ; Twee keer 7 eraf geeft Zondag 30 Maart
If Y > 31 Then GoTo LUSz ; Zomertijd start tussen 24 en 31 Maart
If Y = DAY Then
If UTC is 02 Then ; KLOPT NIET moet uur zijn ????
UUR = 3 ; van 2 uur naar 3 uur
W = 0
Z = 1
EndIf
EndIf
EndIf
EndIf
EndIf

If W = 0 Then
If MONTH = 10 Then
If DAY > 24 Then
X = YEAR / 4 ; dit geeft het aantal schrikkeljaren aan
X = X + YEAR ; Dit geeft het aantal dagen aan dat er af moet.
Y = 169 - X ; bv voor 2098 dit is 24 schrik + 98 = 122 WINDAY
LUSW: ; Aftrekken van 169 = 47
Y = Y -7 ; Drie keer 7 eraf geeft Zondag 26 Oktober
If Y > 31 Then GoTo LUSW ; Wintertijd start tussen 24 en 31 Oktober
If Y = DAY Then
If UTC is 00 Then
UUR = 2
W = 1
Z = 0
EndIf
EndIf
EndIf
EndIf
EndIf

Return

End ;Einde programma

';$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,071281,003.1,W*43

'; verklaring
';RMC Recommended Minimum sentence
';123519 Fix taken at 12:35:19 UTC
';A Status A=active /* The data status. [A] for valid data,[V] for invalid.
';4807.038,N Latitude 48 deg 07.038' N
';01131.000,E Longitude 11 deg 31.000' E
';022.4 Speed over the ground in knots
';084.4 Track angle in degrees True
';071281 Date - 7th of December 1981
';003.1,W Magnetic Variation /* The difference of magnetic direction from true north.
';*43 The checksum data, always begins with *
'; UTC time Winter = + 1 Zomer = + 2

'---bepalen zomer/wintertijd.---------------------------------------------------
; Begin, Einde Zomertijd
;2000 26 maart 29 oktober
;2012 25 maart 28 oktober
;2013 31 maart 27 oktober
;2014 30 maart 26 oktober
;2015 29 maart 25 oktober
;2016 27 maart 30 oktober
;2017 26 maart 29 oktober
;2018 25 maart 28 oktober
;2019 31 maart 27 oktober
;2020 29 maart 25 oktober

;2021 28 maart 31 oktober
;2022 27 maart 30 oktober
;2023 26 maart 29 oktober
;2024 31 maart 27 oktober
;2025 30 maart 26 oktober
;2026 29 maart 25 oktober
;2027 28 maart 31 oktober
;2028 26 maart 29 oktober
;2029 25 maart 28 oktober
;2030 31 maart 27 oktober

;2098 30 maart 26 oktober

Guus@Sint-Michielsgestel

Op 9 september 2021 09:05:14 schreef flipflop:
Hoe weet je die datum dan Lampie? Googlen?

Die datums liggen toch vast? Laatste zondag van maart en laatste zondag van oktober. Wat moet je dan nog met het jaartal?

Arco

Special Member

Zonder jaartal weet je niet op welke datum de laatste zondag valt. Zat inderdaad een foutje in de routine. (zie regel met '<<<<<<<<--------')
De code is vrij gecompliceerd omdat rekening gehouden moet worden met schrikkeljaar, en de uurtest is nogal lastig.
En US/EU verschillen maken het ook al niet makkelijker.

pic basic code:


Dim DSTState     As Byte
    OldDSTState  As Byte

Const DSTParm As Byte[16] = (31,5,31,2,3,10,2,3,14,2,7,2,3,11,2,2)

'==============================================================================
Sub Function Is_DST(Dim pZone As Byte) As Byte               'Tests if it's DST
'==============================================================================
  Dim lBegin, lEnd, lZone As Byte
  '----------------------------------------------------------------------------
  If pZone Then
    lZone = (pZone - 1) << 3
    Result = 0
    lBegin = (((5 * tYear) div 4) - (tYear div 100) + (tYear div 400)) Mod 7
    lEnd   = DstParm[lZone+2] - (lBegin+DstParm[lZone+3]) Mod 7
    lBegin = DstParm[lZone+0] - (lBegin+DstParm[lZone+1]) Mod 7
    '--------------------------------------------------------------------------
    If (tMonth <= DstParm[lZone+4]) Or (tMonth >= DstParm[lZone+5]) Then
      If ((tMonth = DstParm[lZone+4]) And (tMonthDay < lBegin)) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay > lEnd))   Or
         ((tMonth = DstParm[lZone+4]) And (tMonthDay = lBegin)  And (tHour < DstParm[lZone+6])) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay = lEnd)    And (tHour >= DstParm[lZone+7])) Then
        DSTState = 1
      End If
    End If
    If (tMonth >= DstParm[lZone+4]) Or (tMonth <= DstParm[lZone+5]) Then
      If ((tMonth = DstParm[lZone+4]) And (tMonthDay > lBegin)) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay < lEnd))   Or
         ((tMonth = DstParm[lZone+4]) And (tMonthDay = lBegin)  And (tHour >= DstParm[lZone+6])) Or
         ((tMonth = DstParm[lZone+5]) And (tMonthDay = lEnd)    And (tHour < DstParm[lZone+7]-1)) Then
        DSTState = 2
      End If
    End If
    If DSTState = 0 Then
      If (tMonth >= DstParm[lZone+4]) And (tMonth <= DstParm[lZone+5]) Then DSTState = 2 Else DSTState = 1 End If '<<<<<-----------
    End If
    If DstState = 2 Then Result.0 = 1 End If
    If DstState = 1 Then Result.0 = 0 End If

    If OldDSTState <>  DSTState Then
      Result.1 = 1
      OldDSTState = DSTState
    Else
      Result.1 = 0
    End If
  End If
End Sub
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Voor de opslag kan je misschien beter 100 jaar maal 3 bytes opslaan (jaartal - 2000 ) dag in maart en dag in oktober.

Dat lijkt me minder dan die code.

Arco

Special Member

Als je de US code eruit sloopt zit je ook aan iets van 300 bytes...
(US had ik nodig voor een apparaat wat wereldwijd verkocht wordt)

Met alleen opslaan ben je er trouwens niet. Er komt altijd code bij om de 2->3 of 3->2 uur situatie op de overgangsdagen te verwerken...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Onder Linux zijn ze afgestapt van het "uitrekenen" en is er gewoon een tabel die de komende 50 jaar vult conform de huidige regels. En eens in de zoveel tijd krijg je een "urgent" update omdat ergens een derdewereldland een week van te voren besluit het dit jaar toch anders te doen. (het verschilt per land!) Maar goed, voorlopig, voor 1 landje, kan je waarschijnlijk beter iets programmeren dan de tabel gebruiken...

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
buckfast_beekeeper

Golden Member

Op 9 september 2021 11:16:33 schreef Arco:
Zonder jaartal weet je niet op welke datum de laatste zondag valt. Zat inderdaad een foutje in de routine. (zie regel met '<<<<<<<<--------')
De code is vrij gecompliceerd omdat rekening gehouden moet worden met schrikkeljaar, en de uurtest is nogal lastig.
En US/EU verschillen maken het ook al niet makkelijker.
[...][/code]

Waarom moet je rekening houden met een schrikkeljaar? De overgang naar zomertijd is de laatste zondag van maart. Wintertijd is steeds op de laatste zondag van oktober. => in beide gevallen kan de switch niet gebeuren voor de 25ste van de maand. Ik heb geen behoefte daylight saving te gaan uitrekenen voor Rusland.

c code:


// routine om een long terug te geven.
// YYYY het jaartal
// MM de maand 01-12
// DD datum van de maand
// hh het uur 00-23
// mm de minuten 00-59
// ss de seconden 00-59
time_t tmConvert_t(int YYYY, byte MM, byte DD, byte hh, byte mm, byte ss)
{
  tmElements_t tmSet;
  tmSet.Year = YYYY - 1970;
  tmSet.Month = MM;
  tmSet.Day = DD;
  tmSet.Hour = hh;
  tmSet.Minute = mm;
  tmSet.Second = ss;
  return makeTime(tmSet);
}

// bereken begin daylight saving in UTC as long
// het omschakelen is steeds op 1 uur UTC
time_t begin_tm = tmConvert_t(jaar, 3, dag - weekdag, 1, 0, 0);

// bereken einde daylight saving in UTC
time_t eind_tm = tmConvert_t(jaar, 10, dag - weekdag, 1, 0, 0);

if (lngTijd >= begin_tm && lngTijd < eind_tm){
    dayLightSaving = true;
}
else{
    dayLightSaving = false;
}
Van Lambiek wordt goede geuze gemaakt.
Arco

Special Member

Wat is tmConvert_t? Die functie zal je dan toch ook moeten maken...
Schrikkeljaar moet je weten om de weekdag uit te kunnen rekenen.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
buckfast_beekeeper

Golden Member

De functie tmConvert_t is toch gegeven?

time.h bevat in zijn struct toch een tm_wday. Hoef je deze toch niet te gaan berekenen?

Van Lambiek wordt goede geuze gemaakt.
Arco

Special Member

Ik begrijp het niet erg. Wat is MakeTime()? Hoe kom je aan de waarde van de weekdag? Hoe behandel je de 2<->3 uur overgang?

[Bericht gewijzigd door Arco op donderdag 9 september 2021 18:54:25 (19%)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
Hewlett

Honourable Member

Het is een stukje uit een Arduino scetch.... Binnen de Arduino library functie makeTime wordt uiteraard rekening gehouden met schrikkeljaar logica en de locale van de machine, anders kun je UTC als time_t niet berekenen uit de geven datum en tijd :-). time_t is gewoon de Unix epoch (aantal seconden sinds 1-1-1970).

HamRadio PA2HK // Keep Calm and Carry On.
Hewlett

Honourable Member

Je originale (en aangepaste) algoritme werkt om een aantal redenen niet correct voor de US. Niet elke staat in de US gebruikt DST, de staat Hawaii gebruikt geen DST en het zuidelijke deel van de staat Arizona ook niet. Voor datums van voor 2007 werkt je algoritme niet, voor 2007 begon de zomertijd in de US voor alle staten behalve dus Hawaii en deel van Arizona op de eerste zondag in april en eindigde op laatste zondag in oktober. Nog verder in het verleden, voor 1987, was de zomertijd situatie in de US weer anders en er zijn nog een aantal punten in het verleden waarop de DST logical in de US is aangepast.

HamRadio PA2HK // Keep Calm and Carry On.
Arco

Special Member

Daar we toch niet terug kunnen reizen in de tijd, heeft het aanpassen voor alle ooit bedachte dst variaties in het verleden geen nut... (ik heb geen Delorean)...;)
De code zou veel langer worden zonder enkel nut. (wie gaat de klok op een apparaat instellen op 1987?). Datums van voor de actuele dag zijn irrelevant.
Als een staat geen dts gebruikt dan kun je die toch gewoon uitzetten? (keuze is 'gewoon', US, en EU)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Op 9 september 2021 20:51:28 schreef Arco:
Daar we toch niet terug kunnen reizen in de tijd, heeft het aanpassen voor alle ooit bedachte dst variaties in het verleden geen nut... (ik heb geen Delorean)...;)
De code zou veel langer worden zonder enkel nut. (wie gaat de klok op een apparaat instellen op 1987?). Datums van voor de actuele dag zijn irrelevant.
Als een staat geen dts gebruikt dan kun je die toch gewoon uitzetten? (keuze is 'gewoon', US, en EU)

In theorie kan je iets bouwen dat in het verleden werkt. Bijvoorbeeld 'Hoelang gebruiken we de Euro' op een display met uren.

Hewlett

Honourable Member

Wat je zegt klopt voor een deel. Uitzetten van DST kan niet altijd (vooral als je Basic software welke in de embedded controller draait dat niet aanbiedt). Je geeft hier in dit topic een Basic implementatie (best ranzige code ;-) ) welke alleen correct voor de Amerikaanse markt werkt vanaf 2007 en dat niet voor elke staat. Locale is in dit geheel belangrijk, daar hou je helemaal geen rekening mee. Je product is niet op Hawaii verkocht?

Ik wil alleen maar aangeven dat een correcte datum en tijd afhandeling complexe materie is.

HamRadio PA2HK // Keep Calm and Carry On.
Arco

Special Member

Op 9 september 2021 21:40:12 schreef Hewlett:
Wat je zegt klopt voor een deel. Uitzetten van DST kan niet altijd (vooral als je Basic software welke in de embedded controller draait dat niet aanbiedt). Je geeft hier in dit topic een Basic implementatie (best ranzige code ;-) ) welke alleen correct voor de Amerikaanse markt werkt vanaf 2007 en dat niet voor elke staat. Locale is in dit geheel belangrijk, daar hou je helemaal geen rekening mee. Je product is niet op Hawaii verkocht?

Ik zei toch dat de keuzes 'gewone tijd', US dst, EU dst zijn? Wat is daar niet duidelijk aan?

Ik zie niet in wat er 'ranzig' aan is? Gewoon recht toe recht aan code...
Ik ga ook niet voor ieder gehucht aparte code schrijven. Als er een gebied is wat afwijkt, kan ik dat bijvoegen in code, of de klant stelt het handmatig in.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com