DutchStringToDateTimeTransformer   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 198
Duplicated Lines 3.03 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 92.21%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 1
dl 6
loc 198
ccs 71
cts 77
cp 0.9221
rs 9.36
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 3
F transform() 6 109 27
B createDate() 0 36 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace TreeHouse\IoBundle\Item\Modifier\Data\Transformer;
4
5
use TreeHouse\Feeder\Modifier\Data\Transformer\StringToDateTimeTransformer as FallbackTransformer;
6
use TreeHouse\Feeder\Modifier\Data\Transformer\TransformerInterface;
7
8
class DutchStringToDateTimeTransformer implements TransformerInterface
9
{
10
    protected $monthNames = [
11
        1 => 'januari',
12
        2 => 'februari',
13
        3 => 'maart',
14
        4 => 'april',
15
        5 => 'mei',
16
        6 => 'juni',
17
        7 => 'juli',
18
        8 => 'augustus',
19
        9 => 'september',
20
        10 => 'oktober',
21
        11 => 'november',
22
        12 => 'december',
23
    ];
24
25
    /**
26
     * @var \DateTimeZone
27
     */
28
    protected $timezone;
29
30
    /**
31
     * @var string
32
     */
33
    protected $monthRegex;
34
35
    /**
36
     * @param array         $monthNames
37
     * @param \DateTimeZone $timezone
38
     */
39 92
    public function __construct(array $monthNames = [], \DateTimeZone $timezone = null)
40
    {
41 92
        if (!empty($monthNames)) {
42
            $this->monthNames = $monthNames;
43
        }
44
45 92
        $this->monthRegex = implode('|', array_values($this->monthNames));
46 92
        $this->timezone = $timezone ?: new \DateTimeZone('UTC');
47 92
    }
48
49
    /**
50
     * @inheritdoc
51
     */
52 92
    public function transform($value)
53
    {
54 92
        if (is_null($value) || empty($value)) {
55 4
            return null;
56
        }
57
58 88
        if ($value instanceof \DateTime) {
59 2
            return $value;
60
        }
61
62
        // "per direct" => now
63 86 View Code Duplication
        if (preg_match('/(per )?dire(c|k)t/i', $value) || preg_match('/(gelijk|heden)/i', $value)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
64 12
            return new \DateTime('now', $this->timezone);
65
        }
66
67
        // [sinds] 2 [mnd|maand|maanden]
68 74 View Code Duplication
        if (preg_match('/^(sinds\s)?(?P<months>\d+)\s(maand|maanden|mnd)\s?\+?/i', $value, $matches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69 12
            return new \DateTime(sprintf('-%s months', $matches['months']), $this->timezone);
70
        }
71
72
        // [12] oktober 2012|'12
73 62
        $regex = '/^(?P<day>\d*\s?)?(?P<month>' . $this->monthRegex . ')\s+(?P<year>\d{4}|\\\'\d{2})/i';
74 62
        if (preg_match($regex, $value, $matches)) {
75 10
            $year = $matches['year'];
76 10
            $month = array_search(mb_strtolower($matches['month']), $this->monthNames);
77 10
            $day = (isset($matches['day']) && ($matches['day'] !== '')) ? $matches['day'] : 1;
78
79 10
            return $this->createDate($year, $month, $day);
80
        }
81
82
        // 12 oktober [2012|'12]
83 52
        $regex = '/^(?P<day>\d{1,2})\s?(?P<month>' . $this->monthRegex . ')(\s+(?P<year>\d{4}|\\\'\d{2}))?/i';
84 52
        if (preg_match($regex, $value, $matches)) {
85 2
            $year = isset($matches['year']) ? $matches['year'] : date('Y');
86 2
            $month = array_search(mb_strtolower($matches['month']), $this->monthNames);
87 2
            $day = $matches['day'];
88
89 2
            return $this->createDate($year, $month, $day);
90
        }
91
92
        // oktober [12|2012]
93 50
        $regex = '/^(?P<month>' . $this->monthRegex . ')(\s+(?P<day_or_year>\d{4}|\d{1,2}))?/i';
94 50
        if (preg_match($regex, $value, $matches)) {
95 4
            $month = array_search(mb_strtolower($matches['month']), $this->monthNames);
96 4
            $year = date('Y');
97 4
            $day = 1;
98
99 4
            if (isset($matches['day_or_year'])) {
100 2
                if (strlen($matches['day_or_year']) === 2) {
101 2
                    $day = $matches['day_or_year'];
102
                } elseif (strlen($matches['day_or_year']) === 4) {
103
                    $year = $matches['day_or_year'];
104
                }
105
            }
106
107 4
            return $this->createDate($year, $month, $day);
108
        }
109
110
        // [begin|eind] mei [2013]
111 46
        $regex = '/^(?P<day>begin|eind)?\s?(?P<month>' . $this->monthRegex . ')(?P<year>\s+\d{4})?/i';
112 46
        if (preg_match($regex, $value, $matches)) {
113 4
            $year = isset($matches['year']) ? $matches['year'] : date('Y');
114 4
            $month = array_search(mb_strtolower($matches['month']), $this->monthNames);
115 4
            if (isset($matches['day'])) {
116 4
                $day = $matches['day'] === 'begin' ? 5 : 25;
117
            } else {
118
                $day = 1;
119
            }
120
121 4
            return $this->createDate($year, $month, $day);
122
        }
123
124
        $regexes = [
125
            // 12-10-[2012|'12]
126 42
            '/^(?P<day>\d{1,2})[\-\/](?P<month>\d{1,2})[\-\/](?P<year>\d{4}|\\\'?\d{2})/i' => [null, null, null],
127
            // 2012-10-26
128
            '/^(?P<year>\d{4})[\-\/](?P<month>\d{1,2})[\-\/](?P<day>\d{1,2})/i' => [null, null, null],
129
            // 2012-10 (defaults the day to 1)
130
            '/^(?P<year>\d{4})[\-\/](?P<month>\d{1,2})/i' => [null, null, 1],
131
            // 2012 (defaults to jan. 1st)
132
            '/^(?P<year>\d{4})/i' => [null, 1, 1],
133
        ];
134
135 42
        foreach ($regexes as $regex => $defaults) {
136 42
            if (preg_match($regex, $value, $matches)) {
137 36
                list($defaultYear, $defaultMonth, $defaultDay) = $defaults;
138 36
                $year = isset($matches['year'])  ? $matches['year']  : $defaultYear;
139 36
                $month = isset($matches['month']) ? $matches['month'] : $defaultMonth;
140 36
                $day = isset($matches['day'])   ? $matches['day']   : $defaultDay;
141
142 42
                return $this->createDate($year, $month, $day);
143
            }
144
        }
145
146
        // check if strtotime matches
147 6
        if (false !== strtotime($value)) {
148 2
            return new \DateTime($value, $this->timezone);
149
        }
150
151
        // 26/03/2013
152 4
        if (preg_match('/^(?P<day>\d{1,2})\/(?P<month>\d{1,2})\/(?<year>\d{4})/i', $value, $matches)) {
153
            return $this->createDate($matches['year'], $matches['month'], $matches['day']);
154
        }
155
156
        // last resort
157 4
        $transformer = new FallbackTransformer('d-m-Y H:i:s', null, $this->timezone->getName());
158
159 4
        return $transformer->transform($value);
160
    }
161
162
    /**
163
     * @param string $year
164
     * @param string $month
165
     * @param string $day
166
     *
167
     * @return \DateTime
168
     */
169 56
    protected function createDate($year, $month, $day)
170
    {
171
        // year can have a quote prefix ('12)
172 56
        $year = str_replace("'", '', $year);
173
174
        // test for positive integer
175 56
        foreach (['year', 'month', 'day'] as $test) {
176 56
            $$test = intval($$test);
177 56
            if ($$test < 1) {
178 56
                return null;
179
            }
180
        }
181
182
        // year can be 2-digit notation ('12)
183 44
        if (strlen($year) === 2) {
184
            // use the 21st century until 2020, after that use the 20th century (1921-1999)
185 8
            $prefix = $year <= 20 ? 20 : 19;
186 8
            $year = (int) $prefix . $year;
187
        }
188
189
        // test if year seems valid
190 44
        if ((strlen($year) !== 4) || ($year > 2100)) {
191
            return null;
192
        }
193
194
        // check whether this is a valid date
195 44
        if (false === checkdate($month, $day, $year)) {
196 6
            return null;
197
        }
198
199 38
        return \DateTime::createFromFormat(
200 38
            'Y-n-j H:i:s',
201 38
            sprintf('%d-%d-%d 00:00:00', $year, $month, $day),
202 38
            $this->timezone
203
        );
204
    }
205
}
206