BaseFormatConverter.php 31 KB

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