Formatter.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\i18n;
  8. use Yii;
  9. use IntlDateFormatter;
  10. use NumberFormatter;
  11. use DateTime;
  12. use yii\base\InvalidConfigException;
  13. /**
  14. * Formatter is the localized version of [[\yii\base\Formatter]].
  15. *
  16. * Formatter requires the PHP "intl" extension to be installed. Formatter supports localized
  17. * formatting of date, time and numbers, based on the current [[locale]].
  18. *
  19. * This Formatter can replace the `formatter` application component that is configured by default.
  20. * To do so, add the following to your application config under `components`:
  21. *
  22. * ```php
  23. * 'formatter' => [
  24. * 'class' => 'yii\i18n\Formatter',
  25. * ]
  26. * ```
  27. *
  28. * @author Qiang Xue <[email protected]>
  29. * @since 2.0
  30. */
  31. class Formatter extends \yii\base\Formatter
  32. {
  33. /**
  34. * @var string the locale ID that is used to localize the date and number formatting.
  35. * If not set, [[\yii\base\Application::language]] will be used.
  36. */
  37. public $locale;
  38. /**
  39. * @var string|\IntlTimeZone|\DateTimeZone the timezone to use for formatting time and date values.
  40. * This can be any value that may be passed to [date_default_timezone_set()](http://www.php.net/manual/en/function.date-default-timezone-set.php)
  41. * e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
  42. * Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
  43. * This can also be an IntlTimeZone or a DateTimeZone object.
  44. * If not set, [[\yii\base\Application::timezone]] will be used.
  45. */
  46. public $timeZone;
  47. /**
  48. * @var string the default format string to be used to format a date.
  49. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  50. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  51. */
  52. public $dateFormat = 'short';
  53. /**
  54. * @var string the default format string to be used to format a time.
  55. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  56. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  57. */
  58. public $timeFormat = 'short';
  59. /**
  60. * @var string the default format string to be used to format a date and time.
  61. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  62. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  63. */
  64. public $datetimeFormat = 'short';
  65. /**
  66. * @var array the options to be set for the NumberFormatter objects. Please refer to
  67. * [PHP manual](http://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute)
  68. * for the possible options. This property is used by [[createNumberFormatter]] when
  69. * creating a new number formatter to format decimals, currencies, etc.
  70. */
  71. public $numberFormatOptions = [];
  72. /**
  73. * @var string the character displayed as the decimal point when formatting a number.
  74. * If not set, the decimal separator corresponding to [[locale]] will be used.
  75. */
  76. public $decimalSeparator;
  77. /**
  78. * @var string the character displayed as the thousands separator character when formatting a number.
  79. * If not set, the thousand separator corresponding to [[locale]] will be used.
  80. */
  81. public $thousandSeparator;
  82. /**
  83. * Initializes the component.
  84. * This method will check if the "intl" PHP extension is installed and set the
  85. * default value of [[locale]].
  86. * @throws InvalidConfigException if the "intl" PHP extension is not installed.
  87. */
  88. public function init()
  89. {
  90. if (!extension_loaded('intl')) {
  91. throw new InvalidConfigException('The "intl" PHP extension is not installed. It is required to format data values in localized formats.');
  92. }
  93. if ($this->locale === null) {
  94. $this->locale = Yii::$app->language;
  95. }
  96. if ($this->timeZone === null) {
  97. $this->timeZone = Yii::$app->timeZone;
  98. }
  99. if ($this->decimalSeparator === null || $this->thousandSeparator === null) {
  100. $formatter = new NumberFormatter($this->locale, NumberFormatter::DECIMAL);
  101. if ($this->decimalSeparator === null) {
  102. $this->decimalSeparator = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
  103. }
  104. if ($this->thousandSeparator === null) {
  105. $this->thousandSeparator = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
  106. }
  107. }
  108. parent::init();
  109. }
  110. private $_dateFormats = [
  111. 'short' => IntlDateFormatter::SHORT,
  112. 'medium' => IntlDateFormatter::MEDIUM,
  113. 'long' => IntlDateFormatter::LONG,
  114. 'full' => IntlDateFormatter::FULL,
  115. ];
  116. /**
  117. * Formats the value as a date.
  118. * @param integer|string|DateTime $value the value to be formatted. The following
  119. * types of value are supported:
  120. *
  121. * - an integer representing a UNIX timestamp
  122. * - a string that can be parsed into a UNIX timestamp via `strtotime()`
  123. * - a PHP DateTime object
  124. *
  125. * @param string $format the format used to convert the value into a date string.
  126. * If null, [[dateFormat]] will be used.
  127. *
  128. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  129. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  130. *
  131. * @return string the formatted result
  132. * @throws InvalidConfigException when formatting fails due to invalid parameters.
  133. * @see dateFormat
  134. */
  135. public function asDate($value, $format = null)
  136. {
  137. if ($value === null) {
  138. return $this->nullDisplay;
  139. }
  140. $value = $this->normalizeDatetimeValue($value);
  141. if ($format === null) {
  142. $format = $this->dateFormat;
  143. }
  144. if (isset($this->_dateFormats[$format])) {
  145. $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, $this->timeZone);
  146. } else {
  147. $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
  148. if ($formatter !== null) {
  149. $formatter->setPattern($format);
  150. }
  151. }
  152. if ($formatter === null) {
  153. throw new InvalidConfigException(intl_get_error_message());
  154. }
  155. return $formatter->format($value);
  156. }
  157. /**
  158. * Formats the value as a time.
  159. * @param integer|string|DateTime $value the value to be formatted. The following
  160. * types of value are supported:
  161. *
  162. * - an integer representing a UNIX timestamp
  163. * - a string that can be parsed into a UNIX timestamp via `strtotime()`
  164. * - a PHP DateTime object
  165. *
  166. * @param string $format the format used to convert the value into a date string.
  167. * If null, [[dateFormat]] will be used.
  168. *
  169. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  170. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  171. *
  172. * @return string the formatted result
  173. * @throws InvalidConfigException when formatting fails due to invalid parameters.
  174. * @see timeFormat
  175. */
  176. public function asTime($value, $format = null)
  177. {
  178. if ($value === null) {
  179. return $this->nullDisplay;
  180. }
  181. $value = $this->normalizeDatetimeValue($value);
  182. if ($format === null) {
  183. $format = $this->timeFormat;
  184. }
  185. if (isset($this->_dateFormats[$format])) {
  186. $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format], $this->timeZone);
  187. } else {
  188. $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
  189. if ($formatter !== null) {
  190. $formatter->setPattern($format);
  191. }
  192. }
  193. if ($formatter === null) {
  194. throw new InvalidConfigException(intl_get_error_message());
  195. }
  196. return $formatter->format($value);
  197. }
  198. /**
  199. * Formats the value as a datetime.
  200. * @param integer|string|DateTime $value the value to be formatted. The following
  201. * types of value are supported:
  202. *
  203. * - an integer representing a UNIX timestamp
  204. * - a string that can be parsed into a UNIX timestamp via `strtotime()`
  205. * - a PHP DateTime object
  206. *
  207. * @param string $format the format used to convert the value into a date string.
  208. * If null, [[dateFormat]] will be used.
  209. *
  210. * This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
  211. * It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
  212. *
  213. * @return string the formatted result
  214. * @throws InvalidConfigException when formatting fails due to invalid parameters.
  215. * @see datetimeFormat
  216. */
  217. public function asDatetime($value, $format = null)
  218. {
  219. if ($value === null) {
  220. return $this->nullDisplay;
  221. }
  222. $value = $this->normalizeDatetimeValue($value);
  223. if ($format === null) {
  224. $format = $this->datetimeFormat;
  225. }
  226. if (isset($this->_dateFormats[$format])) {
  227. $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format], $this->timeZone);
  228. } else {
  229. $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone);
  230. if ($formatter !== null) {
  231. $formatter->setPattern($format);
  232. }
  233. }
  234. if ($formatter === null) {
  235. throw new InvalidConfigException(intl_get_error_message());
  236. }
  237. return $formatter->format($value);
  238. }
  239. /**
  240. * Formats the value as a decimal number.
  241. * @param mixed $value the value to be formatted
  242. * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
  243. * for details on how to specify a format.
  244. * @return string the formatted result.
  245. */
  246. public function asDecimal($value, $format = null)
  247. {
  248. if ($value === null) {
  249. return $this->nullDisplay;
  250. }
  251. return $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value);
  252. }
  253. /**
  254. * Formats the value as a currency number.
  255. * @param mixed $value the value to be formatted
  256. * @param string $currency the 3-letter ISO 4217 currency code indicating the currency to use.
  257. * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
  258. * for details on how to specify a format.
  259. * @return string the formatted result.
  260. */
  261. public function asCurrency($value, $currency = 'USD', $format = null)
  262. {
  263. if ($value === null) {
  264. return $this->nullDisplay;
  265. }
  266. return $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency);
  267. }
  268. /**
  269. * Formats the value as a percent number.
  270. * @param mixed $value the value to be formatted
  271. * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
  272. * for details on how to specify a format.
  273. * @return string the formatted result.
  274. */
  275. public function asPercent($value, $format = null)
  276. {
  277. if ($value === null) {
  278. return $this->nullDisplay;
  279. }
  280. return $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value);
  281. }
  282. /**
  283. * Formats the value as a scientific number.
  284. * @param mixed $value the value to be formatted
  285. * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
  286. * for details on how to specify a format.
  287. * @return string the formatted result.
  288. */
  289. public function asScientific($value, $format = null)
  290. {
  291. if ($value === null) {
  292. return $this->nullDisplay;
  293. }
  294. return $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value);
  295. }
  296. /**
  297. * Creates a number formatter based on the given type and format.
  298. * @param integer $type the type of the number formatter
  299. * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
  300. * @return NumberFormatter the created formatter instance
  301. */
  302. protected function createNumberFormatter($type, $format)
  303. {
  304. $formatter = new NumberFormatter($this->locale, $type);
  305. if ($format !== null) {
  306. $formatter->setPattern($format);
  307. }
  308. if (!empty($this->numberFormatOptions)) {
  309. foreach ($this->numberFormatOptions as $name => $attribute) {
  310. $formatter->setAttribute($name, $attribute);
  311. }
  312. }
  313. return $formatter;
  314. }
  315. }