Dates7 min readExamples run on PHP 8.5.10

date_create_immutable_from_format()

Parse a date string with an explicit format into a DateTimeImmutable object, and learn how to handle the false result, timezones and unspecified time fields.

Signature

date_create_immutable_from_format(
    string $format,
    string $datetime,
    ?DateTimeZone $timezone = null
): DateTimeImmutable|false
Available since PHP 5.5.07.48.08.18.28.38.48.5All versions →

Purpose

date_create_immutable_from_format() turns a date string into a DateTimeImmutable object when you already know the exact layout of the string. Unlike date_create_immutable(), which guesses at many informal formats, this function demands a format you declare. That makes it a good fit for input such as 05/11/2024, where a guessing parser could confuse day and month.

The manual describes it as a procedural alias of DateTimeImmutable::createFromFormat(). Both behave the same way, so pick whichever style suits your codebase.

Signature

Parameters and defaults

  • $format: a string of date format characters. Common ones are Y (four-digit year), m and d (two-digit month and day), j (day without leading zero), H, i and s (time parts) and M (short month name such as Mar). A backslash escapes a literal character.
  • $datetime: the string to parse. It must match $format completely.
  • $timezone: an optional DateTimeZone. It defaults to null, which means the current default timezone (see date_default_timezone_set()). It is ignored when the parsed string carries its own timezone or is a Unix timestamp.

Return value and failure

On success you get a new DateTimeImmutable. On failure the function returns false; it does not throw. Typical causes are a string that does not match the format, leftover characters after the format has been consumed, or a value that cannot be read for a given format character. Always compare the result with false before calling methods on it.

Example 1: parsing with a timezone

The string below has no timezone information, so the third argument decides how it is interpreted. The result can then be converted to another zone without changing the instant in time.

Example
<?php
date_default_timezone_set('UTC');
$tz = new DateTimeZone('Asia/Tokyo');
$local = date_create_immutable_from_format('Y-m-d H:i:s', '2024-03-15 09:00:00', $tz);
$utc = $local->setTimezone(new DateTimeZone('UTC'));
echo $local->format('c'), "\n";
echo $utc->format('c'), "\n";
Output
2024-03-15T09:00:00+09:00
2024-03-15T00:00:00+00:00

Example 2: the ! modifier and immutability

When the format leaves out time fields, PHP fills them from the current clock. Starting the format with ! resets all fields to the Unix epoch values first, so unspecified time parts become zero and the result is deterministic. The object is also immutable: modify() returns a new object and leaves the original untouched.

Example
<?php
date_default_timezone_set('UTC');
$start = date_create_immutable_from_format('!d/m/Y', '05/11/2024');
$next = $start->modify('+1 day');
echo json_encode([$start->format('Y-m-d H:i:s'), $next->format('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["2024-11-05 00:00:00","2024-11-06 00:00:00"]

Example 3: detecting failure

The first call uses a format that does not match the input, so it returns false. The second uses the right format and returns an object.

Example
<?php
date_default_timezone_set('UTC');
$bad = date_create_immutable_from_format('Y-m-d', '15/03/2024');
var_dump($bad);
$good = date_create_immutable_from_format('!d/m/Y', '15/03/2024');
var_dump($good instanceof DateTimeImmutable);
Output
bool(false)
bool(true)

Edge cases

  • Overflow is accepted. A string like 2024-02-31 matches Y-m-d syntactically, so it does not return false. It rolls forward to 2024-03-02. The problem is only reported as a warning through DateTimeImmutable::getLastErrors(). If you need strict validation, compare the formatted result with the original input.
  • Trailing data fails. Extra characters after the parsed portion cause false. Ending the format with + tells PHP to ignore trailing data, and ending it with | resets all unparsed fields to epoch values.
  • Timezone in the string wins. If the input contains an offset or zone identifier and the format has a matching character such as P, e or T, the $timezone argument has no effect.
  • Unspecified fields use the current time. Without ! or |, omitted fields come from the system clock, which makes results vary between runs.

Common mistakes

  1. Calling ->format() on the result without checking for false. This causes an error when the input is bad.
  2. Forgetting ! or | when parsing a date-only string and then being surprised by a non-zero time of day.
  3. Mixing up d and j, or m and n. The padded forms expect leading zeros; the unpadded forms are more lenient.
  4. Expecting modify() or setTimezone() to change the original object. They return a new instance, so assign the result.
  5. Assuming a successful return means a real calendar date, as shown by the overflow case above.
  • DateTimeImmutable::createFromFormat(), the method this function aliases
  • date_create_from_format(), the mutable DateTime counterpart
  • date_create_immutable(), which parses free-form strings
  • date_default_timezone_set(), which controls the default zone
  • DateTimeImmutable::getLastErrors(), for warnings and errors

Recap

Use date_create_immutable_from_format() when you know the exact input layout. Provide a format, optionally a timezone, and always test for false. Start the format with ! or end it with | to get predictable zeroed time fields, and remember that overflow dates such as February 31 are silently adjusted rather than rejected.

Tested examples

Parse with an explicit timezone and convert to UTC
<?php
date_default_timezone_set('UTC');
$tz = new DateTimeZone('Asia/Tokyo');
$local = date_create_immutable_from_format('Y-m-d H:i:s', '2024-03-15 09:00:00', $tz);
$utc = $local->setTimezone(new DateTimeZone('UTC'));
echo $local->format('c'), "\n";
echo $utc->format('c'), "\n";
Output
2024-03-15T09:00:00+09:00
2024-03-15T00:00:00+00:00
Use ! to zero the time and show immutability
<?php
date_default_timezone_set('UTC');
$start = date_create_immutable_from_format('!d/m/Y', '05/11/2024');
$next = $start->modify('+1 day');
echo json_encode([$start->format('Y-m-d H:i:s'), $next->format('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["2024-11-05 00:00:00","2024-11-06 00:00:00"]
Detect a failed parse
<?php
date_default_timezone_set('UTC');
$bad = date_create_immutable_from_format('Y-m-d', '15/03/2024');
var_dump($bad);
$good = date_create_immutable_from_format('!d/m/Y', '15/03/2024');
var_dump($good instanceof DateTimeImmutable);
Output
bool(false)
bool(true)