Improved masking for number format handling, particularly for datetime masks
This commit is contained in:
parent
f968a95ca5
commit
d7ef6810a4
|
@ -425,26 +425,43 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
|||
'h' => 'g'
|
||||
);
|
||||
|
||||
private static function setLowercaseCallback($matches) {
|
||||
return mb_strtolower($matches[0]);
|
||||
}
|
||||
|
||||
private static function escapeQuotesCallback($matches) {
|
||||
return '\\' . implode('\\', str_split($matches[1]));
|
||||
}
|
||||
|
||||
private static function formatAsDate(&$value, &$format)
|
||||
{
|
||||
// dvc: convert Excel formats to PHP date formats
|
||||
|
||||
// strip off first part containing e.g. [$-F800] or [$USD-409]
|
||||
// general syntax: [$<Currency string>-<language info>]
|
||||
// language info is in hexadecimal
|
||||
$format = preg_replace('/^(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
|
||||
|
||||
// OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case
|
||||
$format = strtolower($format);
|
||||
// OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
|
||||
// but we don't want to change any quoted strings
|
||||
$format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', ['self', 'setLowercaseCallback'], $format);
|
||||
|
||||
$format = strtr($format, self::$dateFormatReplacements);
|
||||
if (!strpos($format, 'A')) {
|
||||
// Only process the non-quoted blocks for date format characters
|
||||
$blocks = explode('"', $format);
|
||||
foreach($blocks as $key => &$block) {
|
||||
if ($key % 2 == 0) {
|
||||
$block = strtr($block, self::$dateFormatReplacements);
|
||||
if (!strpos($block, 'A')) {
|
||||
// 24-hour time format
|
||||
$format = strtr($format, self::$dateFormatReplacements24);
|
||||
$block = strtr($block, self::$dateFormatReplacements24);
|
||||
} else {
|
||||
// 12-hour time format
|
||||
$format = strtr($format, self::$dateFormatReplacements12);
|
||||
$block = strtr($block, self::$dateFormatReplacements12);
|
||||
}
|
||||
}
|
||||
}
|
||||
$format = implode('"', $blocks);
|
||||
|
||||
// escape any quoted characters so that DateTime format() will render them correctly
|
||||
$format = preg_replace_callback('/"(.*)"/U', ['self', 'escapeQuotesCallback'], $format);
|
||||
|
||||
$dateObj = \PHPExcel\Shared\Date::ExcelToPHPObject($value);
|
||||
$value = $dateObj->format($format);
|
||||
|
@ -553,10 +570,12 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
|||
return $value;
|
||||
}
|
||||
|
||||
// Get the sections, there can be up to four sections
|
||||
$sections = explode(';', $format);
|
||||
// Convert any escaped characters to quoted strings, e.g. (\T to "T")
|
||||
$format = preg_replace('/(\\\(.))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
|
||||
// Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
|
||||
$sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
|
||||
|
||||
// Fetch the relevant section depending on whether number is positive, negative, or zero?
|
||||
// Extract the relevant section depending on whether number is positive, negative, or zero?
|
||||
// Text not supported yet.
|
||||
// Here is how the sections apply to various values in Excel:
|
||||
// 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT]
|
||||
|
@ -597,9 +616,13 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
|||
$format = preg_replace($color_regex, '', $format);
|
||||
|
||||
// Let's begin inspecting the format and converting the value to a formatted string
|
||||
if (preg_match('/^(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy]/i', $format)) { // datetime format
|
||||
|
||||
// Check for date/time characters (not inside quotes)
|
||||
if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
|
||||
// datetime format
|
||||
self::formatAsDate($value, $format);
|
||||
} elseif (preg_match('/%$/', $format)) { // % number format
|
||||
} elseif (preg_match('/%$/', $format)) {
|
||||
// % number format
|
||||
self::formatAsPercentage($value, $format);
|
||||
} else {
|
||||
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
|
||||
require_once 'testDataFileIterator.php';
|
||||
|
||||
class NumberFormatDateTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
if (!defined('PHPEXCEL_ROOT')) {
|
||||
define('PHPEXCEL_ROOT', APPLICATION_PATH . '/');
|
||||
}
|
||||
require_once(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
|
||||
|
||||
PHPExcel_Shared_String::setDecimalSeparator('.');
|
||||
PHPExcel_Shared_String::setThousandsSeparator(',');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerNumberFormat
|
||||
*/
|
||||
public function testFormatValueWithMask()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$expectedResult = array_pop($args);
|
||||
$result = call_user_func_array(array('PHPExcel_Style_NumberFormat','toFormattedString'), $args);
|
||||
$this->assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function providerNumberFormat()
|
||||
{
|
||||
return new testDataFileIterator('rawTestData/Style/NumberFormatDates.data');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
22269.0625, 'dd-mm-yyyy hh:mm:ss', "19-12-1960 01:30:00"
|
||||
22269.0625, 'MM/DD/YYYY HH:MM:SS', "12/19/1960 01:30:00" // Oasis uses upper-case
|
||||
22269.0625, 'yyyy-mm-dd\Thh:mm:ss', "1960-12-19T01:30:00" // Date with plaintext escaped with a \
|
||||
22269.0625, 'yyyy-mm-dd"T"hh:mm:ss \Z', "1960-12-19T01:30:00 Z" // Date with plaintext in quotes
|
||||
22269.0625, '"y-m-d" yyyy-mm-dd "h:m:s" hh:mm:ss', "y-m-d 1960-12-19 h:m:s 01:30:00" // Date with quoted formatting characters
|
||||
22269.0625, '"y-m-d "yyyy-mm-dd" h:m:s "hh:mm:ss', "y-m-d 1960-12-19 h:m:s 01:30:00" // Date with quoted formatting characters
|
Loading…
Reference in New Issue