Autoría | Ultima modificación | Ver Log |
<?phpdeclare(strict_types=1);namespace OpenSpout\Reader\XLSX\Helper;/*** @internal*/final class DateFormatHelper{public const KEY_GENERAL = 'general';public const KEY_HOUR_12 = '12h';public const KEY_HOUR_24 = '24h';/*** This map is used to replace Excel format characters by their PHP equivalent.* Keys should be ordered from longest to smallest.* Mapping between Excel format characters and PHP format characters.*/private const excelDateFormatToPHPDateFormatMapping = [self::KEY_GENERAL => [// Time'am/pm' => 'A', // Uppercase Ante meridiem and Post meridiem':mm' => ':i', // Minutes with leading zeros - if preceded by a ":" (otherwise month)'mm:' => 'i:', // Minutes with leading zeros - if followed by a ":" (otherwise month)'ss' => 's', // Seconds, with leading zeros'.s' => '', // Ignore (fractional seconds format does not exist in PHP)// Date'e' => 'Y', // Full numeric representation of a year, 4 digits'yyyy' => 'Y', // Full numeric representation of a year, 4 digits'yy' => 'y', // Two digit representation of a year'mmmmm' => 'M', // Short textual representation of a month, three letters ("mmmmm" should only contain the 1st letter...)'mmmm' => 'F', // Full textual representation of a month'mmm' => 'M', // Short textual representation of a month, three letters'mm' => 'm', // Numeric representation of a month, with leading zeros'm' => 'n', // Numeric representation of a month, without leading zeros'dddd' => 'l', // Full textual representation of the day of the week'ddd' => 'D', // Textual representation of a day, three letters'dd' => 'd', // Day of the month, 2 digits with leading zeros'd' => 'j', // Day of the month without leading zeros],self::KEY_HOUR_12 => ['hh' => 'h', // 12-hour format of an hour without leading zeros'h' => 'g', // 12-hour format of an hour without leading zeros],self::KEY_HOUR_24 => ['hh' => 'H', // 24-hour hours with leading zero'h' => 'G', // 24-hour format of an hour without leading zeros],];/*** Converts the given Excel date format to a format understandable by the PHP date function.** @param string $excelDateFormat Excel date format** @return string PHP date format (as defined here: http://php.net/manual/en/function.date.php)*/public static function toPHPDateFormat(string $excelDateFormat): string{// Remove brackets potentially present at the beginning of the format string// and text portion of the format at the end of it (starting with ";")// See §18.8.31 of ECMA-376 for more detail.$dateFormat = preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat);\assert(null !== $dateFormat);// Double quotes are used to escape characters that must not be interpreted.// For instance, ["Day " dd] should result in "Day 13" and we should not try to interpret "D", "a", "y"// By exploding the format string using double quote as a delimiter, we can get all parts// that must be transformed (even indexes) and all parts that must not be (odd indexes).$dateFormatParts = explode('"', $dateFormat);foreach ($dateFormatParts as $partIndex => $dateFormatPart) {// do not look at odd indexesif (1 === $partIndex % 2) {continue;}// Make sure all characters are lowercase, as the mapping table is using lowercase characters$transformedPart = strtolower($dateFormatPart);// Remove escapes related to non-format characters$transformedPart = str_replace('\\', '', $transformedPart);// Apply general transformation first...$transformedPart = strtr($transformedPart, self::excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);// ... then apply hour transformation, for 12-hour or 24-hour formatif (self::has12HourFormatMarker($dateFormatPart)) {$transformedPart = strtr($transformedPart, self::excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);} else {$transformedPart = strtr($transformedPart, self::excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);}// overwrite the parts array with the new transformed part$dateFormatParts[$partIndex] = $transformedPart;}// Merge all transformed parts back together$phpDateFormat = implode('"', $dateFormatParts);// Finally, to have the date format compatible with the DateTime::format() function, we need to escape// all characters that are inside double quotes (and double quotes must be removed).// For instance, ["Day " dd] should become [\D\a\y\ dd]return preg_replace_callback('/"(.+?)"/', static function ($matches): string {$stringToEscape = $matches[1];$letters = preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);\assert(false !== $letters);return '\\'.implode('\\', $letters);}, $phpDateFormat);}/*** @param string $excelDateFormat Date format as defined by Excel** @return bool Whether the given date format has the 12-hour format marker*/private static function has12HourFormatMarker(string $excelDateFormat): bool{return false !== stripos($excelDateFormat, 'am/pm');}}