Passed
Pull Request — develop_3.0 (#466)
by Adrien
04:47
created

DateFormatHelper::toPHPDateFormat()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 54
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 54
rs 9.0306
c 1
b 0
f 0
ccs 20
cts 20
cp 1
cc 4
eloc 21
nc 4
nop 1
crap 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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