Floris Luiten

Close-up van een blonde jongeman met bril die en kop koffie drinkt in een cafe

Floris Luiten is programmeur bij u0192. Hij houdt van programmeren, cijfers, logica en het bestuderen van het menselijk gedrag. Op deze website laat hij zich over deze en nog meer onderwerpen uit. Wil je weten wat Floris nu denkt? Volg hem via Twitter (@florisl).
Meer informatie over Floris Luiten

Zomertijd in Nederland

Hoewel PHP nu tijdzones ondersteund, is dit wat aan de late kant - de stabiele versie van PHP die ik draai heeft deze ondersteuning nog niet.

Dus wat als je zelf de tijd in UTC wilt omrekenen naar lokale tijd vanaf 1910 tot nu en de toekomst? Dan krijg je te maken met het geweldige ingewikkelde systeem "Nederlandse zomertijd".

Nederlandse zomertijd explained

Het project waar ik nu voor werk gaat terug tot 1910 in gegevens. Hierdoor heb je te maken met 3 tijdzones (AT, NT en MET). Een groot geluk dat de data niet verder teruggaat dan 1910, anders had ik te maken gehad met verschillende lokale tijdzones!

Volgens mij is zomertijd lastiger dan tijdzones. Sinds 1916 kent Nederland zomertijd (hoewel tussen 1945 en 1977 we geen zomertijd hadden). Er zijn verschillende methodes geweest om te berekenen wanneer de zomertijd begon en eindigde.

Hieronder een overzicht gemaakt aan de hand van gegevens van Herman Wessels (kolom 1 en 2). Kolom 3, 4 en 5 heb ik zelf toegevoegd. Hiermee hoop ik duidelijkheid te scheppen in de onduidelijkheid die zomertijd heet.

Zomertijd begin/einddatum in Nederland vanaf 1916 tot 1998
Start zomertijd Einde zomertijd Start verklaard Einde verklaard Methode
woensdag 5 januari 1916 zaterdag 30 september 1916     ?
maandag 16 april 1917 maandag 17 september 1917     ?
maandag 1 april 1918 maandag 30 september 1918 Eerste maandag van april Laatste maandag van september 1
maandag 7 april 1919 maandag 29 september 1919 Eerste maandag van april Laatste maandag van september 1
maandag 5 april 1920 maandag 27 september 1920 Eerste maandag van april Laatste maandag van september 1
maandag 4 april 1921 maandag 26 september 1921 Eerste maandag van april Laatste maandag van september 1
zondag 26 maart 1922 zondag 8 oktober 1922 Laatste zondag van maart Eerste zondag op of na 2 oktober b
vrijdag 1 juni 1923 zondag 7 oktober 1923 Eerste vrijdag van juni Eerste zondag op of na 2 oktober b
zondag 30 maart 1924 zondag 5 oktober 1924 Laatste zondag van maart Eerste zondag op of na 2 oktober b
vrijdag 5 juni 1925 zondag 4 oktober 1925 Eerste vrijdag van juni Eerste zondag op of na 2 oktober b
zaterdag 15 mei 1926 zondag 3 oktober 1926 15 mei Eerste zondag op of na 2 oktober 2
zondag 15 mei 1927 zondag 2 oktober 1927 15 mei Eerste zondag op of na 2 oktober 2
dinsdag 15 mei 1928 zondag 7 oktober 1928 15 mei Eerste zondag op of na 2 oktober 2
woensdag 15 mei 1929 zondag 6 oktober 1929 15 mei Eerste zondag op of na 2 oktober 2
donderdag 15 mei 1930 zondag 5 oktober 1930 15 mei Eerste zondag op of na 2 oktober 2
vrijdag 15 mei 1931 zondag 4 oktober 1931 15 mei Eerste zondag op of na 2 oktober 2
zondag 22 mei 1932 zondag 2 oktober 1932 22 mei Eerste zondag op of na 2 oktober 2d
maandag 15 mei 1933 zondag 8 oktober 1933 15 mei Eerste zondag op of na 2 oktober 2
dinsdag 15 mei 1934 zondag 7 oktober 1934 15 mei Eerste zondag op of na 2 oktober 2
woensdag 15 mei 1935 zondag 6 oktober 1935 15 mei Eerste zondag op of na 2 oktober 2
vrijdag 15 mei 1936 zondag 4 oktober 1936 15 mei Eerste zondag op of na 2 oktober 2
zaterdag 22 mei 1937 zondag 3 oktober 1937 22 mei Eerste zondag op of na 2 oktober 2d
zondag 15 mei 1938 zondag 2 oktober 1938 15 mei Eerste zondag op of na 2 oktober 2
maandag 15 mei 1939 zondag 8 oktober 1939 15 mei Eerste zondag op of na 2 oktober 2
donderdag 16 mei 1940 maandag 2 november 1942     ?
maandag 29 maart 1943 maandag 4 oktober 1943 Eerste maandag na 28 maart Eerste maandag van oktober a
maandag 3 april 1944 maandag 2 oktober 1944 Eerste maandag na 28 maart Eerste maandag van oktober a
maandag 2 april 1945 zondag 16 september 1945     ?
zondag 3 april 1977 zondag 25 september 1977 Eerste zondag van april Laatste zondag op of voor 1 oktober 3
zondag 2 april 1978 zondag 1 oktober 1978 Eerste zondag van april Laatste zondag op of voor 1 oktober 3
zondag 1 april 1979 zondag 30 september 1979 Eerste zondag van april Laatste zondag op of voor 1 oktober 3
zondag 6 april 1980 zondag 28 september 1980 Eerste zondag van april Laatste zondag op of voor 1 oktober 3
zondag 29 maart 1981 zondag 27 september 1981 Laatste zondag van maart Laatste zondag in september 4
zondag 28 maart 1982 zondag 26 september 1982 Laatste zondag van maart Laatste zondag in september 4
zondag 27 maart 1983 zondag 25 september 1983 Laatste zondag van maart Laatste zondag in september 4
zondag 25 maart 1984 zondag 30 september 1984 Laatste zondag van maart Laatste zondag in september 4
zondag 31 maart 1985 zondag 29 september 1985 Laatste zondag van maart Laatste zondag in september 4
zondag 30 maart 1986 zondag 28 september 1986 Laatste zondag van maart Laatste zondag in september 4
zondag 29 maart 1987 zondag 27 september 1987 Laatste zondag van maart Laatste zondag in september 4
zondag 27 maart 1988 zondag 25 september 1988 Laatste zondag van maart Laatste zondag in september 4
zondag 26 maart 1989 zondag 24 september 1989 Laatste zondag van maart Laatste zondag in september 4
zondag 25 maart 1990 zondag 30 september 1990 Laatste zondag van maart Laatste zondag in september 4
zondag 31 maart 1991 zondag 29 september 1991 Laatste zondag van maart Laatste zondag in september 4
zondag 29 maart 1992 zondag 27 september 1992 Laatste zondag van maart Laatste zondag in september 4
zondag 28 maart 1993 zondag 26 september 1993 Laatste zondag van maart Laatste zondag in september 4
zondag 27 maart 1994 zondag 25 september 1994 Laatste zondag van maart Laatste zondag in september 4
zondag 26 maart 1995 zondag 24 september 1995 Laatste zondag van maart Laatste zondag in september 4
zondag 31 maart 1996 zondag 27 oktober 1996 Laatste zondag van maart Laatste zondag van oktober 5
zondag 30 maart 1997 zondag 26 oktober 1997 Laatste zondag van maart Laatste zondag van oktober 5
zondag 29 maart 1998 zondag 25 oktober 1998 Laatste zondag van maart Laatste zondag van oktober 5

Vanaf 1996 blijft methode 5 van toepassing tot heden (2012) en heeft 't geen zin om de lijst nog langer te maken.

Zoals je ziet in 't overzicht heb ik gepoogd om de berekening te verklaren wanneer de zomertijd begint en eindigt. Elke methode waarvan ik vind dat deze de moeite waard is om te implementeren als functie heb ik genummerd (1-5). Methodes die ik niet de moeite waard vind om te implementeren (bijvoorbeeld omdat er slechts 2 jaren gebruik van maken) heb ik genummer a.d.h.v. letters (a-d). Methodes die ik niet kon verklaren omdat er te weinig gegevens beschikbaar waren heb ik gemarkeerd met een '?'.

Deze wirwar van informatie geeft mij een aantal dingen waar ik rekening mee moet houden:

Ik begreep al snel dat ik de individuele methodes in aparte functies ging maken en daar dan één functie voor te zetten die de "uitzonderings"periodes kan vinden. Helaas kent PHP geen standaard functie om "de laatste zondag van maart" te kunnen berkenen, dus laten we daar mee beginnen.

De laatste zondag, eerste zondag enzovoorts

De functies om eerste/laatste dag in een bepaalde maand te berekenen is niet zo ingewikkel doordat je de date('N') kunt gebruiken. Met deze functie kun je exact zien welke weekdag het is op de eerste dag van de maand, om vanuit daaruit te berekenen waar dan de gewenste weekdag ligt.

De code is als volgt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
define
('DATENAME_MONDAY'1);
define('DATENAME_TUESDAY'2);
define('DATENAME_WEDNESDAY'3);
define('DATENAME_THURSDAY'4);
define('DATENAME_FRIDAY'5);
define('DATENAME_SATURDAY'6);
define('DATENAME_SUNDAY'7);

function 
firstDaynameOfMonth($dayName$year$month) {
  
$firstDayOfMonthDayName date('N'mktime(000$month1$year)); 
  if (
$firstDayOfMonthDayName == $dayName) {
    
$firstMondayOfMonth 1;
  } elseif (
$firstDayOfMonthDayName $dayName) {
    
$firstMondayOfMonth $dayName $firstDayOfMonthDayName 1;
  } elseif (
$firstDayOfMonthDayName $dayName) {
    
$firstMondayOfMonth $dayName $firstDayOfMonthDayName 1;
  }
  return 
$firstMondayOfMonth;
}
function 
lastDaynameOfMonth($dayName$year$month) {
  
$maxDaysOfMonth date('t'mktime(000$month1$year));
  
$lastDayOfMonthDayName date('N'mktime(000$month$maxDaysOfMonth$year));
    
  
$lastDayOfMonth = -1;
  if (
$lastDayOfMonthDayName == $dayName) {
    
$lastDayOfMonth $maxDaysOfMonth;
  } elseif (
$lastDayOfMonthDayName $dayName) {
    
$lastDayOfMonth $maxDaysOfMonth $lastDayOfMonthDayName $dayName;
  } elseif (
$lastDayOfMonthDayName $dayName) {
    
$lastDayOfMonth $maxDaysOfMonth $lastDayOfMonthDayName $dayName;
  }
  return 
$lastDayOfMonth;
}

echo 
firstDaynameOfMonth(DATENAME_SUNDAY19905); //Prints '6'

Hoewel ik deze functies zeer uitgebreid kan uitleggen, denk ik dat ze redelijk voor zich spreken. Ga je gang, test ze maar of 't werkt! Hou wel rekening met de beperkingen van de date() functie in PHP.

Methode 1 uitgelegd

Nadat je deze functies hebt, is de rest eigelijk zeer gemakkelijk. Snap je nog niet wat ik bedoel? Laten we dan methode 1 (start op eerste maandag in april, einde op laatste maandag van september) samen doen:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function CETisSummerTimeMethodOne($year$month$day$hour$minute$second) {
  if (
$year >= 1918 AND $year <= 1921) {
    
$startST mktime(2004firstDaynameOfMonth(DATENAME_MONDAY$year4), $year);
    
$endST mktime(2009lastDaynameOfMonth(DATENAME_MONDAY$year9), $year);
    
    
$theDate mktime($hour$minute$second$month$day$year);
    if (
$theDate >= $startST AND $theDate <= $endST) {
      return 
true;
    }
  }
  return 
false;    
}

We kijken eerst of de datum überhaupt in de juiste datumreeks ligt (in de jaren 1918, 1919, 1920, 1921). Als dit 't geval is berekenen we van dat jaar de eerste maandag in april als startST. Daarna berekenen we de laatste maandag van september als endST. Dan hoef je enkel nog te kijken of de datum in deze reeks ligt - is dat 't geval? Dan valt de datum in de zomertijd, anders niet. 

CETisSummerTime uitgelegd

Als je alle individuele methodes hebt geïmplementeerd blijven alleen de "uitzonderings-regels" nog over. Deze heb ik simpelweg in een if-statement gezet tesamen met de vijf methodes als volgt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
function CETisSummerTime($year$month$day$hour$minute$second) {
  
$theDate mktime($hour$minute$second$month$day$year);
  if (
$theDate mktime(000151916)) {
    return 
false;
  } elseif (
      (
$theDate >= mktime(000151916) AND $theDate <= mktime(23009301916)) OR
      (
$theDate >= mktime(2004161917) AND $theDate <= mktime(2009171917)) OR 
      (
$theDate >= mktime(2003261922) AND $theDate <= mktime(2001081922)) OR
      (
$theDate >= mktime(200611923) AND $theDate <= mktime(2001071923)) OR
      (
$theDate >= mktime(2003301924) AND $theDate <= mktime(2001051924)) OR
      (
$theDate >= mktime(200651925) AND $theDate <= mktime(2001041925)) OR
      (
$theDate >= mktime(0005161940) AND $theDate <= mktime(2001121942)) OR
      (
$theDate >= mktime(2003291943) AND $theDate <= mktime(2001041943)) OR
      (
$theDate >= mktime(200431944) AND $theDate <= mktime(2001021944)) OR
      (
$theDate >= mktime(200421945) AND $theDate <= mktime(2009161945)) OR
      (
CETisSummerTimeMethodOne($year$month$day$hour$minute$second)) OR
      (
CETisSummerTimeMethodTwo($year$month$day$hour$minute$second)) OR
      (
CETisSummerTimeMethodThree($year$month$day$hour$minute$second)) OR
      (
CETisSummerTimeMethodFour($year$month$day$hour$minute$second)) OR
      (
CETisSummerTimeMethodFive($year$month$day$hour$minute$second))) {
    return 
true;
  }
  return 
false;
}

Et voilà, je eigen "perfecte" manier om te controleren of een bepaalde tijd in MET of MEZT is.

Twijfel je nog aan mijn implementatie? Dat deed ik ook... Totdat ik aan de hand van de CETisSummerTime()-functie heb berekend op welke dagen zomertijd begint en eindigt. Deze heb ik een tabel naast de gegevens van Herman Wessels gezet en het verschil tussen de twee berekend.

Dus voor de ongelovigen, hier het bewijs dat mijn functie zeker goed werkt:

 

Bewijs dat CETisSummerTime() werkt
Start volgens H.W. Einde volgens H.W. Start volgens CETisSummerTime Einde volgens CETisSummerTime Start verschil Einde verschil
woensdag 5 januari 1916 zaterdag 30 september 1916 05-01-1916 30-09-1916 0 0
maandag 16 april 1917 maandag 17 september 1917 16-04-1917 17-09-1917 0 0
maandag 1 april 1918 maandag 30 september 1918 01-04-1918 30-09-1918 0 0
maandag 7 april 1919 maandag 29 september 1919 07-04-1919 29-09-1919 0 0
maandag 5 april 1920 maandag 27 september 1920 05-04-1920 27-09-1920 0 0
maandag 4 april 1921 maandag 26 september 1921 04-04-1921 26-09-1921 0 0
zondag 26 maart 1922 zondag 8 oktober 1922 26-03-1922 08-10-1922 0 0
vrijdag 1 juni 1923 zondag 7 oktober 1923 01-06-1923 07-10-1923 0 0
zondag 30 maart 1924 zondag 5 oktober 1924 30-03-1924 05-10-1924 0 0
vrijdag 5 juni 1925 zondag 4 oktober 1925 05-06-1925 04-10-1925 0 0
zaterdag 15 mei 1926 zondag 3 oktober 1926 15-05-1926 03-10-1926 0 0
zondag 15 mei 1927 zondag 2 oktober 1927 15-05-1927 02-10-1927 0 0
dinsdag 15 mei 1928 zondag 7 oktober 1928 15-05-1928 07-10-1928 0 0
woensdag 15 mei 1929 zondag 6 oktober 1929 15-05-1929 06-10-1929 0 0
donderdag 15 mei 1930 zondag 5 oktober 1930 15-05-1930 05-10-1930 0 0
vrijdag 15 mei 1931 zondag 4 oktober 1931 15-05-1931 04-10-1931 0 0
zondag 22 mei 1932 zondag 2 oktober 1932 22-05-1932 02-10-1932 0 0
maandag 15 mei 1933 zondag 8 oktober 1933 15-05-1933 08-10-1933 0 0
dinsdag 15 mei 1934 zondag 7 oktober 1934 15-05-1934 07-10-1934 0 0
woensdag 15 mei 1935 zondag 6 oktober 1935 15-05-1935 06-10-1935 0 0
vrijdag 15 mei 1936 zondag 4 oktober 1936 15-05-1936 04-10-1936 0 0
zaterdag 22 mei 1937 zondag 3 oktober 1937 22-05-1937 03-10-1937 0 0
zondag 15 mei 1938 zondag 2 oktober 1938 15-05-1938 02-10-1938 0 0
maandag 15 mei 1939 zondag 8 oktober 1939 15-05-1939 08-10-1939 0 0
donderdag 16 mei 1940 maandag 2 november 1942 16-05-1940 02-11-1942 0 0
maandag 29 maart 1943 maandag 4 oktober 1943 29-03-1943 04-10-1943 0 0
maandag 3 april 1944 maandag 2 oktober 1944 03-04-1944 02-10-1944 0 0
maandag 2 april 1945 zondag 16 september 1945 02-04-1945 16-09-1945 0 0
zondag 3 april 1977 zondag 25 september 1977 03-04-1977 25-09-1977 0 0
zondag 2 april 1978 zondag 1 oktober 1978 02-04-1978 01-10-1978 0 0
zondag 1 april 1979 zondag 30 september 1979 01-04-1979 30-09-1979 0 0
zondag 6 april 1980 zondag 28 september 1980 06-04-1980 28-09-1980 0 0
zondag 29 maart 1981 zondag 27 september 1981 29-03-1981 27-09-1981 0 0
zondag 28 maart 1982 zondag 26 september 1982 28-03-1982 26-09-1982 0 0
zondag 27 maart 1983 zondag 25 september 1983 27-03-1983 25-09-1983 0 0
zondag 25 maart 1984 zondag 30 september 1984 25-03-1984 30-09-1984 0 0
zondag 31 maart 1985 zondag 29 september 1985 31-03-1985 29-09-1985 0 0
zondag 30 maart 1986 zondag 28 september 1986 30-03-1986 28-09-1986 0 0
zondag 29 maart 1987 zondag 27 september 1987 29-03-1987 27-09-1987 0 0
zondag 27 maart 1988 zondag 25 september 1988 27-03-1988 25-09-1988 0 0
zondag 26 maart 1989 zondag 24 september 1989 26-03-1989 24-09-1989 0 0
zondag 25 maart 1990 zondag 30 september 1990 25-03-1990 30-09-1990 0 0
zondag 31 maart 1991 zondag 29 september 1991 31-03-1991 29-09-1991 0 0
zondag 29 maart 1992 zondag 27 september 1992 29-03-1992 27-09-1992 0 0
zondag 28 maart 1993 zondag 26 september 1993 28-03-1993 26-09-1993 0 0
zondag 27 maart 1994 zondag 25 september 1994 27-03-1994 25-09-1994 0 0
zondag 26 maart 1995 zondag 24 september 1995 26-03-1995 24-09-1995 0 0
zondag 31 maart 1996 zondag 27 oktober 1996 31-03-1996 27-10-1996 0 0
zondag 30 maart 1997 zondag 26 oktober 1997 30-03-1997 26-10-1997 0 0
zondag 29 maart 1998 zondag 25 oktober 1998 29-03-1998 25-10-1998 0 0

En zoals altijd, download je simpelweg het hele script.

Bekijk andere blog posts