Signature
date_create_immutable_from_format(
string $format,
string $datetime,
?DateTimeZone $timezone = null
): DateTimeImmutable|falsePurpose
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 areY(four-digit year),mandd(two-digit month and day),j(day without leading zero),H,iands(time parts) andM(short month name such asMar). A backslash escapes a literal character.$datetime: the string to parse. It must match$formatcompletely.$timezone: an optionalDateTimeZone. It defaults tonull, which means the current default timezone (seedate_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.
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";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.
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";["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.
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);bool(false) bool(true)
Edge cases
- Overflow is accepted. A string like
2024-02-31matchesY-m-dsyntactically, so it does not returnfalse. It rolls forward to2024-03-02. The problem is only reported as a warning throughDateTimeImmutable::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,eorT, the$timezoneargument 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
- Calling
->format()on the result without checking forfalse. This causes an error when the input is bad. - Forgetting
!or|when parsing a date-only string and then being surprised by a non-zero time of day. - Mixing up
dandj, ormandn. The padded forms expect leading zeros; the unpadded forms are more lenient. - Expecting
modify()orsetTimezone()to change the original object. They return a new instance, so assign the result. - Assuming a successful return means a real calendar date, as shown by the overflow case above.
Related functions
DateTimeImmutable::createFromFormat(), the method this function aliasesdate_create_from_format(), the mutableDateTimecounterpartdate_create_immutable(), which parses free-form stringsdate_default_timezone_set(), which controls the default zoneDateTimeImmutable::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
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";
2024-03-15T09:00:00+09:00 2024-03-15T00:00:00+00:00
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";
["2024-11-05 00:00:00","2024-11-06 00:00:00"]
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);
bool(false) bool(true)