checkdate

Checks whether a month, day and year form a real Gregorian calendar date, leap years included.

Signature
checkdate(int $month, int $day, int $year): bool
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$monthintThe month. Must be between 1 and 12 inclusive.
$dayintThe day. Must be within the number of days the given month has in the given year, so leap years are taken into account.
$yearintThe year. Must be between 1 and 32767 inclusive.

Return value

booltrue when the three values form a date that exists on the Gregorian calendar, false otherwise.

Errors

Does not throw. Out-of-range values simply return false.

checkdate answers one question: does this month/day/year combination exist on the Gregorian calendar? It knows how many days each month holds and it applies the leap-year rules, so checkdate(2, 29, 2024) is true while checkdate(2, 29, 2023) is false.

When to reach for it

Use it whenever a date arrives as three separate numbers: three <select> dropdowns, three CSV columns, three query parameters. It is the guard you put in front of mktime or a DateTimeImmutable constructor.

That guard matters because PHP's date builders do not reject impossible dates, they roll them over. mktime(0, 0, 0, 2, 30, 2025) silently gives you 2 March, and strtotime('2025-02-30') does the same. A typo becomes a plausible-looking wrong date that nothing downstream flags.

Argument order

The order is month, day, year. That is American order, not the day-first order most of the world writes, and feeding a d/m/Y tuple straight through is by far the most common bug with this function.

checkdate validates the calendar only. It says nothing about the time of day, timezones, or whether the date falls in a range your application accepts, so check those separately.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Validating a three-dropdown birth date form before writing it to the database
  • Rejecting impossible dates in an imported CSV instead of letting mktime roll them forward a day
  • Confirming that a user-supplied 29 February actually falls in a leap year

Frequently asked questions

Does checkdate handle leap years?

Yes. It applies the full Gregorian rule, so 29 February is valid in 2024 and 2000 but not in 2023 or 2100.

What is the argument order for checkdate?

Month, then day, then year — checkdate(12, 31, 2025). Passing day first is the usual cause of a valid date being reported as invalid.

How do I validate a date string like 2025-02-30?

Split it and pass the three integers to checkdate, or use date_parse and check that warning_count is 0. DateTimeImmutable and strtotime both roll invalid dates forward instead of failing.

More examples

Guarding a submitted form date

30 February does not exist, so the guard prints the rejection branch. Without it the same three numbers would have been accepted and stored as 2 March 2025.

Example · php
<?php
$input = ['year' => '2025', 'month' => '02', 'day' => '30'];

$m = (int) $input['month'];
$d = (int) $input['day'];
$y = (int) $input['year'];

if (checkdate($m, $d, $y)) {
    echo 'Accepted: ', sprintf('%04d-%02d-%02d', $y, $m, $d), "\n";
} else {
    echo "Rejected: {$input['year']}-{$input['month']}-{$input['day']} is not a real date\n";
}

Why mktime cannot be used as a validator

mktime happily returns 2025-03-02 for 30 February and 2026-01-01 for month 13, giving no hint that the input was wrong. checkdate returns false for both.

Example · php
<?php
date_default_timezone_set('UTC');

echo date('Y-m-d', mktime(0, 0, 0, 2, 30, 2025)), "\n";
var_dump(checkdate(2, 30, 2025));

echo date('Y-m-d', mktime(0, 0, 0, 13, 1, 2025)), "\n";
var_dump(checkdate(13, 1, 2025));

Finding leap years

Asking whether 29 February exists is a leap-year test. 2100 is divisible by 4 but not by 400, so it is not a leap year and checkdate correctly returns false.

Example · php
<?php
foreach (range(2020, 2030) as $year) {
    if (checkdate(2, 29, $year)) {
        echo $year, " is a leap year\n";
    }
}

echo checkdate(2, 29, 2100) ? "2100 is a leap year\n" : "2100 is not a leap year\n";

Discussion

  • Be the first to comment on this lesson.