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'
|
'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)
|
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]
|
// strip off first part containing e.g. [$-F800] or [$USD-409]
|
||||||
// general syntax: [$<Currency string>-<language info>]
|
// general syntax: [$<Currency string>-<language info>]
|
||||||
// language info is in hexadecimal
|
// language info is in hexadecimal
|
||||||
$format = preg_replace('/^(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
|
$format = preg_replace('/^(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
|
||||||
|
|
||||||
// OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case
|
// OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
|
||||||
$format = strtolower($format);
|
// but we don't want to change any quoted strings
|
||||||
|
$format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', ['self', 'setLowercaseCallback'], $format);
|
||||||
|
|
||||||
$format = strtr($format, self::$dateFormatReplacements);
|
// Only process the non-quoted blocks for date format characters
|
||||||
if (!strpos($format, 'A')) {
|
$blocks = explode('"', $format);
|
||||||
// 24-hour time format
|
foreach($blocks as $key => &$block) {
|
||||||
$format = strtr($format, self::$dateFormatReplacements24);
|
if ($key % 2 == 0) {
|
||||||
} else {
|
$block = strtr($block, self::$dateFormatReplacements);
|
||||||
// 12-hour time format
|
if (!strpos($block, 'A')) {
|
||||||
$format = strtr($format, self::$dateFormatReplacements12);
|
// 24-hour time format
|
||||||
|
$block = strtr($block, self::$dateFormatReplacements24);
|
||||||
|
} else {
|
||||||
|
// 12-hour time format
|
||||||
|
$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);
|
$dateObj = \PHPExcel\Shared\Date::ExcelToPHPObject($value);
|
||||||
$value = $dateObj->format($format);
|
$value = $dateObj->format($format);
|
||||||
|
@ -553,10 +570,12 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the sections, there can be up to four sections
|
// Convert any escaped characters to quoted strings, e.g. (\T to "T")
|
||||||
$sections = explode(';', $format);
|
$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.
|
// Text not supported yet.
|
||||||
// Here is how the sections apply to various values in Excel:
|
// Here is how the sections apply to various values in Excel:
|
||||||
// 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT]
|
// 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT]
|
||||||
|
@ -597,9 +616,13 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
||||||
$format = preg_replace($color_regex, '', $format);
|
$format = preg_replace($color_regex, '', $format);
|
||||||
|
|
||||||
// Let's begin inspecting the format and converting the value to a formatted string
|
// 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);
|
self::formatAsDate($value, $format);
|
||||||
} elseif (preg_match('/%$/', $format)) { // % number format
|
} elseif (preg_match('/%$/', $format)) {
|
||||||
|
// % number format
|
||||||
self::formatAsPercentage($value, $format);
|
self::formatAsPercentage($value, $format);
|
||||||
} else {
|
} else {
|
||||||
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
||||||
|
@ -687,7 +710,7 @@ class NumberFormat extends Supervisor implements \PHPExcel\IComparable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
||||||
// Currency or Accounting
|
// Currency or Accounting
|
||||||
$currencyFormat = $m[0];
|
$currencyFormat = $m[0];
|
||||||
$currencyCode = $m[1];
|
$currencyCode = $m[1];
|
||||||
list($currencyCode) = explode('-', $currencyCode);
|
list($currencyCode) = explode('-', $currencyCode);
|
||||||
|
|
|
@ -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