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.
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:
- Er zijn periodes zonder "logische" verklaring
- Er zijn periodes zonder zomertijd
- Verschilllende periodes met zomertijd hebben verschillende methodes om ze te berekenen
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(0, 0, 0, $month, 1, $year));
if ($firstDayOfMonthDayName == $dayName) {
$firstMondayOfMonth = 1;
} elseif ($firstDayOfMonthDayName > $dayName) {
$firstMondayOfMonth = $dayName + 7 - $firstDayOfMonthDayName + 1;
} elseif ($firstDayOfMonthDayName < $dayName) {
$firstMondayOfMonth = $dayName - $firstDayOfMonthDayName + 1;
}
return $firstMondayOfMonth;
}
function lastDaynameOfMonth($dayName, $year, $month) {
$maxDaysOfMonth = date('t', mktime(0, 0, 0, $month, 1, $year));
$lastDayOfMonthDayName = date('N', mktime(0, 0, 0, $month, $maxDaysOfMonth, $year));
$lastDayOfMonth = -1;
if ($lastDayOfMonthDayName == $dayName) {
$lastDayOfMonth = $maxDaysOfMonth;
} elseif ($lastDayOfMonthDayName > $dayName) {
$lastDayOfMonth = $maxDaysOfMonth - $lastDayOfMonthDayName + $dayName;
} elseif ($lastDayOfMonthDayName < $dayName) {
$lastDayOfMonth = $maxDaysOfMonth - 7 - $lastDayOfMonthDayName + $dayName;
}
return $lastDayOfMonth;
}
echo firstDaynameOfMonth(DATENAME_SUNDAY, 1990, 5); //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(2, 0, 0, 4, firstDaynameOfMonth(DATENAME_MONDAY, $year, 4), $year);
$endST = mktime(2, 0, 0, 9, lastDaynameOfMonth(DATENAME_MONDAY, $year, 9), $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(0, 0, 0, 1, 5, 1916)) {
return false;
} elseif (
($theDate >= mktime(0, 0, 0, 1, 5, 1916) AND $theDate <= mktime(23, 0, 0, 9, 30, 1916)) OR
($theDate >= mktime(2, 0, 0, 4, 16, 1917) AND $theDate <= mktime(2, 0, 0, 9, 17, 1917)) OR
($theDate >= mktime(2, 0, 0, 3, 26, 1922) AND $theDate <= mktime(2, 0, 0, 10, 8, 1922)) OR
($theDate >= mktime(2, 0, 0, 6, 1, 1923) AND $theDate <= mktime(2, 0, 0, 10, 7, 1923)) OR
($theDate >= mktime(2, 0, 0, 3, 30, 1924) AND $theDate <= mktime(2, 0, 0, 10, 5, 1924)) OR
($theDate >= mktime(2, 0, 0, 6, 5, 1925) AND $theDate <= mktime(2, 0, 0, 10, 4, 1925)) OR
($theDate >= mktime(0, 0, 0, 5, 16, 1940) AND $theDate <= mktime(2, 0, 0, 11, 2, 1942)) OR
($theDate >= mktime(2, 0, 0, 3, 29, 1943) AND $theDate <= mktime(2, 0, 0, 10, 4, 1943)) OR
($theDate >= mktime(2, 0, 0, 4, 3, 1944) AND $theDate <= mktime(2, 0, 0, 10, 2, 1944)) OR
($theDate >= mktime(2, 0, 0, 4, 2, 1945) AND $theDate <= mktime(2, 0, 0, 9, 16, 1945)) 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:
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