Completed
Push — master ( e3f840...58d424 )
by Sebastian
06:21
created

Date::cleanDate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/*
3
 * citeproc-php
4
 *
5
 * @link        http://github.com/seboettg/citeproc-php for the source repository
6
 * @copyright   Copyright (c) 2016 Sebastian Böttger.
7
 * @license     https://opensource.org/licenses/MIT
8
 */
9
10
namespace Seboettg\CiteProc\Rendering\Date;
11
12
use Seboettg\CiteProc\CiteProc;
13
use Seboettg\CiteProc\Exception\CiteProcException;
14
use Seboettg\CiteProc\Styles\AffixesTrait;
15
use Seboettg\CiteProc\Styles\DisplayTrait;
16
use Seboettg\CiteProc\Styles\FormattingTrait;
17
use Seboettg\CiteProc\Styles\TextCaseTrait;
18
use Seboettg\CiteProc\Util;
19
use Seboettg\Collection\ArrayList;
20
21
22
/**
23
 * Class Date
24
 * @package Seboettg\CiteProc\Rendering
25
 *
26
 * @author Sebastian Böttger <[email protected]>
27
 */
28
class Date
29
{
30
31
    use AffixesTrait,
32
        DisplayTrait,
33
        FormattingTrait,
34
        TextCaseTrait;
35
36
    // ymd
37
    const DATE_RANGE_STATE_NONE         = 0; // 000
38
    const DATE_RANGE_STATE_DAY          = 1; // 001
39
    const DATE_RANGE_STATE_MONTH        = 2; // 010
40
    const DATE_RANGE_STATE_MONTHDAY     = 3; // 011
41
    const DATE_RANGE_STATE_YEAR         = 4; // 100
42
    const DATE_RANGE_STATE_YEARDAY      = 5; // 101
43
    const DATE_RANGE_STATE_YEARMONTH    = 6; // 110
44
    const DATE_RANGE_STATE_YEARMONTHDAY = 7; // 111
45
46
    private static $localizedDateFormats = [
47
        'numeric',
48
        'text'
49
    ];
50
51
    /**
52
     * @var ArrayList
53
     */
54
    private $dateParts;
55
56
    /**
57
     * @var string
58
     */
59
    private $form = "";
60
61
    /**
62
     * @var string
63
     */
64
    private $variable = "";
65
66
    /**
67
     * @var string
68
     */
69
    private $datePartsAttribute = "";
70
71
    public function __construct(\SimpleXMLElement $node)
72
    {
73
        $this->dateParts = new ArrayList();
74
75
        /** @var \SimpleXMLElement $attribute */
76 View Code Duplication
        foreach ($node->attributes() as $attribute) {
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...
77
            switch ($attribute->getName()) {
78
                case 'form':
79
                    $this->form = (string) $attribute;
80
                    break;
81
                case 'variable':
82
                    $this->variable = (string) $attribute;
83
                    break;
84
                case 'date-parts':
85
                    $this->datePartsAttribute = (string) $attribute;
86
            }
87
        }
88
        /** @var \SimpleXMLElement $child */
89
        foreach ($node->children() as $child) {
90
            if ($child->getName() === "date-part") {
91
                $datePartName = (string) $child->attributes()["name"];
92
                $this->dateParts->set($this->form . "-" . $datePartName, Util\Factory::create($child));
93
            }
94
        }
95
96
        $this->initAffixesAttributes($node);
97
        $this->initDisplayAttributes($node);
98
        $this->initFormattingAttributes($node);
99
        $this->initTextCaseAttributes($node);
100
    }
101
102
    /**
103
     * @param $data
104
     * @return string
105
     */
106
    public function render($data)
107
    {
108
        $ret = "";
109
        $var = null;
0 ignored issues
show
Unused Code introduced by
$var is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
110
        if (isset($data->{$this->variable})) {
111
            $var = $data->{$this->variable};
112
        } else {
113
            return "";
114
        }
115
116
        try {
117
            $this->prepareDatePartsInVariable($data, $var);
118
        } catch (CiteProcException $e) {
119
            if (!preg_match("/(\p{L}+)\s?([\-\-\&,])\s?(\p{L}+)/u", $data->{$this->variable}->raw)) {
120
                return $this->addAffixes($this->format($this->applyTextCase($data->{$this->variable}->raw)));
121
            }
122
        }
123
124
        $form = $this->form;
125
        $dateParts = !empty($this->datePartsAttribute) ? explode("-", $this->datePartsAttribute) : [];
126
        $this->prepareDatePartsChildren($dateParts, $form);
127
128
129
        // No date-parts in date-part attribute defined, take into account that the defined date-part children will be used.
130
        if (empty($this->datePartsAttribute) && $this->dateParts->count() > 0) {
131
            /** @var DatePart $part */
132
            foreach ($this->dateParts as $part) {
133
                $dateParts[] = $part->getName();
134
            }
135
        }
136
137
        /* cs:date may have one or more cs:date-part child elements (see Date-part). The attributes set on
138
        these elements override those specified for the localized date formats (e.g. to get abbreviated months for all
139
        locales, the form attribute on the month-cs:date-part element can be set to “short”). These cs:date-part
140
        elements do not affect which, or in what order, date parts are rendered. Affixes, which are very
141
        locale-specific, are not allowed on these cs:date-part elements. */
142
143
        if ($this->dateParts->count() > 0) {
144
145
            /* if (isset($var->raw) && !preg_match("/(\p{L}+)\s?([\-\-\&,])\s?(\p{L}+)/u", $var->raw) && $this->dateParts->count() > 0) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
146
                //$var->{"date-parts"} = [];
147
            } else*/
148
            if (!isset($var->{'date-parts'})) { // ignore empty date-parts
149
                return "";
150
            }
151
152
            if (count($data->{$this->variable}->{'date-parts'}) === 1) {
153
                $data_ = $this->createDateTime($data->{$this->variable}->{'date-parts'});
154
                /** @var DatePart $datePart */
155
                foreach ($this->dateParts as $key => $datePart) {
156
                    list($f, $p) = explode("-", $key);
0 ignored issues
show
Unused Code introduced by
The assignment to $f is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
157
                    if (in_array($p, $dateParts)) {
158
                        $ret .= $datePart->render($data_[0], $this);
159
                    }
160
                }
161
            } else if (count($var->{'date-parts'}) === 2) { //date range
162
                $data_ = $this->createDateTime($var->{'date-parts'});
163
                $from = $data_[0];
164
                $to = $data_[1];
165
                $interval = $to->diff($from);
166
                $delim = "";
167
                $toRender = 0;
168
                if ($interval->y > 0) {
169
                    $toRender |= self::DATE_RANGE_STATE_YEAR;
170
                    $delim = $this->dateParts->get($this->form . "-year")->getRangeDelimiter();
171
                }
172
                if ($interval->m > 0 && $from->getMonth() - $to->getMonth() !== 0) {
173
                    $toRender |= self::DATE_RANGE_STATE_MONTH;
174
                    $delim = $this->dateParts->get($this->form . "-month")->getRangeDelimiter();
175
                }
176
                if ($interval->d > 0 && $from->getDay() - $to->getDay() !== 0) {
177
                    $toRender |= self::DATE_RANGE_STATE_DAY;
178
                    $delim = $this->dateParts->get($this->form . "-day")->getRangeDelimiter();
179
                }
180
181
                $ret = $this->renderDateRange($toRender, $from, $to, $delim);
182
            }
183
184
            if (isset($var->raw) && preg_match("/(\p{L}+)\s?([\-\-\&,])\s?(\p{L}+)/u", $var->raw, $matches)) {
185
                return $matches[1] . $matches[2] . $matches[3];
186
            }
187
        }
188
        // fallback:
189
        // When there are no dateParts children, but date-parts attribute in date
190
        // render numeric
191
        else if (!empty($this->datePartsAttribute)) {
192
            $data = $this->createDateTime($var->{'date-parts'});
193
            $ret = $this->renderNumeric($data[0]);
194
        }
195
196
        return !empty($ret) ? $this->addAffixes($this->format($this->applyTextCase($ret))) : "";
197
    }
198
199
    private function createDateTime($dates)
200
    {
201
        $data = [];
202
        foreach ($dates as $date) {
203
            $date = $this->cleanDate($date);
204
            if ($date[0] < 1000) {
205
                $dateTime = new DateTime(0, 0, 0);
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a object<DateTimeZone>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
206
                $dateTime->setDay(0)->setMonth(0)->setYear(0);
207
                $data[] = $dateTime;
208
            }
209
            $dateTime = new DateTime($date[0], array_key_exists(1, $date) ? $date[1] : 1, array_key_exists(2, $date) ? $date[2] : 1);
210
            if (!array_key_exists(1, $date)) {
211
                $dateTime->setMonth(0);
212
            }
213
            if (!array_key_exists(2, $date)) {
214
                $dateTime->setDay(0);
215
            }
216
            $data[] = $dateTime;
217
        }
218
219
        return $data;
220
    }
221
222
    /**
223
     * @param integer $differentParts
224
     * @param DateTime $from
225
     * @param DateTime $to
226
     * @param $delim
227
     * @return string
228
     */
229
    private function renderDateRange($differentParts, DateTime $from, DateTime $to, $delim)
230
    {
231
        $ret = "";
232
        $dateParts_ = [];
233
        switch ($differentParts) {
234
            case self::DATE_RANGE_STATE_YEAR:
235
                foreach ($this->dateParts as $key => $datePart) {
236
                    if (strpos($key, "year") !== false) {
237
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
238
                    }
239
                    if (strpos($key, "month") !== false) {
240
                        $day = !empty($d = $from->getMonth()) ? $d : "";
241
                        $ret .= $day;
242
                    }
243 View Code Duplication
                    if (strpos($key, "day") !== false) {
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...
244
                        $day = !empty($d = $from->getDay()) ? $datePart->render($from, $this) : "";
245
                        $ret .= $day;
246
                    }
247
                }
248
                break;
249
            case self::DATE_RANGE_STATE_MONTH:
250
                /**
251
                 * @var string $key
252
                 * @var DatePart $datePart
253
                 */
254
                foreach ($this->dateParts as $key => $datePart) {
255
                    if (strpos($key, "year") !== false) {
256
                        $ret .= $datePart->render($from, $this);
257
                    }
258
                    if (strpos($key, "month")) {
259
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
260
                    }
261 View Code Duplication
                    if (strpos($key, "day") !== false) {
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...
262
                        $day = !empty($d = $from->getDay()) ? $datePart->render($from, $this) : "";
263
                        $ret .= $day;
264
                    }
265
                }
266
                break;
267
            case self::DATE_RANGE_STATE_DAY:
268
                /**
269
                 * @var string $key
270
                 * @var DatePart $datePart
271
                 */
272
                foreach ($this->dateParts as $key => $datePart) {
273
                    if (strpos($key, "year") !== false) {
274
                        $ret .= $datePart->render($from, $this);
275
                    }
276
                    if (strpos($key, "month") !== false) {
277
                        $ret .= $datePart->render($from, $this);
278
                    }
279
                    if (strpos($key, "day")) {
280
                        $ret .= $this->renderOneRangePart($datePart, $from, $to, $delim);
281
                    }
282
                }
283
                break;
284
            case self::DATE_RANGE_STATE_YEARMONTHDAY:
285
                $i = 0;
286 View Code Duplication
                foreach ($this->dateParts as $datePart) {
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...
287
                    if ($i === $this->dateParts->count() - 1) {
288
                        $ret .= $datePart->renderPrefix();
289
                        $ret .= $datePart->renderWithoutAffixes($from, $this);
290
                    } else {
291
                        $ret .= $datePart->render($from, $this);
292
                    }
293
                    ++$i;
294
                }
295
                $ret .= $delim;
296
                $i = 0;
297 View Code Duplication
                foreach ($this->dateParts as $datePart) {
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...
298
                    if ($i == 0) {
299
                        $ret .= $datePart->renderWithoutAffixes($to, $this);
300
                        $ret .= $datePart->renderSuffix();
301
                    } else {
302
                        $ret .= $datePart->render($to, $this);
303
                    }
304
                    ++$i;
305
                }
306
                break;
307 View Code Duplication
            case self::DATE_RANGE_STATE_YEARMONTH:
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...
308
                $dp = $this->dateParts->toArray();
309
                $i = 0;
310
                $dateParts_ = [];
311
                array_walk($dp, function($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
312
                    if (strpos($key, "year") !== false || strpos($key, "month") !== false) {
313
                        $dateParts_["yearmonth"][] = $datePart;
314
                    }
315
                    if (strpos($key, "day") !== false) {
316
                        $dateParts_["day"] = $datePart;
317
                    }
318
                });
319
                break;
320 View Code Duplication
            case self::DATE_RANGE_STATE_YEARDAY:
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...
321
                $dp = $this->dateParts->toArray();
322
                $i = 0;
323
                $dateParts_ = [];
324
                array_walk($dp, function($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
325
                    if (strpos($key, "year") !== false || strpos($key, "day") !== false) {
326
                        $dateParts_["yearday"][] = $datePart;
327
                    }
328
                    if (strpos($key, "month") !== false) {
329
                        $dateParts_["month"] = $datePart;
330
                    }
331
                });
332
                break;
333 View Code Duplication
            case self::DATE_RANGE_STATE_MONTHDAY:
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...
334
                $dp = $this->dateParts->toArray();
335
                $i = 0;
336
                $dateParts_ = [];
337
                array_walk($dp, function($datePart, $key) use (&$i, &$dateParts_, $differentParts) {
338
                    //$bit = sprintf("%03d", decbin($differentParts));
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
339
                    if (strpos($key, "month") !== false || strpos($key, "day") !== false) {
340
                        $dateParts_["monthday"][] = $datePart;
341
                    }
342
                    if (strpos($key, "year") !== false) {
343
                        $dateParts_["year"] = $datePart;
344
                    }
345
                });
346
                break;
347
        }
348
        switch ($differentParts) {
349
            case self::DATE_RANGE_STATE_YEARMONTH:
350
            case self::DATE_RANGE_STATE_YEARDAY:
351
            case self::DATE_RANGE_STATE_MONTHDAY:
352
                /**
353
                 * @var $key
354
                 * @var DatePart $datePart */
355
                foreach ($dateParts_ as $key => $datePart) {
356
                    if (is_array($datePart)) {
357
358
                        $f  = $datePart[0]->render($from, $this);
359
                        $f .= $datePart[1]->renderPrefix();
360
                        $f .= $datePart[1]->renderWithoutAffixes($from, $this);
361
                        $t  = $datePart[0]->renderWithoutAffixes($to, $this);
362
                        $t .= $datePart[0]->renderSuffix();
363
                        $t .= $datePart[1]->render($to, $this);
364
                        $ret .= $f . $delim . $t;
365
                    } else {
366
                        $ret .= $datePart->render($from, $this);
367
                    }
368
                }
369
                break;
370
        }
371
        return $ret;
372
    }
373
374
    /**
375
     * @param $datePart
376
     * @param DateTime $from
377
     * @param DateTime $to
378
     * @param $delim
379
     * @return string
380
     */
381
    protected function renderOneRangePart(DatePart $datePart, $from, $to, $delim)
382
    {
383
        $prefix = $datePart->renderPrefix();
384
        $from = $datePart->renderWithoutAffixes($from, $this);
385
        $to = $datePart->renderWithoutAffixes($to, $this);
386
        $suffix = !empty($to) ? $datePart->renderSuffix() : "";
387
        return $prefix . $from . $delim . $to . $suffix;
388
    }
389
390
    /**
391
     * @param string $format
392
     * @return bool
393
     */
394
    private function hasDatePartsFromLocales($format)
395
    {
396
        $dateXml = CiteProc::getContext()->getLocale()->getDateXml();
397
        return !empty($dateXml[$format]);
398
    }
399
400
    /**
401
     * @param string $format
402
     * @return array
403
     */
404
    private function getDatePartsFromLocales($format)
405
    {
406
        $ret = [];
407
        // date parts from locales
408
        $dateFromLocale_ = CiteProc::getContext()->getLocale()->getDateXml();
409
        $dateFromLocale = $dateFromLocale_[$format];
410
411
        // no custom date parts within the date element (this)?
412
        if (!empty($dateFromLocale)) {
413
414
            $dateForm = array_filter(is_array($dateFromLocale) ? $dateFromLocale : [$dateFromLocale], function($element) use ($format) {
415
                /** @var \SimpleXMLElement $element */
416
                $dateForm = (string) $element->attributes()["form"];
417
                return $dateForm === $format;
418
            });
419
420
            //has dateForm from locale children (date-part elements)?
421
            $localeDate = array_pop($dateForm);
422
423
            if ($localeDate instanceof \SimpleXMLElement && $localeDate->count() > 0) {
424
                foreach ($localeDate as $child) {
425
                    $ret[] = $child;
426
                }
427
            }
428
        }
429
        return $ret;
430
    }
431
432
    /**
433
     * @return string
434
     */
435
    public function getVariable()
436
    {
437
        return $this->variable;
438
    }
439
440
    /**
441
     * @param $data
442
     * @param $var
443
     * @throws CiteProcException
444
     */
445
    private function prepareDatePartsInVariable($data, $var)
446
    {
447
        if (!isset($data->{$this->variable}->{'date-parts'}) || empty($data->{$this->variable}->{'date-parts'})) {
448
            if (isset($data->{$this->variable}->raw) && !empty($data->{$this->variable}->raw)) {
449
                //try {
450
                // try to parse date parts from "raw" attribute
451
                $var->{'date-parts'} = Util\DateHelper::parseDateParts($data->{$this->variable});
452
                //} catch (CiteProcException $e) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
453
                //    if (!preg_match("/(\p{L}+)\s?([\-\-\&,])\s?(\p{L}+)/u", $data->{$this->variable}->raw)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
454
                //        return $this->addAffixes($this->format($this->applyTextCase($data->{$this->variable}->raw)));
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
455
                //    }
456
                //}
457
            } else {
458
                throw new CiteProcException("No valid date format");
459
                //return "";
460
            }
461
        }
462
    }
463
464
    /**
465
     * @param string $form
466
     */
467
    private function prepareDatePartsChildren($dateParts, $form)
468
    {
469
        /* Localized date formats are selected with the optional form attribute, which must set to either “numeric”
470
        (for fully numeric formats, e.g. “12-15-2005”), or “text” (for formats with a non-numeric month, e.g.
471
        “December 15, 2005”). Localized date formats can be customized in two ways. First, the date-parts attribute may
472
        be used to show fewer date parts. The possible values are:
473
            - “year-month-day” - (default), renders the year, month and day
474
            - “year-month” - renders the year and month
475
            - “year” - renders the year */
476
477
        if ($this->dateParts->count() < 1 && in_array($form, self::$localizedDateFormats)) {
478
            if ($this->hasDatePartsFromLocales($form)) {
479
                $datePartsFromLocales = $this->getDatePartsFromLocales($form);
480
                array_filter($datePartsFromLocales, function(\SimpleXMLElement $item) use ($dateParts) {
481
                    return in_array($item["name"], $dateParts);
482
                });
483
484
                foreach ($datePartsFromLocales as $datePartNode) {
485
                    $datePart = $datePartNode["name"];
486
                    $this->dateParts->set("$form-$datePart", Util\Factory::create($datePartNode));
487
                }
488
            } else { //otherwise create default date parts
489
                foreach ($dateParts as $datePart) {
490
                    $this->dateParts->add("$form-$datePart", new DatePart(new \SimpleXMLElement('<date-part name="' . $datePart . '" form="' . $form . '" />')));
491
                }
492
            }
493
        }
494
    }
495
496
497
    private function renderNumeric(DateTime $date)
498
    {
499
        return $date->renderNumeric();
500
    }
501
502
    public function getForm()
503
    {
504
        return $this->form;
505
    }
506
507
    private function cleanDate($date)
508
    {
509
        $ret = [];
510
        foreach ($date as $key => $datePart) {
511
            $ret[$key] = Util\NumberHelper::extractNumber(Util\StringHelper::removeBrackets($datePart));
512
        }
513
        return $ret;
514
    }
515
}