Permalink
Browse files

* (Fixes issue 402) CNumberFormatter does not format decimals and per…

…centages correctly
  • Loading branch information...
1 parent 0f56ddc commit de4ee96ad0e97c754d51356c4fe0777e1be87544 qiang.xue committed Jun 28, 2009
Showing with 116 additions and 19 deletions.
  1. +1 −0 CHANGELOG
  2. +45 −19 framework/i18n/CNumberFormatter.php
  3. +70 −0 tests/unit/framework/i18n/CNumberFormatterTest.php
View
@@ -7,6 +7,7 @@ Version 1.0.7 to be released
- Bug #372: CCacheHttpSession should initialize cache first before using it (Qiang)
- Bug #388: 'params' options passed to linkButton are not cleared after submit (Qiang)
- Bug #393: Greek language code should be 'el' insead of 'gr' (Qiang)
+- Bug #402: CNumberFormatter does not format decimals and percentages correctly (Qiang)
- Bug #404: AR would fail when CDbLogRoute uses the same DB connection (Qiang)
- Bug: CMemCache has a typo when using memcached (Qiang)
- Bug: COciCommandBuilder is referencing undefined variable (Qiang)
@@ -138,8 +138,9 @@ public function formatDecimal($value)
* @param array format with the following structure:
* <pre>
* array(
- * 'decimalDigits'=>2, // number of required digits after decimal point; if -1, it means we should drop decimal point
- * 'integerDigits'=>1, // number of required digits before decimal point
+ * 'decimalDigits'=>2, // number of required digits after decimal point; 0s will be padded if not enough digits; if -1, it means we should drop decimal point
+ * 'maxDecimalDigits'=>3, // maximum number of digits after decimal point. Additional digits will be truncated.
+ * 'integerDigits'=>1, // number of required digits before decimal point; 0s will be padded if not enough digits
* 'groupSize1'=>3, // the primary grouping size; if 0, it means no grouping
* 'groupSize2'=>0, // the secondary grouping size; if 0, it means no secondary grouping
* 'positivePrefix'=>'+', // prefix to positive number
@@ -156,17 +157,24 @@ protected function formatNumber($format,$value)
{
$negative=$value<0;
$value=abs($value*$format['multiplier']);
- if($format['decimalDigits']>=0)
- $value=round($value,$format['decimalDigits']);
- list($integer,$decimal)=explode('.',sprintf('%F',$value));
-
- if($format['decimalDigits']>=0)
+ if($format['maxDecimalDigits']>=0)
+ $value=round($value,$format['maxDecimalDigits']);
+ $value="$value";
+ if(($pos=strpos($value,'.'))!==false)
{
- $decimal=rtrim(substr($decimal,0,$format['decimalDigits']),'0');
- $decimal=$this->_locale->getNumberSymbol('decimal').str_pad($decimal,$format['decimalDigits'],'0');
+ $integer=substr($value,0,$pos);
+ $decimal=substr($value,$pos+1);
}
else
+ {
+ $integer=$value;
$decimal='';
+ }
+
+ if($format['decimalDigits']>strlen($decimal))
+ $decimal=str_pad($decimal,$format['decimalDigits'],'0');
+ if(strlen($decimal)>0)
+ $decimal=$this->_locale->getNumberSymbol('decimal').$decimal;
$integer=str_pad($integer,$format['integerDigits'],'0',STR_PAD_LEFT);
if($format['groupSize1']>0 && strlen($integer)>$format['groupSize1'])
@@ -202,13 +210,22 @@ protected function parseFormat($pattern)
// find out prefix and suffix for positive and negative patterns
$patterns=explode(';',$pattern);
- list($format['positivePrefix'],$format['positiveSuffix'])=preg_split('/[#,\.0]+/',$patterns[0]);
- if(isset($patterns[1])) // with a negative pattern
- list($format['negativePrefix'],$format['negativeSuffix'])=preg_split('/[#,\.0]+/',$patterns[1]);
+ $format['positivePrefix']=$format['positiveSuffix']=$format['negativePrefix']=$format['negativeSuffix']='';
+ if(preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[0],$matches))
+ {
+ $format['positivePrefix']=$matches[1];
+ $format['positiveSuffix']=$matches[2];
+ }
+
+ if(isset($patterns[1]) && preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[1],$matches)) // with a negative pattern
+ {
+ $format['negativePrefix']=$matches[1];
+ $format['negativeSuffix']=$matches[2];
+ }
else
{
- $format['negativePrefix']=$this->_locale->getNumberSymbol('minusSign');
- $format['negativeSuffix']='';
+ $format['negativePrefix']=$this->_locale->getNumberSymbol('minusSign').$format['positivePrefix'];
+ $format['negativeSuffix']=$format['positiveSuffix'];
}
$pattern=$patterns[0];
@@ -227,21 +244,30 @@ protected function parseFormat($pattern)
$format['decimalDigits']=$pos2-$pos;
else
$format['decimalDigits']=0;
+ if(($pos3=strrpos($pattern,'#'))>=$pos2)
+ $format['maxDecimalDigits']=$pos3-$pos;
+ else
+ $format['maxDecimalDigits']=$format['decimalDigits'];
$pattern=substr($pattern,0,$pos);
}
else // no decimal part
- $format['decimalDigits']=-1; // do not display decimal point
+ {
+ $format['decimalDigits']=0;
+ $format['maxDecimalDigits']=0;
+ }
// find out things about integer part
- if(($pos=strpos($pattern,'0'))!==false)
- $format['integerDigits']=strlen(str_replace(',','',substr($pattern,$pos)));
+ $p=str_replace(',','',$pattern);
+ if(($pos=strpos($p,'0'))!==false)
+ $format['integerDigits']=strrpos($p,'0')-$pos+1;
else
$format['integerDigits']=0;
// find out group sizes. some patterns may have two different group sizes
+ $p=str_replace('#','0',$pattern);
if(($pos=strrpos($pattern,','))!==false)
{
- $format['groupSize1']=strlen($pattern)-$pos-1;
- if(($pos2=strrpos(substr($pattern,0,$pos),','))!==false)
+ $format['groupSize1']=strrpos($p,'0')-$pos;
+ if(($pos2=strrpos(substr($p,0,$pos),','))!==false)
$format['groupSize2']=$pos-$pos2-1;
else
$format['groupSize2']=0;
@@ -0,0 +1,70 @@
+<?php
+
+class CNumberFormatterTest extends CTestCase
+{
+ public $usFormatter;
+ public $deFormatter;
+
+ public function setUp()
+ {
+ $this->usFormatter=new CNumberFormatter('en_us');
+ $this->deFormatter=new CNumberFormatter('de');
+ }
+
+ public function testFormatCurrency()
+ {
+ $numbers=array(
+ array(0, '$0.00', '0,00 $'),
+ array(100, '$100.00', '100,00 $'),
+ array(-100, '($100.00)', '−100,00 $'),
+ array(100.123, '$100.12', '100,12 $'),
+ array(100.1, '$100.10', '100,10 $'),
+ array(100.126, '$100.13', '100,13 $'),
+ array(1000.126, '$1,000.13', '1.000,13 $'),
+ array(1000000.123, '$1,000,000.12', '1.000.000,12 $'),
+ );
+
+ foreach($numbers as $number)
+ {
+ $this->assertEquals($number[1],$this->usFormatter->formatCurrency($number[0],'USD'));
+ $this->assertEquals($number[2],$this->deFormatter->formatCurrency($number[0],'USD'));
+ }
+ }
+
+ public function testFormatDecimal()
+ {
+ $numbers=array(
+ array(0, '0', '0'),
+ array(100, '100', '100'),
+ array(-100, '-100', '−100'),
+ array(100.123, '100.123', '100,123'),
+ array(100.1, '100.1', '100,1'),
+ array(100.1206, '100.121', '100,121'),
+ array(1000.1206, '1,000.121', '1.000,121'),
+ array(1000000.123, '1,000,000.123', '1.000.000,123'),
+ );
+
+ foreach($numbers as $number)
+ {
+ $this->assertEquals($number[1],$this->usFormatter->formatDecimal($number[0]));
+ $this->assertEquals($number[2],$this->deFormatter->formatDecimal($number[0]));
+ }
+ }
+
+ public function testFormatPercentage()
+ {
+ $numbers=array(
+ array(0, '0%', '0 %'),
+ array(0.123, '12%', '12 %'),
+ array(-0.123, '-12%', '−12 %'),
+ array(10.12, '1,012%', '1.012 %'),
+ array(10000.1, '1,000,010%', '1.000.010 %'),
+ );
+
+ foreach($numbers as $number)
+ {
+ $this->assertEquals($number[1],$this->usFormatter->formatPercentage($number[0]));
+ $this->assertEquals($number[2],$this->deFormatter->formatPercentage($number[0]));
+ }
+ }
+}

0 comments on commit de4ee96

Please sign in to comment.