Signature
date_create_from_format(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|falsePurpose
date_create_from_format() turns a string into a DateTime object when you know the exact layout of that string. Unlike date_create() or strtotime(), which guess at many human-readable layouts, this function reads the input strictly according to a format you supply. That makes it a good fit for imports, form fields and API values such as 15/03/2024 14:30, where a guess could swap day and month.
The manual describes it as a procedural alias of DateTime::createFromFormat(). The two behave the same, so you can pick whichever style your codebase uses.
Signature
Parameters and defaults
$formatis a string built from the same kind of format characters used bydate(), for exampleY(four-digit year),m(two-digit month),d(two-digit day),H,iands. Any character that is not a format code must match the input literally. Escape a letter with a backslash to match it as plain text.$datetimeis the string to parse. It must match the format from start to end.$timezoneis an optionalDateTimeZone. It defaults tonull, which means the current default timezone is used. If the input string itself carries a timezone (for instance through thePoreformat codes), that value wins and$timezoneis ignored.
Return value and failure
On success you get a DateTime object, which is mutable. On failure the function returns false. Typical causes are text that does not match the format, missing digits, or leftover characters after the format has been consumed. Always compare the result with false before using it. date_get_last_errors() reports details after a call, and in PHP 8.2 and later it returns false when there were no warnings or errors.
Example 1: strict parsing with a reset
The ! character at the start of a format resets all fields to the Unix epoch before parsing. Fields you do not mention then become predictable instead of being copied from the current time.
$d = date_create_from_format('!d/m/Y H:i', '15/03/2024 14:30', new DateTimeZone('UTC'));
echo date_format($d, 'Y-m-d\TH:i:sP'), "\n";2024-03-15T14:30:00+00:00
Example 2: a timezone inside the string
When the input contains an offset, the offset is used and the timezone argument does not override it. The timestamp shows the real instant: 14:30 at +02:00 is 12:30 UTC.
$d = date_create_from_format('!Y-m-d H:i P', '2024-03-15 14:30 +02:00', new DateTimeZone('UTC'));
echo json_encode([
'iso' => date_format($d, 'c'),
'timestamp' => date_timestamp_get($d),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"iso":"2024-03-15T14:30:00+02:00","timestamp":1710505800}
Example 3: failure versus silent rollover
Text with no digits cannot match Y-m-d, so the call returns false. A well-formed but impossible date such as February 30 does not fail. It rolls over to March 1 and records a warning.
$bad = date_create_from_format('!Y-m-d', 'not a date', new DateTimeZone('UTC'));
$rolled = date_create_from_format('!Y-m-d', '2024-02-30', new DateTimeZone('UTC'));
$errors = date_get_last_errors();
echo json_encode([
'bad' => $bad,
'rolled' => date_format($rolled, 'Y-m-d'),
'warnings' => $errors['warning_count'],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"bad":false,"rolled":"2024-03-01","warnings":1}
Edge cases
- Missing fields use the current time. Without
!or|, any date or time part absent from the format is filled from the system clock. If you parse only a date, the time of day may come from now. Start the format with!to reset to the epoch, or end it with|to reset only the fields that were not parsed. - Invalid dates overflow. Parsing can succeed even when the calendar date does not exist. Check
date_get_last_errors()for warnings if you need strict validation. - Trailing data is an error. If the string has extra characters the format does not describe, parsing fails.
- Literal text. Separators such as
/,-and spaces must match exactly. Escape letters that would otherwise be read as format codes.
Common mistakes
- Using the result without checking for
false, which leads to a fatal error when you call a method on it. - Forgetting
!or|and getting a time of day that changes from run to run. - Assuming the
$timezoneargument converts a value that already has an offset. It does not. - Writing a format that does not match the input, such as
d-m-Yfor slash-separated text. - Treating a successful return as proof that the date is real, when it may be a rolled-over value.
Related functions
date_create()for flexible, free-form inputdate_create_immutable_from_format()for an immutable resultdate_format()to output a parsed valuedate_get_last_errors()to inspect warnings and errorsdate_default_timezone_set()to control the default zone
Recap
Use date_create_from_format() when the input layout is known and fixed. Start with ! or end with | to avoid hidden current-time values, check for false, and inspect the last errors when impossible dates must be rejected.
Tested examples
$d = date_create_from_format('!d/m/Y H:i', '15/03/2024 14:30', new DateTimeZone('UTC'));
echo date_format($d, 'Y-m-d\TH:i:sP'), "\n";
2024-03-15T14:30:00+00:00
$d = date_create_from_format('!Y-m-d H:i P', '2024-03-15 14:30 +02:00', new DateTimeZone('UTC'));
echo json_encode([
'iso' => date_format($d, 'c'),
'timestamp' => date_timestamp_get($d),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"iso":"2024-03-15T14:30:00+02:00","timestamp":1710505800}
$bad = date_create_from_format('!Y-m-d', 'not a date', new DateTimeZone('UTC'));
$rolled = date_create_from_format('!Y-m-d', '2024-02-30', new DateTimeZone('UTC'));
$errors = date_get_last_errors();
echo json_encode([
'bad' => $bad,
'rolled' => date_format($rolled, 'Y-m-d'),
'warnings' => $errors['warning_count'],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"bad":false,"rolled":"2024-03-01","warnings":1}