Completed
Pull Request — master (#226)
by Adrien
04:48 queued 02:20
created

DateFormatHelper   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 5
c 1
b 0
f 0
lcom 1
cbo 0
dl 0
loc 112
ccs 24
cts 24
cp 1
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
B toPHPDateFormat() 0 51 4
A has12HourFormatMarker() 0 4 1
1
<?php
2
3
namespace Box\Spout\Reader\XLSX\Helper;
4
5
/**
6
 * Class DateFormatHelper
7
 * This class provides helper functions to format Excel dates
8
 *
9
 * @package Box\Spout\Reader\XLSX\Helper
10
 */
11
class DateFormatHelper
12
{
13
    const KEY_GENERAL = 'general';
14
    const KEY_HOUR_12 = '12h';
15
    const KEY_HOUR_24 = '24h';
16
17
    /**
18
     * This map is used to replace Excel format characters by their PHP equivalent.
19
     * Keys should be ordered from longest to smallest.
20
     *
21
     * @var array Mapping between Excel format characters and PHP format characters
22
     */
23
    private static $excelDateFormatToPHPDateFormatMapping = [
24
        self::KEY_GENERAL => [
25
            // Time
26
            'am/pm' => 'A',  // Uppercase Ante meridiem and Post meridiem
27
            ':mm'   => ':i', // Minutes with leading zeros - if preceded by a ":" (otherwise month)
28
            'mm:'   => 'i:', // Minutes with leading zeros - if followed by a ":" (otherwise month)
29
            'ss'    => 's',  // Seconds, with leading zeros
30
            '.s'    => '',   // Ignore (fractional seconds format does not exist in PHP)
31
32
            // Date
33
            'e'     => 'Y',  // Full numeric representation of a year, 4 digits
34
            'yyyy'  => 'Y',  // Full numeric representation of a year, 4 digits
35
            'yy'    => 'y',  // Two digit representation of a year
36
            'mmmmm' => 'M',  // Short textual representation of a month, three letters ("mmmmm" should only contain the 1st letter...)
37
            'mmmm'  => 'F',  // Full textual representation of a month
38
            'mmm'   => 'M',  // Short textual representation of a month, three letters
39
            'mm'    => 'm',  // Numeric representation of a month, with leading zeros
40
            'm'     => 'n',  // Numeric representation of a month, without leading zeros
41
            'dddd'  => 'l',  // Full textual representation of the day of the week
42
            'ddd'   => 'D',  // Textual representation of a day, three letters
43
            'dd'    => 'd',  // Day of the month, 2 digits with leading zeros
44
            'd'     => 'j',  // Day of the month without leading zeros
45
        ],
46
        self::KEY_HOUR_12 => [
47
            'hh'    => 'h',  // 12-hour format of an hour without leading zeros
48
            'h'     => 'g',  // 12-hour format of an hour without leading zeros
49
        ],
50
        self::KEY_HOUR_24 => [
51
            'hh'    => 'H',  // 24-hour hours with leading zero
52
            'h'     => 'G',  // 24-hour format of an hour without leading zeros
53
        ],
54
    ];
55
56
    /**
57
     * Converts the given Excel date format to a format understandable by the PHP date function.
58
     *
59
     * @param string $excelDateFormat Excel date format
60
     * @return string PHP date format (as defined here: http://php.net/manual/en/function.date.php)
61
     */
62 42
    public static function toPHPDateFormat($excelDateFormat)
63
    {
64
        // Remove brackets potentially present at the beginning of the format string
65 42
        $dateFormat = preg_replace('/^(\[\$[^\]]+?\])/i', '', $excelDateFormat);
66
67
        // Double quotes are used to escape characters that must not be interpreted.
68
        // For instance, ["Day " dd] should result in "Day 13" and we should not try to interpret "D", "a", "y"
69
        // By exploding the format string using double quote as a delimiter, we can get all parts
70
        // that must be transformed (even indexes) and all parts that must not be (odd indexes).
71 42
        $dateFormatParts = explode('"', $dateFormat);
72
73 42
        foreach ($dateFormatParts as $partIndex => $dateFormatPart) {
74
            // do not look at odd indexes
75 42
            if ($partIndex % 2 === 1) {
76 9
                continue;
77
            }
78
79
            // Make sure all characters are lowercase, as the mapping table is using lowercase characters
80 42
            $transformedPart = strtolower($dateFormatPart);
81
82
            // Remove escapes related to non-format characters
83 42
            $transformedPart = str_replace('\\', '', $transformedPart);
84
85
            // Apply general transformation first...
86 42
            $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]);
87
88
            // ... then apply hour transformation, for 12-hour or 24-hour format
89 42
            if (self::has12HourFormatMarker($dateFormatPart)) {
90 15
                $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]);
91 15
            } else {
92 30
                $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]);
93
            }
94
95
            // overwrite the parts array with the new transformed part
96 42
            $dateFormatParts[$partIndex] = $transformedPart;
97 42
        }
98
99
        // Merge all transformed parts back together
100 42
        $phpDateFormat = implode('"', $dateFormatParts);
101
102
        // Finally, to have the date format compatible with the DateTime::format() function, we need to escape
103
        // all characters that are inside double quotes (and double quotes must be removed).
104
        // For instance, ["Day " dd] should become [\D\a\y\ dd]
105 42
        $phpDateFormat = preg_replace_callback('/"(.+?)"/', function($matches) {
106 9
            $stringToEscape = $matches[1];
107 9
            $letters = preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY);
108 9
            return '\\' . implode('\\', $letters);
109 42
        }, $phpDateFormat);
110
111 42
        return $phpDateFormat;
112
    }
113
114
    /**
115
     * @param string $excelDateFormat Date format as defined by Excel
116
     * @return bool Whether the given date format has the 12-hour format marker
117
     */
118 42
    private static function has12HourFormatMarker($excelDateFormat)
119
    {
120 42
        return (stripos($excelDateFormat, 'am/pm') !== false);
121
    }
122
}
123