protected function doParsingInStrictMode($datetimeToParse, array $parsedFormat, array $localizedLiterals)
{
$datetimeElements = ['year' => null, 'month' => null, 'day' => null, 'hour' => null, 'minute' => null, 'second' => null, 'timezone' => null];
$using12HourClock = false;
$timeIsPm = false;
try {
foreach ($parsedFormat as $subformat) {
if (is_array($subformat)) {
// This is literal string, should match exactly
if (I18n\Utility::stringBeginsWith($datetimeToParse, $subformat[0])) {
$datetimeToParse = substr_replace($datetimeToParse, '', 0, strlen($subformat[0]));
continue;
} else {
return false;
}
}
$lengthOfSubformat = strlen($subformat);
$numberOfCharactersToRemove = 0;
switch ($subformat[0]) {
case 'h':
case 'K':
$hour = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 12);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $hour < 10 ? 1 : 2;
if ($subformat[0] === 'h' && $hour === 12) {
$hour = 0;
}
$datetimeElements['hour'] = $hour;
$using12HourClock = true;
break;
case 'k':
case 'H':
$hour = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 24);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $hour < 10 ? 1 : 2;
if ($subformat[0] === 'k' && $hour === 24) {
$hour = 0;
}
$datetimeElements['hour'] = $hour;
break;
case 'a':
$dayPeriods = $localizedLiterals['dayPeriods']['format']['wide'];
if (I18n\Utility::stringBeginsWith($datetimeToParse, $dayPeriods['am'])) {
$numberOfCharactersToRemove = strlen($dayPeriods['am']);
} elseif (I18n\Utility::stringBeginsWith($datetimeToParse, $dayPeriods['pm'])) {
$timeIsPm = true;
$numberOfCharactersToRemove = strlen($dayPeriods['pm']);
} else {
return false;
}
break;
case 'm':
$minute = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 0, 59);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $minute < 10 ? 1 : 2;
$datetimeElements['minute'] = $minute;
break;
case 's':
$second = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 0, 59);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $second < 10 ? 1 : 2;
$datetimeElements['second'] = $second;
break;
case 'd':
$dayOfTheMonth = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 31);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $dayOfTheMonth < 10 ? 1 : 2;
$datetimeElements['day'] = $dayOfTheMonth;
break;
case 'M':
case 'L':
$typeOfLiteral = $subformat[0] === 'L' ? 'stand-alone' : 'format';
if ($lengthOfSubformat <= 2) {
$month = $this->extractAndCheckNumber($datetimeToParse, $lengthOfSubformat === 2, 1, 12);
$numberOfCharactersToRemove = $lengthOfSubformat === 1 && $month < 10 ? 1 : 2;
} elseif ($lengthOfSubformat <= 4) {
$lengthOfLiteral = $lengthOfSubformat === 3 ? 'abbreviated' : 'wide';
$month = 0;
foreach ($localizedLiterals['months'][$typeOfLiteral][$lengthOfLiteral] as $monthId => $monthName) {
if (I18n\Utility::stringBeginsWith($datetimeToParse, $monthName)) {
$month = $monthId;
break;
}
}
} else {
throw new InvalidArgumentException('Cannot parse formats with narrow month pattern as it is not unique.', 1279965245);
}
if ($month === 0) {
return false;
}
$datetimeElements['month'] = $month;
break;
case 'y':
if ($lengthOfSubformat === 2) {
/** @todo How should the XX date be returned? Like 19XX? **/
$year = substr($datetimeToParse, 0, 2);
$numberOfCharactersToRemove = 2;
} else {
$year = substr($datetimeToParse, 0, $lengthOfSubformat);
$datetimeToParseLength = strlen($datetimeToParse);
for ($i = $lengthOfSubformat; $i < $datetimeToParseLength; ++$i) {
if (is_numeric($datetimeToParse[$i])) {
$year .= $datetimeToParse[$i];
} else {
break;
}
}
$numberOfCharactersToRemove = $i;
}
if (!is_numeric($year)) {
return false;
}
$year = (int) $year;
$datetimeElements['year'] = $year;
break;
case 'v':
case 'z':
if ($lengthOfSubformat <= 3) {
$pattern = self::PATTERN_MATCH_STRICT_TIMEZONE_ABBREVIATION;
} else {
$pattern = self::PATTERN_MATCH_STRICT_TIMEZONE_TZ;
}
if (preg_match($pattern, $datetimeToParse, $matches) !== 1) {
return false;
}
$datetimeElements['timezone'] = $matches[0];
break;
case 'D':
case 'F':
case 'w':
case 'W':
case 'Q':
case 'q':
case 'G':
case 'S':
case 'E':
case 'Y':
case 'u':
case 'l':
case 'g':
case 'e':
case 'c':
case 'A':
case 'Z':
case 'V':
// Silently ignore unsupported formats or formats that there is no need to parse
break;
default:
throw new InvalidArgumentException('Unexpected format symbol, "' . $subformat[0] . '" detected for date / time parsing.', 1279965528);
}
if ($using12HourClock && $timeIsPm) {
$datetimeElements['hour'] += 12;
$timeIsPm = false;
}
if ($numberOfCharactersToRemove > 0) {
$datetimeToParse = substr_replace($datetimeToParse, '', 0, $numberOfCharactersToRemove);
}
}
} catch (Exception\InvalidParseStringException $exception) {
// Method extractAndCheckNumber() throws exception when constraints in $datetimeToParse are not fulfilled
return false;
}
return $datetimeElements;
}