Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/**
4
 * NumberFormat class file.
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the BSD License.
8
 *
9
 * Copyright(c) 2004 by Qiang Xue. All rights reserved.
10
 *
11
 * To contact the author write to {@link mailto:qiang.xue@gmail.com Qiang Xue}
12
 * The latest version of PRADO can be obtained from:
13
 * {@link http://prado.sourceforge.net/}
14
 *
15
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
16
 * @version $Revision: 1.6 $  $Date: 2005/12/20 09:32:42 $
17
 * @package System.I18N.core
18
 */
19
 
20
/**
21
 * Get the NumberFormatInfo class file.
22
 */
23
require_once(dirname(__FILE__).'/NumberFormatInfo.php');
24
 
25
 
26
/**
27
 * Get the encoding utilities
28
 */
29
require_once(dirname(__FILE__).'/util.php');
30
 
31
 
32
/**
33
 * NumberFormat class.
34
 *
35
 * NumberFormat formats decimal numbers in any locale. The decimal
36
 * number is formatted according to a particular pattern. These
37
 * patterns can arise from the NumberFormatInfo object which is
38
 * culturally sensitive. The NumberFormat class can be instantiated in
39
 * many ways. E.g.
40
 *
41
 * <code>
42
 *  //create a invariant number formatter.
43
 *	$formatter = new NumberFormat();
44
 *
45
 *  //create a number format for the french language locale.
46
 *  $fr = new NumberFormat('fr');
47
 *
48
 *  //create a number format base on a NumberFormatInfo instance $numberInfo.
49
 *  $format = new NumberFormat($numberInfo);
50
 * </code>
51
 *
52
 * A normal decimal number can also be displayed as a currency
53
 * or as a percentage. For example
54
 * <code>
55
 * $format->format(1234.5); //Decimal number "1234.5"
56
 * $format->format(1234.5,'c'); //Default currency "$1234.50"
57
 * $format->format(0.25, 'p') //Percent "25%"
58
 * </code>
59
 *
60
 * Currency is formated using the localized currency pattern. For example
61
 * to format the number as Japanese Yen:
62
 * <code>
63
 *  $ja = new NumberFormat('ja_JP');
64
 *
65
 *  //Japanese currency pattern, and using Japanese Yen symbol
66
 *  $ja->format(123.14,'c','JPY'); //�?123 (Yen 123)
67
 * </code>
68
 * For each culture, the symbol for each currency may be different.
69
 *
70
 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
71
 * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004
72
 * @package System.I18N.core
73
 */
74
class NumberFormat
75
{
76
 
77
	/**
78
	 * The DateTimeFormatInfo, containing culture specific patterns and names.
79
 	 * @var DateTimeFormatInfo
80
	 */
81
	protected $formatInfo;
82
 
83
	/**
84
	 * Create a new number format instance. The constructor can be instantiated
85
	 * with a string that represent a culture/locale. Similarly, passing
86
	 * a CultureInfo or NumberFormatInfo instance will instantiated a instance
87
	 * for that particular culture.
88
	 * @param mixed either null, a CultureInfo, a NumberFormatInfo, or string
89
	 * @return NumberFormat
90
	 */
91
	function __construct($formatInfo=null)
92
	{
93
		if($formatInfo === null)
94
			$this->formatInfo = NumberFormatInfo::getInvariantInfo();
95
		else if($formatInfo instanceof CultureInfo)
96
			$this->formatInfo = $formatInfo->NumberFormat;
97
		else if($formatInfo instanceof NumberFormatInfo)
98
			$this->formatInfo = $formatInfo;
99
		else
100
			$this->formatInfo =
101
				NumberFormatInfo::getInstance($formatInfo);
102
	}
103
 
104
	/**
105
	 * For the number for a certain pattern. The valid patterns are
106
	 * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for
107
	 * 3 decimal places.
108
	 * @param mixed the number to format.
109
	 * @param string the format pattern, either, 'c', 'd', 'e', 'p'
110
	 * or a custom pattern. E.g. "#.000" will format the number to
111
	 * 3 decimal places.
112
	 * @param string 3-letter ISO 4217 code. For example, the code
113
	 * "USD" represents the US Dollar and "EUR" represents the Euro currency.
114
	 * @return string formatted number string
115
	 */
116
	function format($number, $pattern='d', $currency='USD', $charset='UTF-8')
117
	{
118
		$this->setPattern($pattern);
119
 
120
		if(strtolower($pattern) == 'p')
121
			$number = $number * 100;
122
 
123
		$string = (string)$number;
124
 
125
		$decimal = $this->formatDecimal($string);
126
		$integer = $this->formatInteger(abs($number));
127
 
128
		if(strlen($decimal)>0)
129
			$result = $integer.$decimal;
130
		else
131
			$result = $integer;
132
 
133
		//get the suffix
134
		if($number >= 0)
135
			$suffix = $this->formatInfo->PositivePattern;
136
		else if($number < 0)
137
			$suffix = $this->formatInfo->NegativePattern;
138
		else
139
			$suffix = array("","");
140
 
141
		//append and prepend suffix
142
		$result = $suffix[0].$result.$suffix[1];
143
 
144
		//replace currency sign
145
		$symbol = @$this->formatInfo->getCurrencySymbol($currency);
146
		if($symbol === null) {
147
			$symbol = $currency;
148
		}
149
 
150
		$result = str_replace('¤',$symbol, $result);
151
 
152
		return I18N_toEncoding($result, $charset);
153
	}
154
 
155
	/**
156
	 * For the integer, perform groupings and string padding.
157
	 * @param string the decimal number in string form.
158
	 * @return string  formatted integer string with grouping
159
	 */
160
	protected function formatInteger($string)
161
	{
162
		$string = (string)$string;
163
 
164
		$decimalDigits = $this->formatInfo->DecimalDigits;
165
		//if not decimal digits, assume 0 decimal points.
166
		if(is_int($decimalDigits) && $decimalDigits > 0)
167
			$string = (string)round(floatval($string),$decimalDigits);
168
		$dp = strpos($string, '.');
169
		if(is_int($dp))
170
			$string = substr($string, 0, $dp);
171
		$integer = '';
172
 
173
		$digitSize = $this->formatInfo->getDigitSize();
174
 
175
		$string = str_pad($string, $digitSize, '0',STR_PAD_LEFT);
176
 
177
		$len = strlen($string);
178
 
179
		$groupSeparator = $this->formatInfo->GroupSeparator;
180
		$groupSize = $this->formatInfo->GroupSizes;
181
 
182
 
183
		$firstGroup = true;
184
		$multiGroup = is_int($groupSize[1]);
185
		$count = 0;
186
 
187
		if(is_int($groupSize[0]))
188
		{
189
			//now for the integer groupings
190
			for($i=0; $i<$len; $i++)
191
			{
192
				$char = $string{$len-$i-1};
193
 
194
				if($multiGroup && $count == 0)
195
				{
196
					if($i != 0 && $i%$groupSize[0] == 0)
197
					{
198
						$integer = $groupSeparator . $integer;
199
						$count++;
200
					}
201
				}
202
				else if($multiGroup && $count >= 1)
203
				{
204
					if($i != 0 && ($i-$groupSize[0])%$groupSize[1] == 0)
205
					{
206
						$integer = $groupSeparator . $integer;
207
						$count++;
208
					}
209
				}
210
				else
211
				{
212
					if($i != 0 && $i%$groupSize[0] == 0)
213
					{
214
						$integer = $groupSeparator . $integer;
215
						$count++;
216
					}
217
				}
218
 
219
				$integer = $char . $integer;
220
			}
221
		}
222
		else
223
			$integer = $string;
224
 
225
		return $integer;
226
	}
227
 
228
	/**
229
	 * Format the decimal places.
230
	 * @param string the decimal number in string form.
231
	 * @return string formatted decimal places.
232
	 */
233
	protected function formatDecimal($string)
234
	{
235
		$dp = strpos($string, '.');
236
		$decimal = '';
237
 
238
		$decimalDigits = $this->formatInfo->DecimalDigits;
239
		$decimalSeparator = $this->formatInfo->DecimalSeparator;
240
 
241
		//do the correct rounding here
242
		//$string = round(floatval($string), $decimalDigits);
243
		if(is_int($dp))
244
		{
245
			if($decimalDigits == -1)
246
			{
247
				$decimal = substr($string, $dp+1);
248
			}
249
			else if(is_int($decimalDigits))
250
			{
251
				$float = round((float)$string, $decimalDigits);
252
				if(strpos((string)$float, '.') === false)
253
				{
254
					$decimal = str_pad($decimal,$decimalDigits,'0');
255
				}
256
				else
257
				{
258
					$decimal = substr($float, strpos($float,'.')+1);
259
					if(strlen($decimal)<$decimalDigits)
260
						$decimal = str_pad($decimal,$decimalDigits,'0');
261
				}
262
			}
263
			else
264
				return $decimal;
265
 
266
			return $decimalSeparator.$decimal;
267
		}
268
		else if ($decimalDigits > 0)
269
			return $decimalSeparator.str_pad($decimal,$decimalDigits,'0');
270
 
271
		return $decimal;
272
	}
273
 
274
	/**
275
	 * Set the pattern to format against. The default patterns
276
	 * are retrieved from the NumberFormatInfo instance.
277
	 * @param string the requested patterns.
278
	 * @return string a number format pattern.
279
	 */
280
	protected function setPattern($pattern)
281
	{
282
		switch($pattern)
283
		{
284
			case 'c':
285
			case 'C':
286
				$this->formatInfo->setPattern(NumberFormatInfo::CURRENCY);
287
				break;
288
			case 'd':
289
			case 'D':
290
				$this->formatInfo->setPattern(NumberFormatInfo::DECIMAL);
291
				break;
292
			case 'e':
293
			case 'E':
294
				$this->formatInfo->setPattern(NumberFormatInfo::SCIENTIFIC);
295
				break;
296
			case 'p':
297
			case 'P':
298
				$this->formatInfo->setPattern(NumberFormatInfo::PERCENTAGE);
299
				break;
300
			default:
301
				$this->formatInfo->setPattern($pattern);
302
				break;
303
		}
304
	}
305
}
306