Time::parseInputTime()   F
last analyzed

Complexity

Conditions 29
Paths 14400

Size

Total Lines 90
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 68
CRAP Score 29.0025

Importance

Changes 0
Metric Value
cc 29
eloc 71
nc 14400
nop 3
dl 0
loc 90
ccs 68
cts 69
cp 0.9855
crap 29.0025
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
namespace Xoops\Core\Locale;
13
14
use Xoops\Core\Locale\Punic\Calendar;
15
use Punic\Data;
16
use Punic\Plural;
17
use Xoops\Locale;
18
19
/**
20
 * Xoops\Core\Locale\Time - localized time handling
21
 *
22
 * @category  Xoops\Core\Locale\Time
23
 * @package   Xoops
24
 * @author    Richard Griffith <[email protected]>
25
 * @copyright 2015 XOOPS Project (http://xoops.org)/
26
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
27
 * @link      http://xoops.org
28
 */
29
class Time
30
{
31
    /**
32
     * cleanTime
33
     *
34
     * @param number|\DateTime|string $time An Unix timestamp, DateTime instance or string accepted by strtotime.
35
     *
36
     * @return \DateTime
37
     */
38 46
    public static function cleanTime($time = null)
39
    {
40 46
        if (is_a($time, '\DateTime')) {
41 20
            return $time->setTimezone(Locale::getTimeZone());
42
        }
43 30
        if ($time === null || $time === 0 || $time === '') {
44 28
            return new \DateTime('now', Locale::getTimeZone());
45
        }
46 22
        return Calendar::toDateTime($time, Locale::getTimeZone());
47
    }
48
49
    /**
50
     * Describe an relative interval from $dateStart to $dateEnd (eg '2 days ago').
51
     * Only the largest differing unit is described, and the next smaller unit will be used
52
     * for rounding.
53
     *
54
     * @param \DateTime      $dateEnd   The terminal date
55
     * @param \DateTime|null $dateStart The anchor date, defaults to now. (if it has a timezone different than
56
     *                        $dateEnd, we'll use the one of $dateEnd)
57
     * @param string         $width     The format name; it can be '', 'short' or 'narrow'
58
     * @param string         $locale    The locale to use. If empty we'll use the default locale set in \Punic\Data
59
     *
60
     * @return string
61
     *
62
     * @throws \InvalidArgumentException
63
     */
64 14
    public static function describeRelativeInterval($dateEnd, $dateStart = null, $width = '', $locale = '')
65
    {
66 14
        if (!is_a($dateEnd, '\DateTime')) {
67
            throw new \InvalidArgumentException('Not a DateTime object');
68
        }
69 14
        if (empty($dateStart) && ($dateStart !== 0) && ($dateStart !== '0')) {
70 14
            $dateStart = new \DateTime('now');
71
        } elseif (!is_a($dateStart, '\DateTime')) {
72
            throw new \InvalidArgumentException('Not a DateTime object');
73
        } else {
74
            $dateStart = clone $dateStart;
75
        }
76 14
        $dateStart->setTimezone($dateEnd->getTimezone());
77
78
        //$utc = new \DateTimeZone('UTC');
79
        //$dateEndUTC = new \DateTime($dateEnd->format('Y-m-d H:i:s'), $utc);
80
        //$dateStartUTC = new \DateTime($dateStart->format('Y-m-d H:i:s'), $utc);
81 14
        $parts = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $parts is dead and can be removed.
Loading history...
82 14
        $data = Data::get('dateFields', $locale);
83
84 14
        $diff = $dateStart->diff($dateEnd, false);
85 14
        $past = (boolean) $diff->invert;
86 14
        $value = 0;
87 14
        $key = '';
88 14
        if ($diff->y != 0) {
89 2
            $key = 'year';
90 2
            $value = $diff->y + (($diff->m > 6) ? 1 : 0);
91 12
        } elseif ($diff->m != 0) {
92
            $key = 'month';
93
            $value = $diff->m + (($diff->d > 15) ? 1 : 0);
94 12
        } elseif ($diff->d != 0) {
95 6
            $key = 'day';
96 6
            $value = $diff->d + (($diff->h > 12) ? 1 : 0);
97 6
        } elseif ($diff->h != 0) {
98
            $key = 'hour';
99
            $value = $diff->h + (($diff->i > 30) ? 1 : 0);
100 6
        } elseif ($diff->i != 0) {
101
            $key = 'minute';
102
            $value = $diff->i + (($diff->s > 30) ? 1 : 0);
103 6
        } elseif ($diff->s != 0) {
104 4
            $key = 'second';
105 4
            $value = $diff->s + round($diff->f, 0);
106
        }
107 14
        if ($value==0) {
108 2
            $key = 'second';
109 2
            $relKey = 'relative-type-0';
110 2
            $relPattern = null;
111 12
        } elseif ($key === 'day' && $value >1 && $value <7) {
112
            $dow = $dateEnd->format('N') - 1;
113
            $days = array('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun');
114
            $key = $days[$dow];
115
            $relKey = ($past) ? "relative-type--1" : "relative-type-1";
116
            $relPattern = null;
117
        } else {
118 12
            if ($value == 1 && isset($data[$key]['relative-type--1'])) {
119 4
                $relKey = ($past) ? 'relative-type--1' : 'relative-type-1';
120 4
                $relPattern = null;
121
            } else {
122 8
                $relKey = ($past) ? 'relativeTime-type-past' : 'relativeTime-type-future';
123 8
                $rule = Plural::getRuleOfType($value, Plural::RULETYPE_CARDINAL, $locale);
124 8
                $relPattern = 'relativeTimePattern-count-' . $rule;
125
            }
126
        }
127 14
        if (!empty($width) && array_key_exists($key . '-' . $width, $data)) {
128
            $key .= '-' . $width;
129
        }
130 14
        if (empty($relPattern)) {
131 6
            $relativeString = $data[$key][$relKey];
132
        } else {
133 8
            $tempString = $data[$key][$relKey][$relPattern];
134 8
            $tempString = str_replace('{0}', '%d', $tempString);
135 8
            $relativeString = sprintf($tempString, $value);
136
        }
137 14
        return $relativeString;
138
    }
139
140
    /**
141
     * Format a date.
142
     *
143
     * @param number|\DateTime|string $value      An Unix timestamp, a `\DateTime` instance or a string accepted
144
     *                                             by strtotime().
145
     * @param string                  $width      The format name; it can be
146
     *                                               'full' (eg 'EEEE, MMMM d, y' - 'Wednesday, August 20, 2014'),
147
     *                                               'long' (eg 'MMMM d, y' - 'August 20, 2014'),
148
     *                                               'medium' (eg 'MMM d, y' - 'August 20, 2014') or
149
     *                                               'short' (eg 'M/d/yy' - '8/20/14').
150
     * @param string|\DateTimeZone    $toTimezone The timezone to set; leave empty to use the default timezone
151
     *                                             (or the timezone associated to $value if it's already a \DateTime)
152
     * @param string                  $locale     The locale to use. If empty we'll use the default
153
     *
154
     * @return string Returns an empty string if $value is empty, the localized textual representation otherwise
155
     */
156 8
    public static function formatDate($value, $width = 'short', $toTimezone = '', $locale = '')
157
    {
158
        try {
159 8
            $formatted = Calendar::formatDateEx($value, $width, $toTimezone, $locale);
160
        } catch (\Punic\Exception $e) {
161
            \Xoops::getInstance()->events()->triggerEvent('core.exception', $e);
162
            $formatted = '';
163
        }
164 8
        return $formatted;
165
    }
166
167
    /**
168
     * Format a date.
169
     *
170
     * @param number|\DateTime|string $value      An Unix timestamp, a `\DateTime` instance or a string accepted
171
     *                                             by strtotime().
172
     * @param string                  $width      The format name; it can be
173
     *                                               'full' (eg 'h:mm:ss a zzzz' - '11:42:13 AM GMT+2:00'),
174
     *                                               'long' (eg 'h:mm:ss a z' - '11:42:13 AM GMT+2:00'),
175
     *                                               'medium' (eg 'h:mm:ss a' - '11:42:13 AM') or
176
     *                                               'short' (eg 'h:mm a' - '11:42 AM')
177
     * @param string|\DateTimeZone    $toTimezone The timezone to set; leave empty to use the default timezone
178
     *                                             (or the timezone associated to $value if it's already a \DateTime)
179
     * @param string                  $locale     The locale to use. If empty we'll use the default
180
     *
181
     * @return string Returns an empty string if $value is empty, the localized textual representation otherwise
182
     *
183
     * @throws \Punic\Exception Throws an exception in case of problems
184
     */
185 8
    public static function formatTime($value, $width = 'short', $toTimezone = '', $locale = '')
186
    {
187
        try {
188 8
            $formatted = Calendar::formatTimeEx($value, $width, $toTimezone, $locale);
189
        } catch (\Punic\Exception $e) {
190
            \Xoops::getInstance()->events()->triggerEvent('core.exception', $e);
191
            $formatted = '';
192
        }
193 8
        return $formatted;
194
    }
195
196
    /**
197
     * Format a date/time.
198
     *
199
     * @param \DateTime $value The \DateTime instance for which you want the localized textual representation
200
     * @param string    $width The format name; it can be 'full', 'long', 'medium', 'short' or a combination
201
     *                          for date+time like 'full|short' or a combination for format+date+time like
202
     *                          'full|full|short'
203
     *                          You can also append an asterisk ('*') to the date part of $width. If so,
204
     *                          special day names may be used (like 'Today', 'Yesterday', 'Tomorrow') instead
205
     *                          of the date part.
206
     * @param string $locale   The locale to use. If empty we'll use the default locale
207
     *
208
     * @return string Returns an empty string if $value is empty, the localized textual representation otherwise
209
     *
210
     * @throws \Punic\Exception Throws an exception in case of problems
211
     */
212 8
    public static function formatDateTime(\DateTime $value, $width, $locale = '')
213
    {
214 8
        return Calendar::formatDatetime($value, $width, $locale);
215
    }
216
217
    /**
218
     * Perform any localization required for date picker used in Form\DateSelect
219
     *
220
     * @return void
221
     */
222 4
    public static function localizeDatePicker()
223
    {
224 4
        $delimiter = '-';
225 4
        $locale = Locale::normalizeLocale(Locale::getCurrent(), $delimiter, false);
226 4
        if ('zh_Hant' === Locale::getCurrent()) {
227
            $locale = 'zh-TW';
228
        }
229 4
        if ($locale === 'zh') {
230
            $locale = 'zh-CN';
231
        }
232 4
        list($language) = explode($delimiter, $locale);
233 4
        $xoops = \Xoops::getInstance();
234
235 4
        $locales = array($locale, $language);
236 4
        foreach ($locales as $name) {
237 4
            $i18nScript = 'media/jquery/ui/i18n/datepicker-' . $name . '.js';
238 4
            if (file_exists($xoops->path($i18nScript))) {
239
                $xoops->theme()->addBaseScriptAssets($i18nScript);
0 ignored issues
show
Bug introduced by
$i18nScript of type string is incompatible with the type array expected by parameter $assets of Xoops\Core\Theme\XoopsTheme::addBaseScriptAssets(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

239
                $xoops->theme()->addBaseScriptAssets(/** @scrutinizer ignore-type */ $i18nScript);
Loading history...
240
                return;
241
            }
242
        }
243 4
    }
244
245
    /**
246
     * turn a utf8 string into an array of characters
247
     *
248
     * @param string $input string to convert
249
     *
250
     * @return array
251
     */
252 1
    protected static function utf8StringToChars($input)
253
    {
254 1
        $chars = array();
255 1
        $strLen = mb_strlen($input, 'UTF-8');
256 1
        for ($i = 0; $i < $strLen; $i++) {
257 1
            $chars[] = mb_substr($input, $i, 1, 'UTF-8');
258
        }
259 1
        return $chars;
260
    }
261
262
    /**
263
     * parse a date input according to a locale and apply it to a DateTime object
264
     *
265
     * @param \DateTime $datetime datetime to apply date to
266
     * @param string    $input    localized date string
267
     * @param string    $locale   optional locale to use, leave blank to use current
268
     *
269
     * @return void
270
     *
271
     * @throws \Punic\Exception\ValueNotInList
272
     */
273 1
    protected static function parseInputDate(\DateTime $datetime, $input, $locale = '')
274
    {
275 1
        $year = 0;
276 1
        $month = 0;
277 1
        $day = 0;
278
279 1
        $order = [];
280 1
        $dateFormat = Calendar::getDateFormat('short', $locale);
281 1
        $formatChars = static::utf8StringToChars($dateFormat);
282 1
        $state = 'non';
283 1
        $newstate = $state;
0 ignored issues
show
Unused Code introduced by
The assignment to $newstate is dead and can be removed.
Loading history...
284 1
        foreach ($formatChars as $char) {
285 1
            switch ($char) {
286 1
                case 'y':
287 1
                    $newstate = 'y';
288 1
                    break;
289 1
                case 'M':
290 1
                    $newstate = 'm';
291 1
                    break;
292 1
                case 'd':
293 1
                    $newstate = 'd';
294 1
                    break;
295
                default:
296 1
                    $newstate = 'non';
297 1
                    break;
298
            }
299 1
            if ($newstate !== $state) {
300 1
                if (in_array($newstate, ['y', 'm', 'd'])) {
301 1
                    $order[] = $newstate;
302
                }
303 1
                $state = $newstate;
304
            }
305
        }
306
307 1
        $pieces = [];
308 1
        $pieceIndex = -1;
309 1
        $inputChars = static::utf8StringToChars($input);
310 1
        $state = 'non';
311 1
        $newstate = $state;
312 1
        foreach ($inputChars as $char) {
313
            switch ($char) {
314 1
                case '0':
315 1
                case '1':
316 1
                case '2':
317 1
                case '3':
318 1
                case '4':
319 1
                case '5':
320 1
                case '6':
321 1
                case '7':
322 1
                case '8':
323 1
                case '9':
324 1
                    $newstate = 'digit';
325 1
                    break;
326
                default:
327 1
                    $newstate = 'non';
328 1
                    break;
329
            }
330 1
            if ($newstate !== $state) {
331 1
                if ($newstate === 'digit') {
332 1
                    $pieces[++$pieceIndex] = $char;
333
                }
334 1
                $state = $newstate;
335 1
            } elseif ($state === 'digit') {
336 1
                $pieces[$pieceIndex] .= $char;
337
            }
338
        }
339
340 1
        foreach ($pieces as $i => $piece) {
341 1
            $piece = (int) ltrim($piece, '0');
342 1
            switch ($order[$i]) {
343 1
                case 'd':
344 1
                    $day = $piece;
345 1
                    break;
346 1
                case 'm':
347 1
                    $month = $piece;
348 1
                    break;
349 1
                case 'y':
350 1
                    $year = $piece;
351 1
                    break;
352
            }
353
        }
354 1
        if ($year < 100) {
355
            if ($year<70) {
356
                $year += 2000;
357
            } else {
358
                $year += 1900;
359
            }
360
        }
361 1
        $datetime->setDate($year, $month, $day);
362
        // public DateTime DateTime::setTime ( int $hour , int $minute [, int $second = 0 ] )
363 1
    }
364
365
    /**
366
     * parse a time input according to a locale and apply it to a DateTime object
367
     *
368
     * @param \DateTime $datetime datetime to apply time to
369
     * @param string    $input    localized time string
370
     * @param string    $locale   optional locale to use, leave blank to use current
371
     *
372
     * @return void
373
     *
374
     * @throws \Punic\Exception\BadArgumentType
375
     * @throws \Punic\Exception\ValueNotInList
376
     */
377 1
    protected static function parseInputTime(\DateTime $datetime, $input, $locale = '')
378
    {
379 1
        $timeFormat = Calendar::getTimeFormat('short', $locale);
380 1
        $am = Calendar::getDayperiodName('am', 'wide', $locale);
381 1
        $pm = Calendar::getDayperiodName('pm', 'wide', $locale);
382 1
        $clock12 = Calendar::has12HoursClock($locale);
383
384 1
        $hour = 0;
385 1
        $minute = 0;
386 1
        $second = 0;
387
388 1
        $order = [];
389 1
        $formatChars = static::utf8StringToChars($timeFormat);
390 1
        $state = 'non';
391 1
        $newstate = $state;
0 ignored issues
show
Unused Code introduced by
The assignment to $newstate is dead and can be removed.
Loading history...
392 1
        foreach ($formatChars as $char) {
393 1
            switch ($char) {
394 1
                case 'h':
395 1
                case 'H':
396 1
                    $newstate = 'h';
397 1
                    break;
398 1
                case 'm':
399 1
                    $newstate = 'm';
400 1
                    break;
401 1
                case 'a':
402
                default:
403 1
                    $newstate = 'non';
404 1
                    break;
405
            }
406 1
            if ($newstate !== $state) {
407 1
                if (in_array($newstate, ['h', 'm'])) {
408 1
                    $order[] = $newstate;
409
                }
410 1
                $state = $newstate;
411
            }
412
        }
413
414 1
        $pieces = [];
415 1
        $pieceIndex = -1;
416 1
        $inputChars = static::utf8StringToChars($input);
417 1
        $state = 'non';
418 1
        $newstate = $state;
419 1
        foreach ($inputChars as $char) {
420
            switch ($char) {
421 1
                case '0':
422 1
                case '1':
423 1
                case '2':
424 1
                case '3':
425 1
                case '4':
426 1
                case '5':
427 1
                case '6':
428 1
                case '7':
429 1
                case '8':
430 1
                case '9':
431 1
                    $newstate = 'digit';
432 1
                    break;
433
                default:
434 1
                    $newstate = 'non';
435 1
                    break;
436
            }
437 1
            if ($newstate !== $state) {
438 1
                if ($newstate === 'digit') {
439 1
                    $pieces[++$pieceIndex] = $char;
440
                }
441 1
                $state = $newstate;
442 1
            } elseif ($state === 'digit') {
443 1
                $pieces[$pieceIndex] .= $char;
444
            }
445
        }
446
447 1
        foreach ($pieces as $i => $piece) {
448 1
            $piece = (int) ltrim($piece, '0');
449 1
            switch ($order[$i]) {
450 1
                case 'h':
451 1
                    $hour = $piece;
452 1
                    break;
453 1
                case 'm':
454 1
                    $minute = $piece;
455 1
                    break;
456
            }
457
        }
458 1
        if ($clock12) {
459 1
            if ($hour == 12 && false !== mb_strpos($input, $am)) {
460 1
                $hour = 0;
461
            }
462 1
            if (false !== mb_strpos($input, $pm)) {
463
                $hour += 12;
464
            }
465
        }
466 1
        $datetime->setTime($hour, $minute, $second);
467 1
    }
468
469
    /**
470
     * Convert a XOOPS DateSelect or DateTime form input into a DateTime object
471
     *
472
     * @param string|string[] $input  date string, or array of date and time strings
473
     * @param string          $locale optional locale to use, leave blank to use current
474
     *
475
     * @return \DateTime
476
     */
477 1
    public static function inputToDateTime($input, $locale = '')
478
    {
479 1
        $dateTime = static::cleanTime();
480 1
        $dateTime->setTime(0, 0, 0);
481
482 1
        if (is_array($input)) {
483 1
            static::parseInputDate($dateTime, $input['date'], $locale);
484 1
            static::parseInputTime($dateTime, $input['time'], $locale);
485
        } else { // single string should be just a date
486 1
            static::parseInputDate($dateTime, $input, $locale);
487
        }
488 1
        return $dateTime;
489
    }
490
}
491