Passed
Push — master ( fd241d...e02998 )
by Alexander
09:07
created

BaseFormatConverter::createFormatter()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 6
nop 3
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 4
rs 9.9666
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use IntlDateFormatter;
11
use Yii;
12
13
/**
14
 * BaseFormatConverter provides concrete implementation for [[FormatConverter]].
15
 *
16
 * Do not use BaseFormatConverter. Use [[FormatConverter]] instead.
17
 *
18
 * @author Carsten Brandt <[email protected]>
19
 * @author Enrica Ruedin <[email protected]>
20
 * @since 2.0
21
 */
22
class BaseFormatConverter
23
{
24
    /**
25
     * @var array the php fallback definition to use for the ICU short patterns `short`, `medium`, `long` and `full`.
26
     * This is used as fallback when the `intl` extension is not installed.
27
     */
28
    public static $phpFallbackDatePatterns = [
29
        'short' => [
30
            'date' => 'n/j/y',
31
            'time' => 'H:i',
32
            'datetime' => 'n/j/y H:i',
33
        ],
34
        'medium' => [
35
            'date' => 'M j, Y',
36
            'time' => 'g:i:s A',
37
            'datetime' => 'M j, Y g:i:s A',
38
        ],
39
        'long' => [
40
            'date' => 'F j, Y',
41
            'time' => 'g:i:sA',
42
            'datetime' => 'F j, Y g:i:sA',
43
        ],
44
        'full' => [
45
            'date' => 'l, F j, Y',
46
            'time' => 'g:i:sA T',
47
            'datetime' => 'l, F j, Y g:i:sA T',
48
        ],
49
    ];
50
    /**
51
     * @var array the jQuery UI fallback definition to use for the ICU short patterns `short`, `medium`, `long` and `full`.
52
     * This is used as fallback when the `intl` extension is not installed.
53
     */
54
    public static $juiFallbackDatePatterns = [
55
        'short' => [
56
            'date' => 'd/m/y',
57
            'time' => '',
58
            'datetime' => 'd/m/y',
59
        ],
60
        'medium' => [
61
            'date' => 'M d, yy',
62
            'time' => '',
63
            'datetime' => 'M d, yy',
64
        ],
65
        'long' => [
66
            'date' => 'MM d, yy',
67
            'time' => '',
68
            'datetime' => 'MM d, yy',
69
        ],
70
        'full' => [
71
            'date' => 'DD, MM d, yy',
72
            'time' => '',
73
            'datetime' => 'DD, MM d, yy',
74
        ],
75
    ];
76
77
    private static $_icuShortFormats = [
78
        'short' => 3, // IntlDateFormatter::SHORT,
79
        'medium' => 2, // IntlDateFormatter::MEDIUM,
80
        'long' => 1, // IntlDateFormatter::LONG,
81
        'full' => 0, // IntlDateFormatter::FULL,
82
    ];
83
84
85
    /**
86
     * Converts a date format pattern from [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)
87
     * to [PHP `date()` function format](https://www.php.net/manual/en/function.date).
88
     *
89
     * The conversion is limited to date patterns that do not use escaped characters.
90
     * Patterns like `d 'of' MMMM yyyy` which will result in a date like `1 of December 2014` may not be converted correctly
91
     * because of the use of escaped characters.
92
     *
93
     * Pattern constructs that are not supported by the PHP format will be removed.
94
     *
95
     * @param string $pattern date format pattern in ICU format.
96
     * @param string $type 'date', 'time', or 'datetime'.
97
     * @param string|null $locale the locale to use for converting ICU short patterns `short`, `medium`, `long` and `full`.
98
     * If not given, `Yii::$app->language` will be used.
99
     * @return string The converted date format pattern.
100
     * @throws \Exception
101
     */
102 245
    public static function convertDateIcuToPhp($pattern, $type = 'date', $locale = null)
103
    {
104 245
        if (isset(self::$_icuShortFormats[$pattern])) {
105 9
            if (extension_loaded('intl')) {
106 4
                $pattern = static::createFormatter($locale, $type, $pattern);
107
            } else {
108 5
                return static::$phpFallbackDatePatterns[$pattern][$type];
109
            }
110
        }
111
        // https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
112
        // escaped text
113 241
        $escaped = [];
114 241
        if (preg_match_all('/(?<!\')\'(.*?[^\'])\'(?!\')/', $pattern, $matches, PREG_SET_ORDER)) {
115 2
            foreach ($matches as $match) {
116 2
                $match[1] = str_replace('\'\'', '\'', $match[1]);
117 2
                $escaped[$match[0]] = '\\' . implode('\\', preg_split('//u', $match[1], -1, PREG_SPLIT_NO_EMPTY));
118
            }
119
        }
120
121 241
        return strtr($pattern, array_merge($escaped, [
122 241
            "''" => "\\'",  // two single quotes produce one
123
            'G' => '',      // era designator like (Anno Domini)
124
            'Y' => 'o',     // 4digit year of "Week of Year"
125
            'y' => 'Y',     // 4digit year e.g. 2014
126
            'yyyy' => 'Y',  // 4digit year e.g. 2014
127
            'yy' => 'y',    // 2digit year number eg. 14
128
            'u' => '',      // extended year e.g. 4601
129
            'U' => '',      // cyclic year name, as in Chinese lunar calendar
130
            'r' => '',      // related Gregorian year e.g. 1996
131
            'Q' => '',      // number of quarter
132
            'QQ' => '',     // number of quarter '02'
133
            'QQQ' => '',    // quarter 'Q2'
134
            'QQQQ' => '',   // quarter '2nd quarter'
135
            'QQQQQ' => '',  // number of quarter '2'
136
            'q' => '',      // number of Stand Alone quarter
137
            'qq' => '',     // number of Stand Alone quarter '02'
138
            'qqq' => '',    // Stand Alone quarter 'Q2'
139
            'qqqq' => '',   // Stand Alone quarter '2nd quarter'
140
            'qqqqq' => '',  // number of Stand Alone quarter '2'
141
            'M' => 'n',     // Numeric representation of a month, without leading zeros
142
            'MM' => 'm',    // Numeric representation of a month, with leading zeros
143
            'MMM' => 'M',   // A short textual representation of a month, three letters
144
            'MMMM' => 'F',  // A full textual representation of a month, such as January or March
145
            'MMMMM' => '',
146
            'L' => 'n',     // Stand alone month in year
147
            'LL' => 'm',    // Stand alone month in year
148
            'LLL' => 'M',   // Stand alone month in year
149
            'LLLL' => 'F',  // Stand alone month in year
150
            'LLLLL' => '',  // Stand alone month in year
151
            'w' => 'W',     // ISO-8601 week number of year
152
            'ww' => 'W',    // ISO-8601 week number of year
153
            'W' => '',      // week of the current month
154
            'd' => 'j',     // day without leading zeros
155
            'dd' => 'd',    // day with leading zeros
156
            'D' => 'z',     // day of the year 0 to 365
157
            'F' => '',      // Day of Week in Month. eg. 2nd Wednesday in July
158
            'g' => '',      // Modified Julian day. This is different from the conventional Julian day number in two regards.
159
            'E' => 'D',     // day of week written in short form eg. Sun
160
            'EE' => 'D',
161
            'EEE' => 'D',
162
            'EEEE' => 'l',  // day of week fully written eg. Sunday
163
            'EEEEE' => '',
164
            'EEEEEE' => '',
165
            'e' => 'N',     // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun
166
            'ee' => 'N',    // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year
167
            'eee' => 'D',
168
            'eeee' => 'l',
169
            'eeeee' => '',
170
            'eeeeee' => '',
171
            'c' => 'N',     // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun
172
            'cc' => 'N',    // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year
173
            'ccc' => 'D',
174
            'cccc' => 'l',
175
            'ccccc' => '',
176
            'cccccc' => '',
177
            'a' => 'A',     // AM/PM marker
178
            'h' => 'g',     // 12-hour format of an hour without leading zeros 1 to 12h
179
            'hh' => 'h',    // 12-hour format of an hour with leading zeros, 01 to 12 h
180
            'H' => 'G',     // 24-hour format of an hour without leading zeros 0 to 23h
181
            'HH' => 'H',    // 24-hour format of an hour with leading zeros, 00 to 23 h
182
            'k' => '',      // hour in day (1~24)
183
            'kk' => '',     // hour in day (1~24)
184
            'K' => '',      // hour in am/pm (0~11)
185
            'KK' => '',     // hour in am/pm (0~11)
186
            'm' => 'i',     // Minutes without leading zeros, not supported by php but we fallback
187
            'mm' => 'i',    // Minutes with leading zeros
188
            's' => 's',     // Seconds, without leading zeros, not supported by php but we fallback
189
            'ss' => 's',    // Seconds, with leading zeros
190
            'S' => '',      // fractional second
191
            'SS' => '',     // fractional second
192
            'SSS' => '',    // fractional second
193
            'SSSS' => '',   // fractional second
194
            'A' => '',      // milliseconds in day
195
            'z' => 'T',     // Timezone abbreviation
196
            'zz' => 'T',    // Timezone abbreviation
197
            'zzz' => 'T',   // Timezone abbreviation
198
            'zzzz' => 'T',  // Timezone full name, not supported by php but we fallback
199
            'Z' => 'O',     // Difference to Greenwich time (GMT) in hours
200
            'ZZ' => 'O',    // Difference to Greenwich time (GMT) in hours
201
            'ZZZ' => 'O',   // Difference to Greenwich time (GMT) in hours
202
            'ZZZZ' => '\G\M\TP', // Time Zone: long localized GMT (=OOOO) e.g. GMT-08:00
203
            'ZZZZZ' => '',  //  TIme Zone: ISO8601 extended hms? (=XXXXX)
204
            'O' => '',      // Time Zone: short localized GMT e.g. GMT-8
205
            'OOOO' => '\G\M\TP', //  Time Zone: long localized GMT (=ZZZZ) e.g. GMT-08:00
206
            'v' => '\G\M\TP', // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here
207
            'vvvv' => '\G\M\TP', // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here
208
            'V' => '',      // Time Zone: short time zone ID
209
            'VV' => 'e',    // Time Zone: long time zone ID
210
            'VVV' => '',    // Time Zone: time zone exemplar city
211
            'VVVV' => '\G\M\TP', // Time Zone: generic location (falls back to OOOO) using the ICU defined fallback here
212
            'X' => '',      // Time Zone: ISO8601 basic hm?, with Z for 0, e.g. -08, +0530, Z
213
            'XX' => 'O, \Z', // Time Zone: ISO8601 basic hm, with Z, e.g. -0800, Z
214
            'XXX' => 'P, \Z',    // Time Zone: ISO8601 extended hm, with Z, e.g. -08:00, Z
215
            'XXXX' => '',   // Time Zone: ISO8601 basic hms?, with Z, e.g. -0800, -075258, Z
216
            'XXXXX' => '',  // Time Zone: ISO8601 extended hms?, with Z, e.g. -08:00, -07:52:58, Z
217
            'x' => '',      // Time Zone: ISO8601 basic hm?, without Z for 0, e.g. -08, +0530
218
            'xx' => 'O',    // Time Zone: ISO8601 basic hm, without Z, e.g. -0800
219
            'xxx' => 'P',   // Time Zone: ISO8601 extended hm, without Z, e.g. -08:00
220
            'xxxx' => '',   // Time Zone: ISO8601 basic hms?, without Z, e.g. -0800, -075258
221
            'xxxxx' => '',  // Time Zone: ISO8601 extended hms?, without Z, e.g. -08:00, -07:52:58
222
        ]));
223
    }
224
225
    /**
226
     * Converts a date format pattern from [PHP `date()` function format](https://www.php.net/manual/en/function.date)
227
     * to [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax).
228
     *
229
     * Pattern constructs that are not supported by the ICU format will be removed.
230
     *
231
     * Since 2.0.13 it handles escaped characters correctly.
232
     *
233
     * @param string $pattern date format pattern in PHP `date()` function format.
234
     * @return string The converted date format pattern.
235
     */
236 85
    public static function convertDatePhpToIcu($pattern)
237
    {
238
        // https://www.php.net/manual/en/function.date
239 85
        $result = strtr($pattern, [
240 85
            "'" => "''''",  // single `'` should be encoded as `''`, which internally should be encoded as `''''`
241
            // Day
242
            '\d' => "'d'",
243
            'd' => 'dd',    // Day of the month, 2 digits with leading zeros 	01 to 31
244
            '\D' => "'D'",
245
            'D' => 'eee',   // A textual representation of a day, three letters 	Mon through Sun
246
            '\j' => "'j'",
247
            'j' => 'd',     // Day of the month without leading zeros 	1 to 31
248
            '\l' => "'l'",
249
            'l' => 'eeee',  // A full textual representation of the day of the week 	Sunday through Saturday
250
            '\N' => "'N'",
251
            'N' => 'e',     // ISO-8601 numeric representation of the day of the week, 1 (for Monday) through 7 (for Sunday)
252
            '\S' => "'S'",
253
            'S' => '',      // English ordinal suffix for the day of the month, 2 characters 	st, nd, rd or th. Works well with j
254
            '\w' => "'w'",
255
            'w' => '',      // Numeric representation of the day of the week 	0 (for Sunday) through 6 (for Saturday)
256
            '\z' => "'z'",
257
            'z' => 'D',     // The day of the year (starting from 0) 	0 through 365
258
            // Week
259
            '\W' => "'W'",
260
            'W' => 'w',     // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) 	Example: 42 (the 42nd week in the year)
261
            // Month
262
            '\F' => "'F'",
263
            'F' => 'MMMM',  // A full textual representation of a month, January through December
264
            '\m' => "'m'",
265
            'm' => 'MM',    // Numeric representation of a month, with leading zeros 	01 through 12
266
            '\M' => "'M'",
267
            'M' => 'MMM',   // A short textual representation of a month, three letters 	Jan through Dec
268
            '\n' => "'n'",
269
            'n' => 'M',     // Numeric representation of a month, without leading zeros 	1 through 12, not supported by ICU but we fallback to "with leading zero"
270
            '\t' => "'t'",
271
            't' => '',      // Number of days in the given month 	28 through 31
272
            // Year
273
            '\L' => "'L'",
274
            'L' => '',      // Whether it's a leap year, 1 if it is a leap year, 0 otherwise.
275
            '\o' => "'o'",
276
            'o' => 'Y',     // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.
277
            '\Y' => "'Y'",
278
            'Y' => 'yyyy',  // A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
279
            '\y' => "'y'",
280
            'y' => 'yy',    // A two digit representation of a year 	Examples: 99 or 03
281
            // Time
282
            '\a' => "'a'",
283
            'a' => 'a',     // Lowercase Ante meridiem and Post meridiem, am or pm
284
            '\A' => "'A'",
285
            'A' => 'a',     // Uppercase Ante meridiem and Post meridiem, AM or PM, not supported by ICU but we fallback to lowercase
286
            '\B' => "'B'",
287
            'B' => '',      // Swatch Internet time 	000 through 999
288
            '\g' => "'g'",
289
            'g' => 'h',     // 12-hour format of an hour without leading zeros 	1 through 12
290
            '\G' => "'G'",
291
            'G' => 'H',     // 24-hour format of an hour without leading zeros 0 to 23h
292
            '\h' => "'h'",
293
            'h' => 'hh',    // 12-hour format of an hour with leading zeros, 01 to 12 h
294
            '\H' => "'H'",
295
            'H' => 'HH',    // 24-hour format of an hour with leading zeros, 00 to 23 h
296
            '\i' => "'i'",
297
            'i' => 'mm',    // Minutes with leading zeros 	00 to 59
298
            '\s' => "'s'",
299
            's' => 'ss',    // Seconds, with leading zeros 	00 through 59
300
            '\u' => "'u'",
301
            'u' => '',      // Microseconds. Example: 654321
302
            // Timezone
303
            '\e' => "'e'",
304
            'e' => 'VV',    // Timezone identifier. Examples: UTC, GMT, Atlantic/Azores
305
            '\I' => "'I'",
306
            'I' => '',      // Whether or not the date is in daylight saving time, 1 if Daylight Saving Time, 0 otherwise.
307
            '\O' => "'O'",
308
            'O' => 'xx',    // Difference to Greenwich time (GMT) in hours, Example: +0200
309
            '\P' => "'P'",
310
            'P' => 'xxx',   // Difference to Greenwich time (GMT) with colon between hours and minutes, Example: +02:00
311
            '\T' => "'T'",
312
            'T' => 'zzz',   // Timezone abbreviation, Examples: EST, MDT ...
313
            '\Z' => "'Z'",
314
            'Z' => '',      // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400
315
            // Full Date/Time
316
            '\c' => "'c'",
317
            'c' => "yyyy-MM-dd'T'HH:mm:ssxxx", // ISO 8601 date, e.g. 2004-02-12T15:19:21+00:00
318
            '\r' => "'r'",
319
            'r' => 'eee, dd MMM yyyy HH:mm:ss xx', // RFC 2822 formatted date, Example: Thu, 21 Dec 2000 16:01:07 +0200
320
            '\U' => "'U'",
321
            'U' => '',      // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
322
            '\\\\' => '\\',
323
        ]);
324
325
        // remove `''` - they're result of consecutive escaped chars (`\A\B` will be `'A''B'`, but should be `'AB'`)
326
        // real `'` are encoded as `''''`
327 85
        return strtr($result, [
328 85
            "''''" => "''",
329
            "''" => '',
330
        ]);
331
    }
332
333
    /**
334
     * Converts a date format pattern from [ICU format](https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax)
335
     * to [jQuery UI date format](https://api.jqueryui.com/datepicker/#utility-formatDate).
336
     *
337
     * Pattern constructs that are not supported by the jQuery UI format will be removed.
338
     *
339
     * @param string $pattern date format pattern in ICU format.
340
     * @param string $type 'date', 'time', or 'datetime'.
341
     * @param string|null $locale the locale to use for converting ICU short patterns `short`, `medium`, `long` and `full`.
342
     * If not given, `Yii::$app->language` will be used.
343
     * @return string The converted date format pattern.
344
     * @throws \Exception
345
     */
346 105
    public static function convertDateIcuToJui($pattern, $type = 'date', $locale = null)
347
    {
348 105
        if (isset(self::$_icuShortFormats[$pattern])) {
349 4
            if (extension_loaded('intl')) {
350 4
                $pattern = static::createFormatter($locale, $type, $pattern);
351
            } else {
352
                return static::$juiFallbackDatePatterns[$pattern][$type];
353
            }
354
        }
355
        // https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
356
        // escaped text
357 105
        $escaped = [];
358 105
        if (preg_match_all('/(?<!\')\'.*?[^\']\'(?!\')/', $pattern, $matches)) {
359 2
            foreach ($matches[0] as $match) {
360 2
                $escaped[$match] = $match;
361
            }
362
        }
363
364 105
        return strtr($pattern, array_merge($escaped, [
365 105
            'G' => '',      // era designator like (Anno Domini)
366
            'Y' => '',      // 4digit year of "Week of Year"
367
            'y' => 'yy',    // 4digit year e.g. 2014
368
            'yyyy' => 'yy', // 4digit year e.g. 2014
369
            'yy' => 'y',    // 2digit year number eg. 14
370
            'u' => '',      // extended year e.g. 4601
371
            'U' => '',      // cyclic year name, as in Chinese lunar calendar
372
            'r' => '',      // related Gregorian year e.g. 1996
373
            'Q' => '',      // number of quarter
374
            'QQ' => '',     // number of quarter '02'
375
            'QQQ' => '',    // quarter 'Q2'
376
            'QQQQ' => '',   // quarter '2nd quarter'
377
            'QQQQQ' => '',  // number of quarter '2'
378
            'q' => '',      // number of Stand Alone quarter
379
            'qq' => '',     // number of Stand Alone quarter '02'
380
            'qqq' => '',    // Stand Alone quarter 'Q2'
381
            'qqqq' => '',   // Stand Alone quarter '2nd quarter'
382
            'qqqqq' => '',  // number of Stand Alone quarter '2'
383
            'M' => 'm',     // Numeric representation of a month, without leading zeros
384
            'MM' => 'mm',   // Numeric representation of a month, with leading zeros
385
            'MMM' => 'M',   // A short textual representation of a month, three letters
386
            'MMMM' => 'MM', // A full textual representation of a month, such as January or March
387
            'MMMMM' => '',
388
            'L' => 'm',     // Stand alone month in year
389
            'LL' => 'mm',   // Stand alone month in year
390
            'LLL' => 'M',   // Stand alone month in year
391
            'LLLL' => 'MM', // Stand alone month in year
392
            'LLLLL' => '',  // Stand alone month in year
393
            'w' => '',      // ISO-8601 week number of year
394
            'ww' => '',     // ISO-8601 week number of year
395
            'W' => '',      // week of the current month
396
            'd' => 'd',     // day without leading zeros
397
            'dd' => 'dd',   // day with leading zeros
398
            'D' => 'o',     // day of the year 0 to 365
399
            'F' => '',      // Day of Week in Month. eg. 2nd Wednesday in July
400
            'g' => '',      // Modified Julian day. This is different from the conventional Julian day number in two regards.
401
            'E' => 'D',     // day of week written in short form eg. Sun
402
            'EE' => 'D',
403
            'EEE' => 'D',
404
            'EEEE' => 'DD', // day of week fully written eg. Sunday
405
            'EEEEE' => '',
406
            'EEEEEE' => '',
407
            'e' => '',      // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun
408
            'ee' => '',     // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year
409
            'eee' => 'D',
410
            'eeee' => '',
411
            'eeeee' => '',
412
            'eeeeee' => '',
413
            'c' => '',      // ISO-8601 numeric representation of the day of the week 1=Mon to 7=Sun
414
            'cc' => '',     // php 'w' 0=Sun to 6=Sat isn't supported by ICU -> 'w' means week number of year
415
            'ccc' => 'D',
416
            'cccc' => 'DD',
417
            'ccccc' => '',
418
            'cccccc' => '',
419
            'a' => '',      // am/pm marker
420
            'h' => '',      // 12-hour format of an hour without leading zeros 1 to 12h
421
            'hh' => '',     // 12-hour format of an hour with leading zeros, 01 to 12 h
422
            'H' => '',      // 24-hour format of an hour without leading zeros 0 to 23h
423
            'HH' => '',     // 24-hour format of an hour with leading zeros, 00 to 23 h
424
            'k' => '',      // hour in day (1~24)
425
            'kk' => '',     // hour in day (1~24)
426
            'K' => '',      // hour in am/pm (0~11)
427
            'KK' => '',     // hour in am/pm (0~11)
428
            'm' => '',      // Minutes without leading zeros, not supported by php but we fallback
429
            'mm' => '',     // Minutes with leading zeros
430
            's' => '',      // Seconds, without leading zeros, not supported by php but we fallback
431
            'ss' => '',     // Seconds, with leading zeros
432
            'S' => '',      // fractional second
433
            'SS' => '',     // fractional second
434
            'SSS' => '',    // fractional second
435
            'SSSS' => '',   // fractional second
436
            'A' => '',      // milliseconds in day
437
            'z' => '',      // Timezone abbreviation
438
            'zz' => '',     // Timezone abbreviation
439
            'zzz' => '',    // Timezone abbreviation
440
            'zzzz' => '',   // Timezone full name, not supported by php but we fallback
441
            'Z' => '',      // Difference to Greenwich time (GMT) in hours
442
            'ZZ' => '',     // Difference to Greenwich time (GMT) in hours
443
            'ZZZ' => '',    // Difference to Greenwich time (GMT) in hours
444
            'ZZZZ' => '',   // Time Zone: long localized GMT (=OOOO) e.g. GMT-08:00
445
            'ZZZZZ' => '',  // Time Zone: ISO8601 extended hms? (=XXXXX)
446
            'O' => '',      // Time Zone: short localized GMT e.g. GMT-8
447
            'OOOO' => '',   // Time Zone: long localized GMT (=ZZZZ) e.g. GMT-08:00
448
            'v' => '',      // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here
449
            'vvvv' => '',   // Time Zone: generic non-location (falls back first to VVVV and then to OOOO) using the ICU defined fallback here
450
            'V' => '',      // Time Zone: short time zone ID
451
            'VV' => '',     // Time Zone: long time zone ID
452
            'VVV' => '',    // Time Zone: time zone exemplar city
453
            'VVVV' => '',   // Time Zone: generic location (falls back to OOOO) using the ICU defined fallback here
454
            'X' => '',      // Time Zone: ISO8601 basic hm?, with Z for 0, e.g. -08, +0530, Z
455
            'XX' => '',     // Time Zone: ISO8601 basic hm, with Z, e.g. -0800, Z
456
            'XXX' => '',    // Time Zone: ISO8601 extended hm, with Z, e.g. -08:00, Z
457
            'XXXX' => '',   // Time Zone: ISO8601 basic hms?, with Z, e.g. -0800, -075258, Z
458
            'XXXXX' => '',  // Time Zone: ISO8601 extended hms?, with Z, e.g. -08:00, -07:52:58, Z
459
            'x' => '',      // Time Zone: ISO8601 basic hm?, without Z for 0, e.g. -08, +0530
460
            'xx' => '',     // Time Zone: ISO8601 basic hm, without Z, e.g. -0800
461
            'xxx' => '',    // Time Zone: ISO8601 extended hm, without Z, e.g. -08:00
462
            'xxxx' => '',   // Time Zone: ISO8601 basic hms?, without Z, e.g. -0800, -075258
463
            'xxxxx' => '',  // Time Zone: ISO8601 extended hms?, without Z, e.g. -08:00, -07:52:58
464
        ]));
465
    }
466
467
    /**
468
     * Converts a date format pattern from [PHP `date()` function format](https://www.php.net/manual/en/function.date)
469
     * to [jQuery UI date format](https://api.jqueryui.com/datepicker/#utility-formatDate).
470
     *
471
     * The conversion is limited to date patterns that do not use escaped characters.
472
     * Patterns like `jS \o\f F Y` which will result in a date like `1st of December 2014` may not be converted correctly
473
     * because of the use of escaped characters.
474
     *
475
     * Pattern constructs that are not supported by the jQuery UI format will be removed.
476
     *
477
     * @param string $pattern date format pattern in PHP `date()` function format.
478
     * @return string The converted date format pattern.
479
     */
480 38
    public static function convertDatePhpToJui($pattern)
481
    {
482
        // https://www.php.net/manual/en/function.date
483 38
        return strtr($pattern, [
484
            // Day
485 38
            'd' => 'dd',    // Day of the month, 2 digits with leading zeros 	01 to 31
486
            'D' => 'D',     // A textual representation of a day, three letters 	Mon through Sun
487
            'j' => 'd',     // Day of the month without leading zeros 	1 to 31
488
            'l' => 'DD',    // A full textual representation of the day of the week 	Sunday through Saturday
489
            'N' => '',      // ISO-8601 numeric representation of the day of the week, 1 (for Monday) through 7 (for Sunday)
490
            'S' => '',      // English ordinal suffix for the day of the month, 2 characters 	st, nd, rd or th. Works well with j
491
            'w' => '',      // Numeric representation of the day of the week 	0 (for Sunday) through 6 (for Saturday)
492
            'z' => 'o',     // The day of the year (starting from 0) 	0 through 365
493
            // Week
494
            'W' => '',      // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) 	Example: 42 (the 42nd week in the year)
495
            // Month
496
            'F' => 'MM',    // A full textual representation of a month, January through December
497
            'm' => 'mm',    // Numeric representation of a month, with leading zeros 	01 through 12
498
            'M' => 'M',     // A short textual representation of a month, three letters 	Jan through Dec
499
            'n' => 'm',     // Numeric representation of a month, without leading zeros 	1 through 12
500
            't' => '',      // Number of days in the given month 	28 through 31
501
            // Year
502
            'L' => '',      // Whether it's a leap year, 1 if it is a leap year, 0 otherwise.
503
            'o' => '',      // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.
504
            'Y' => 'yy',    // A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
505
            'y' => 'y',     // A two digit representation of a year 	Examples: 99 or 03
506
            // Time
507
            'a' => '',      // Lowercase Ante meridiem and Post meridiem, am or pm
508
            'A' => '',      // Uppercase Ante meridiem and Post meridiem, AM or PM, not supported by ICU but we fallback to lowercase
509
            'B' => '',      // Swatch Internet time 	000 through 999
510
            'g' => '',      // 12-hour format of an hour without leading zeros 	1 through 12
511
            'G' => '',      // 24-hour format of an hour without leading zeros 0 to 23h
512
            'h' => '',      // 12-hour format of an hour with leading zeros, 01 to 12 h
513
            'H' => '',      // 24-hour format of an hour with leading zeros, 00 to 23 h
514
            'i' => '',      // Minutes with leading zeros 	00 to 59
515
            's' => '',      // Seconds, with leading zeros 	00 through 59
516
            'u' => '',      // Microseconds. Example: 654321
517
            // Timezone
518
            'e' => '',      // Timezone identifier. Examples: UTC, GMT, Atlantic/Azores
519
            'I' => '',      // Whether or not the date is in daylight saving time, 1 if Daylight Saving Time, 0 otherwise.
520
            'O' => '',      // Difference to Greenwich time (GMT) in hours, Example: +0200
521
            'P' => '',      // Difference to Greenwich time (GMT) with colon between hours and minutes, Example: +02:00
522
            'T' => '',      // Timezone abbreviation, Examples: EST, MDT ...
523
            'Z' => '',      // Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400
524
            // Full Date/Time
525
            'c' => 'yyyy-MM-dd', // ISO 8601 date, e.g. 2004-02-12T15:19:21+00:00, skipping the time here because it is not supported
526
            'r' => 'D, d M yy', // RFC 2822 formatted date, Example: Thu, 21 Dec 2000 16:01:07 +0200, skipping the time here because it is not supported
527
            'U' => '@',     // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
528
        ]);
529
    }
530
531
    /**
532
     * Creates a date/time formatter based on the given parameters.
533
     *
534
     * @param string|null $locale The locale to be used. If null, the application's current language will be used.
535
     * @param string $type The type of formatter ('date', 'time', etc.)
536
     * @param string $pattern The pattern for the IntlDateFormatter.
537
     *
538
     * @return string The resulting pattern after formatter creation.
539
     *
540
     * @throws \Exception If the 'intl' extension is not loaded.
541
     */
542 8
    private static function createFormatter($locale, $type, $pattern)
543
    {
544 8
        if ($locale === null) {
545 2
            $locale = Yii::$app->language;
546
        }
547
548 8
        if ($type === 'date') {
549 4
            $formatter = new IntlDateFormatter($locale, self::$_icuShortFormats[$pattern], IntlDateFormatter::NONE);
550 4
        } elseif ($type === 'time') {
551 2
            $formatter = new IntlDateFormatter($locale, IntlDateFormatter::NONE, self::$_icuShortFormats[$pattern]);
552
        } else {
553 2
            $formatter = new IntlDateFormatter($locale, self::$_icuShortFormats[$pattern], self::$_icuShortFormats[$pattern]);
554
        }
555
556 8
        return $formatter->getPattern();
557
    }
558
}
559