Dates7 min readExamples run on PHP 8.5.10

date_create_from_format()

Parse a date string that follows a known custom format into a DateTime object, with notes on defaults, failure cases and silent date rollover.

Signature

date_create_from_format(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false
Available since PHP 5.3.07.48.08.18.28.38.48.5All versions →

Purpose

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

  • $format is a string built from the same kind of format characters used by date(), for example Y (four-digit year), m (two-digit month), d (two-digit day), H, i and s. 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.
  • $datetime is the string to parse. It must match the format from start to end.
  • $timezone is an optional DateTimeZone. It defaults to null, which means the current default timezone is used. If the input string itself carries a timezone (for instance through the P or e format codes), that value wins and $timezone is 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.

Example
<?php
$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";
Output
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.

Example
<?php
$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";
Output
{"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.

Example
<?php
$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";
Output
{"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

  1. Using the result without checking for false, which leads to a fatal error when you call a method on it.
  2. Forgetting ! or | and getting a time of day that changes from run to run.
  3. Assuming the $timezone argument converts a value that already has an offset. It does not.
  4. Writing a format that does not match the input, such as d-m-Y for slash-separated text.
  5. Treating a successful return as proof that the date is real, when it may be a rolled-over value.
  • date_create() for flexible, free-form input
  • date_create_immutable_from_format() for an immutable result
  • date_format() to output a parsed value
  • date_get_last_errors() to inspect warnings and errors
  • date_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

Strict parsing with a reset
<?php
$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";
Output
2024-03-15T14:30:00+00:00
A timezone inside the string
<?php
$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";
Output
{"iso":"2024-03-15T14:30:00+02:00","timestamp":1710505800}
Failure versus silent rollover
<?php
$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";
Output
{"bad":false,"rolled":"2024-03-01","warnings":1}