Kryteria:
Każdy rok podzielny przez 4 jest rokiem przestępnym, chyba że dzieli się go przez 100, chyba że jest podzielny przez 400. A więc:
2004 - leap year - divisible by 4
1900 - not a leap year - divisible by 4, but also divisible by 100
2000 - leap year - divisible by 4, also divisible by 100, but divisible by 400
Luty ma 29 dni w roku przestępnym i 28 dni, gdy nie jest to rok przestępny
30 dni w kwietniu, czerwcu, wrześniu i listopadzie
31 dni w styczniu, marcu, maju, lipcu, sierpniu, październiku i grudniu
Test:
Następujące daty powinny przejść walidację:
1976-02-29
2000-02-29
2004-02-29
1999-01-31
Poniższe daty powinny zakończyć się niepowodzeniem:
2015-02-29
2015-04-31
1900-02-29
1999-01-32
2015-02-00
Zasięg:
Będziemy testować daty od 1 stycznia 1000 do 31 grudnia 2999. Z technicznego punktu widzenia, obecnie używany kalendarz gregoriański wszedł do użytku dopiero w 1753 r. Dla Imperium Brytyjskiego i w różnych latach w 1600 r. Dla krajów w Europie, ale nie zamierzam martw się o to.
Regex do przetestowania na rok przestępny:
Lata podzielne przez 400:
1200|1600|2000|2400|2800
can be shortened to:
(1[26]|2[048])00
if you wanted all years from 1AD to 9999 then this would do it:
(0[48]|[13579][26]|[2468][048])00
if you're happy with accepting 0000 as a valid year then it can be shortened:
([13579][26]|[02468][048])00
Lata podzielne przez 4:
[12]\d([02468][048]|[13579][26])
Lata podzielne przez 100:
[12]\d00
Nie można podzielić przez 100:
[12]\d([1-9]\d|\d[1-9])
Lata podzielne przez 100, ale nie przez 400:
((1[1345789])|(2[1235679]))00
Podzielna przez 4, ale nie przez 100:
[12]\d([2468][048]|[13579][26]|0[48])
Lata przestępne:
divisible by 400 or (divisible by 4 and not divisible by 100)
((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48])
Nie można podzielić przez 4:
[12]\d([02468][1235679]|[13579][01345789])
To nie jest rok przestępny:
Not divisible by 4 OR is divisible by 100 but not by 400
([12]\d([02468][1235679]|[13579][01345789]))|(((1[1345789])|(2[1235679]))00)
Ważny miesiąc i dzień z wyłączeniem lutego (MM-DD):
((01|03|05|07|08|10|12)-(0[1-9]|[12]\d|3[01]))|((04|06|09|11)-(0[1-9]|[12]\d|30))
shortened to:
((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30))
Luty z 28 dniami:
02-(0[1-9]|1\d|2[0-8])
Luty z 29 dniami:
02-(0[1-9]|[12]\d)
Ważna data:
(leap year followed by (valid month-day-excluding-february OR 29-day-february))
OR
(non leap year followed by (valid month-day-excluding-february OR 28-day-february))
((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8]))))
Więc masz to wyrażenie regularne dla dat między 1 stycznia 1000 a 31 grudnia 2999 w formacie RRRR-MM-DD.
Podejrzewam, że można to trochę skrócić, ale zostawię to komuś innemu.
To będzie pasować do wszystkich ważnych dat. Jeśli chcesz, aby był ważny tylko wtedy, gdy zawiera tylko jedną datę i nic więcej, zawiń go w ten ^( )$
sposób:
^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$
Jeśli chcesz, aby była to opcjonalna data (tj. Może być pusta lub ważna), dodaj ^$|
na początku, tak jak poniżej :
^$|^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$
date("Y-m-d", strtotime("2012-09-12"))=="2012-09-12";
lub PHPcheckdate ( int $month , int $day , int $year )
.