phacility/phabricator · error · Exception
Expected ISO8601 datetime in the format "19990105T112233Z",
Error message
Expected ISO8601 datetime in the format "19990105T112233Z", found "%s".
What it means
PhutilCalendarAbsoluteDateTime::newFromISO8601() accepts only the basic ISO 8601 form YYYYMMDD, optionally followed by THHMMSS and an optional trailing Z. Any deviation - hyphens, colons, timezone offsets like +0100, missing digits, or an empty string - fails the regex and throws.
Source
Thrown at src/applications/calendar/parser/data/PhutilCalendarAbsoluteDateTime.php:26
private $day;
private $hour = 0;
private $minute = 0;
private $second = 0;
private $timezone;
public static function newFromISO8601($value, $timezone = 'UTC') {
$pattern =
'/^'.
'(?P<y>\d{4})(?P<m>\d{2})(?P<d>\d{2})'.
'(?:'.
'T(?P<h>\d{2})(?P<i>\d{2})(?P<s>\d{2})(?<z>Z)?'.
')?'.
'\z/';
$matches = null;
$ok = preg_match($pattern, $value, $matches);
if (!$ok) {
throw new Exception(
pht(
'Expected ISO8601 datetime in the format "19990105T112233Z", '.
'found "%s".',
$value));
}
if (isset($matches['z'])) {
if ($timezone != 'UTC') {
throw new Exception(
pht(
'ISO8601 date ends in "Z" indicating UTC, but a timezone other '.
'than UTC ("%s") was specified.',
$timezone));
}
}
$datetime = id(new self())
->setYear((int)$matches['y'])View on GitHub (pinned to 5720a38cfe)
Solutions
- Normalize to basic format before parsing: strip everything except digits and a trailing Z (e.g. preg_replace('/[^0-9TZ]/', '', $value)).
- Generate correct values directly: gmdate('Ymd\\THis\\Z', $timestamp).
- If the input carries an offset, convert it to a UTC timestamp first (new DateTime($input)->getTimestamp()) and format as basic UTC.
- Trim whitespace: $value = trim($value) before calling.
Example fix
// before
$dt = PhutilCalendarAbsoluteDateTime::newFromISO8601('2024-01-05T11:22:33Z');
// after
$dt = PhutilCalendarAbsoluteDateTime::newFromISO8601('20240105T112233Z'); Defensive patterns
Strategy: type-guard
Validate before calling
$value = trim($value);
if (!preg_match('/^\d{4}\d{2}\d{2}(T\d{2}\d{2}\d{2}Z?)?$/', $value)) {
// normalize extended format to basic before calling the parser
$dt = new DateTime($value);
$value = $dt->format('Ymd\\THis');
}
$abs = PhutilCalendarAbsoluteDateTime::newFromISO8601($value); Type guard
function isBasicIso8601DateTime($value) {
return (bool) preg_match('/^\d{4}\d{2}\d{2}(T\d{2}\d{2}\d{2}Z?)?$/', trim((string)$value));
} Try / catch
try {
$dt = PhutilCalendarAbsoluteDateTime::newFromISO8601($value, $tz);
} catch (Exception $ex) {
// skip/log the bad property; keep parsing the rest of the ICS object
phlog('Bad ISO8601 datetime: '.$value);
continue;
} Prevention
- Generate values with gmdate('Ymd\\THis\\Z', $ts) instead of date('c').
- Strip separators when importing: preg_replace('/[^0-9TZ]/', '', $value).
- Trim whitespace on all externally sourced datetime strings.
When it happens
Trigger: newFromISO8601('2024-01-05'), newFromISO8601('20240105T11:22:33'), newFromISO8601('20240105T112233+0100'), newFromISO8601(''). Extended-format values from PHP's DateTime::format('c') or from ICS files with relaxed producers.
Common situations: Passing output of date('c') or DATE_ATOM; user-typed dates; third-party ICS/calendar feeds emitting extended ISO format; whitespace padding around the value.
Related errors
- Expected ISO8601 duration in the format "P12DT3H4M5S", found
- ISO8601 date ends in "Z" indicating UTC, but a timezone othe
- RRULE dictionary includes unknown key "%s". Expected keys ar
- Unexpected value "%s" in "%s" RULE property: expected an int
- Unexpected value "%s" in "%s" RRULE property: expected only
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/2516107701e372e8.
Report an issue: GitHub.