protected function doParsingInLenientMode($datetimeToParse, array $parsedFormat, array $localizedLiterals)
{
$datetimeElements = ['year' => null, 'month' => null, 'day' => null, 'hour' => null, 'minute' => null, 'second' => null, 'timezone' => null];
$using12HourClock = false;
$timeIsPm = false;
foreach ($parsedFormat as $subformat) {
try {
if (is_array($subformat)) {
// This is literal string, and we ignore them
continue;
}
$lengthOfSubformat = strlen($subformat);
$numberOfCharactersToRemove = 0;
$position = 0;
switch ($subformat[0]) {
case 'K':
$hour = $this->extractNumberAndGetPosition($datetimeToParse, $position);
if ($hour >= 0 && $hour <= 11) {
$numberOfCharactersToRemove = $position + strlen($hour);
$datetimeElements['hour'] = (int) $hour;
$using12HourClock = true;
break;
}
case 'h':
if (!isset($hour)) {
$hour = $this->extractNumberAndGetPosition($datetimeToParse, $position);
}
if ($hour >= 1 && $hour <= 12) {
$numberOfCharactersToRemove = $position + strlen($hour);
if ((int) $hour === 12) {
$hour = 0;
}
$datetimeElements['hour'] = (int) $hour;
$using12HourClock = true;
break;
}
case 'H':
if (!isset($hour)) {
$hour = $this->extractNumberAndGetPosition($datetimeToParse, $position);
}
if ($hour >= 0 && $hour <= 23) {
$numberOfCharactersToRemove = $position + strlen($hour);
$datetimeElements['hour'] = (int) $hour;
break;
}
case 'k':
if (!isset($hour)) {
$hour = $this->extractNumberAndGetPosition($datetimeToParse, $position);
}
if ($hour >= 1 && $hour <= 24) {
$numberOfCharactersToRemove = $position + strlen($hour);
if ((int) $hour === 24) {
$hour = 0;
}
$datetimeElements['hour'] = (int) $hour;
break;
} else {
throw new Exception\InvalidParseStringException('Unable to match number string to any hour format.', 1280488645);
}
case 'a':
$dayPeriods = $localizedLiterals['dayPeriods']['format']['wide'];
$positionOfDayPeriod = strpos($datetimeToParse, $dayPeriods['am']);
if ($positionOfDayPeriod !== false) {
$numberOfCharactersToRemove = $positionOfDayPeriod + strlen($dayPeriods['am']);
} else {
$positionOfDayPeriod = strpos($datetimeToParse, $dayPeriods['pm']);
if ($positionOfDayPeriod !== false) {
$numberOfCharactersToRemove = $positionOfDayPeriod + strlen($dayPeriods['pm']);
$timeIsPm = true;
} else {
throw new Exception\InvalidParseStringException('Unable to match any day period.', 1280489183);
}
}
break;
case 'm':
$minute = $this->extractNumberAndGetPosition($datetimeToParse, $position);
if ($minute < 0 && $minute > 59) {
throw new Exception\InvalidParseStringException('Expected minute is out of range.', 1280489411);
}
$numberOfCharactersToRemove = $position + strlen($minute);
$datetimeElements['minute'] = (int) $minute;
break;
case 's':
$second = $this->extractNumberAndGetPosition($datetimeToParse, $position);
if ($second < 0 && $second > 59) {
throw new Exception\InvalidParseStringException('Expected second is out of range.', 1280489412);
}
$numberOfCharactersToRemove = $position + strlen($second);
$datetimeElements['second'] = (int) $second;
break;
case 'd':
$day = $this->extractNumberAndGetPosition($datetimeToParse, $position);
if ($day < 1 && $day > 31) {
throw new Exception\InvalidParseStringException('Expected day is out of range.', 1280489413);
}
$numberOfCharactersToRemove = $position + strlen($day);
$datetimeElements['day'] = (int) $day;
break;
case 'M':
case 'L':
$typeOfLiteral = $subformat[0] === 'L' ? 'stand-alone' : 'format';
switch ($lengthOfSubformat) {
case 1:
case 2:
try {
$month = $this->extractNumberAndGetPosition($datetimeToParse, $position);
if ($month >= 1 && $month <= 31) {
$numberOfCharactersToRemove = $position + strlen($month);
$datetimeElements['month'] = (int) $month;
break;
}
} catch (Exception\InvalidParseStringException $exception) {
// Try to match month's name by cases below
}
case 3:
foreach ($localizedLiterals['months'][$typeOfLiteral]['abbreviated'] as $monthId => $monthName) {
$positionOfMonthName = strpos($datetimeToParse, $monthName);
if ($positionOfMonthName !== false) {
$numberOfCharactersToRemove = $positionOfMonthName + strlen($monthName);
$datetimeElements['month'] = (int) $monthId;
break;
}
}
if ($datetimeElements['month'] !== null) {
break;
}
case 4:
foreach ($localizedLiterals['months'][$typeOfLiteral]['wide'] as $monthId => $monthName) {
$positionOfMonthName = strpos($datetimeToParse, $monthName);
if ($positionOfMonthName !== false) {
$numberOfCharactersToRemove = $positionOfMonthName + strlen($monthName);
$datetimeElements['month'] = (int) $monthId;
break;
}
}
if ($datetimeElements['month'] === null) {
throw new Exception\InvalidParseStringException('Neither month name or number were matched.', 1280497950);
}
default:
throw new InvalidArgumentException('Cannot parse formats with narrow month pattern as it is not unique.', 1280495827);
}
break;
case 'y':
$year = $this->extractNumberAndGetPosition($datetimeToParse, $position);
$numberOfCharactersToRemove = $position + strlen($year);
/** @todo Two digits date (like 99) shoud be handled here somehow **/
$datetimeElements['year'] = (int) $year;
break;
case 'v':
case 'z':
if ($lengthOfSubformat <= 3) {
$firstPattern = self::PATTERN_MATCH_LENIENT_TIMEZONE_ABBREVIATION;
$secondPattern = self::PATTERN_MATCH_LENIENT_TIMEZONE_TZ;
} else {
$firstPattern = self::PATTERN_MATCH_LENIENT_TIMEZONE_TZ;
$secondPattern = self::PATTERN_MATCH_LENIENT_TIMEZONE_ABBREVIATION;
}
if (preg_match($firstPattern, $datetimeToParse, $matches) === 0) {
if (preg_match($secondPattern, $datetimeToParse, $matches) === 0) {
throw new Exception\InvalidParseStringException('Expected timezone identifier was not found.', 1280492312);
}
}
$timezone = $matches[0];
$numberOfCharactersToRemove = strpos($datetimeToParse, $timezone) + strlen($timezone);
$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.', 1279965529);
}
if ($using12HourClock && $timeIsPm) {
$datetimeElements['hour'] += 12;
$timeIsPm = false;
}
if ($numberOfCharactersToRemove > 0) {
$datetimeToParse = substr_replace($datetimeToParse, '', 0, $numberOfCharactersToRemove);
}
} catch (Exception\InvalidParseStringException $exception) {
// Matching failed, but in lenient mode we ignore it and try to match next element
continue;
}
}
return $datetimeElements;
}