Fix time format for duration was incorrect
When using format `[h]:mm` it should convert to the "total hours:minutes" Closes #666 Fixes #664 Fixes #446 Fixes #342
This commit is contained in:
parent
39b573b29d
commit
699da09176
|
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
- Improved performance when loading large spreadsheets - [#824](https://github.com/PHPOffice/PhpSpreadsheet/pull/824)
|
- Improved performance when loading large spreadsheets - [#824](https://github.com/PHPOffice/PhpSpreadsheet/pull/824)
|
||||||
- Fix color from CSS when reading from HTML - [#831](https://github.com/PHPOffice/PhpSpreadsheet/pull/831)
|
- Fix color from CSS when reading from HTML - [#831](https://github.com/PHPOffice/PhpSpreadsheet/pull/831)
|
||||||
- Fix infinite loop when reading invalid ODS files - [#832](https://github.com/PHPOffice/PhpSpreadsheet/pull/832)
|
- Fix infinite loop when reading invalid ODS files - [#832](https://github.com/PHPOffice/PhpSpreadsheet/pull/832)
|
||||||
|
- Fix time format for duration is incorrect - [#666](https://github.com/PHPOffice/PhpSpreadsheet/pull/666)
|
||||||
|
|
||||||
## [1.5.2] - 2018-11-25
|
## [1.5.2] - 2018-11-25
|
||||||
|
|
||||||
|
|
|
@ -424,9 +424,9 @@ class NumberFormat extends Supervisor
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $dateFormatReplacements24 = [
|
private static $dateFormatReplacements24 = [
|
||||||
'hh' => 'H',
|
'hh' => 'H',
|
||||||
'h' => 'G',
|
'h' => 'G',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
|
* Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock).
|
||||||
|
@ -434,9 +434,9 @@ class NumberFormat extends Supervisor
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $dateFormatReplacements12 = [
|
private static $dateFormatReplacements12 = [
|
||||||
'hh' => 'h',
|
'hh' => 'h',
|
||||||
'h' => 'g',
|
'h' => 'g',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static function setLowercaseCallback($matches)
|
private static function setLowercaseCallback($matches)
|
||||||
{
|
{
|
||||||
|
@ -467,6 +467,13 @@ class NumberFormat extends Supervisor
|
||||||
$block = strtr($block, self::$dateFormatReplacements);
|
$block = strtr($block, self::$dateFormatReplacements);
|
||||||
if (!strpos($block, 'A')) {
|
if (!strpos($block, 'A')) {
|
||||||
// 24-hour time format
|
// 24-hour time format
|
||||||
|
// when [h]:mm format, the [h] should replace to the hours of the value * 24
|
||||||
|
if (false !== strpos($block, '[h]')) {
|
||||||
|
$hours = (int) ($value * 24);
|
||||||
|
$block = str_replace('[h]', $hours, $block);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$block = strtr($block, self::$dateFormatReplacements24);
|
$block = strtr($block, self::$dateFormatReplacements24);
|
||||||
} else {
|
} else {
|
||||||
// 12-hour time format
|
// 12-hour time format
|
||||||
|
@ -636,104 +643,105 @@ class NumberFormat extends Supervisor
|
||||||
// Save format with color information for later use below
|
// Save format with color information for later use below
|
||||||
$formatColor = $format;
|
$formatColor = $format;
|
||||||
|
|
||||||
// Strip color information
|
|
||||||
$color_regex = '/^\\[[a-zA-Z]+\\]/';
|
|
||||||
$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
|
||||||
|
|
||||||
// Check for date/time characters (not inside quotes)
|
// Check for date/time characters (not inside quotes)
|
||||||
if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
|
if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
|
||||||
// datetime format
|
// datetime format
|
||||||
self::formatAsDate($value, $format);
|
self::formatAsDate($value, $format);
|
||||||
} elseif (preg_match('/%$/', $format)) {
|
|
||||||
// % number format
|
|
||||||
self::formatAsPercentage($value, $format);
|
|
||||||
} else {
|
} else {
|
||||||
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
// Strip color information
|
||||||
$value = 'EUR ' . sprintf('%1.2f', $value);
|
$color_regex = '/^\\[[a-zA-Z]+\\]/';
|
||||||
|
$format = preg_replace($color_regex, '', $format);
|
||||||
|
if (preg_match('/%$/', $format)) {
|
||||||
|
// % number format
|
||||||
|
self::formatAsPercentage($value, $format);
|
||||||
} else {
|
} else {
|
||||||
// Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
|
if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
|
||||||
$format = str_replace(['"', '*'], '', $format);
|
$value = 'EUR ' . sprintf('%1.2f', $value);
|
||||||
|
|
||||||
// Find out if we need thousands separator
|
|
||||||
// This is indicated by a comma enclosed by a digit placeholder:
|
|
||||||
// #,# or 0,0
|
|
||||||
$useThousands = preg_match('/(#,#|0,0)/', $format);
|
|
||||||
if ($useThousands) {
|
|
||||||
$format = preg_replace('/0,0/', '00', $format);
|
|
||||||
$format = preg_replace('/#,#/', '##', $format);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale thousands, millions,...
|
|
||||||
// This is indicated by a number of commas after a digit placeholder:
|
|
||||||
// #, or 0.0,,
|
|
||||||
$scale = 1; // same as no scale
|
|
||||||
$matches = [];
|
|
||||||
if (preg_match('/(#|0)(,+)/', $format, $matches)) {
|
|
||||||
$scale = pow(1000, strlen($matches[2]));
|
|
||||||
|
|
||||||
// strip the commas
|
|
||||||
$format = preg_replace('/0,+/', '0', $format);
|
|
||||||
$format = preg_replace('/#,+/', '#', $format);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preg_match('/#?.*\?\/\?/', $format, $m)) {
|
|
||||||
if ($value != (int) $value) {
|
|
||||||
self::formatAsFraction($value, $format);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Handle the number itself
|
// Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
|
||||||
|
$format = str_replace(['"', '*'], '', $format);
|
||||||
|
|
||||||
// scale number
|
// Find out if we need thousands separator
|
||||||
$value = $value / $scale;
|
// This is indicated by a comma enclosed by a digit placeholder:
|
||||||
|
// #,# or 0,0
|
||||||
|
$useThousands = preg_match('/(#,#|0,0)/', $format);
|
||||||
|
if ($useThousands) {
|
||||||
|
$format = preg_replace('/0,0/', '00', $format);
|
||||||
|
$format = preg_replace('/#,#/', '##', $format);
|
||||||
|
}
|
||||||
|
|
||||||
// Strip #
|
// Scale thousands, millions,...
|
||||||
$format = preg_replace('/\\#/', '0', $format);
|
// This is indicated by a number of commas after a digit placeholder:
|
||||||
|
// #, or 0.0,,
|
||||||
|
$scale = 1; // same as no scale
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match('/(#|0)(,+)/', $format, $matches)) {
|
||||||
|
$scale = pow(1000, strlen($matches[2]));
|
||||||
|
|
||||||
// Remove locale code [$-###]
|
// strip the commas
|
||||||
$format = preg_replace('/\[\$\-.*\]/', '', $format);
|
$format = preg_replace('/0,+/', '0', $format);
|
||||||
|
$format = preg_replace('/#,+/', '#', $format);
|
||||||
|
}
|
||||||
|
|
||||||
$n = '/\\[[^\\]]+\\]/';
|
if (preg_match('/#?.*\?\/\?/', $format, $m)) {
|
||||||
$m = preg_replace($n, '', $format);
|
if ($value != (int) $value) {
|
||||||
$number_regex = '/(0+)(\\.?)(0*)/';
|
self::formatAsFraction($value, $format);
|
||||||
if (preg_match($number_regex, $m, $matches)) {
|
}
|
||||||
$left = $matches[1];
|
} else {
|
||||||
$dec = $matches[2];
|
// Handle the number itself
|
||||||
$right = $matches[3];
|
|
||||||
|
|
||||||
// minimun width of formatted number (including dot)
|
// scale number
|
||||||
$minWidth = strlen($left) + strlen($dec) + strlen($right);
|
$value = $value / $scale;
|
||||||
if ($useThousands) {
|
|
||||||
$value = number_format(
|
// Strip #
|
||||||
$value,
|
$format = preg_replace('/\\#/', '0', $format);
|
||||||
strlen($right),
|
|
||||||
StringHelper::getDecimalSeparator(),
|
// Remove locale code [$-###]
|
||||||
StringHelper::getThousandsSeparator()
|
$format = preg_replace('/\[\$\-.*\]/', '', $format);
|
||||||
);
|
|
||||||
$value = preg_replace($number_regex, $value, $format);
|
$n = '/\\[[^\\]]+\\]/';
|
||||||
} else {
|
$m = preg_replace($n, '', $format);
|
||||||
if (preg_match('/[0#]E[+-]0/i', $format)) {
|
$number_regex = '/(0+)(\\.?)(0*)/';
|
||||||
// Scientific format
|
if (preg_match($number_regex, $m, $matches)) {
|
||||||
$value = sprintf('%5.2E', $value);
|
$left = $matches[1];
|
||||||
} elseif (preg_match('/0([^\d\.]+)0/', $format)) {
|
$dec = $matches[2];
|
||||||
$value = self::complexNumberFormatMask($value, $format);
|
$right = $matches[3];
|
||||||
} else {
|
|
||||||
$sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
|
// minimun width of formatted number (including dot)
|
||||||
$value = sprintf($sprintf_pattern, $value);
|
$minWidth = strlen($left) + strlen($dec) + strlen($right);
|
||||||
|
if ($useThousands) {
|
||||||
|
$value = number_format(
|
||||||
|
$value,
|
||||||
|
strlen($right),
|
||||||
|
StringHelper::getDecimalSeparator(),
|
||||||
|
StringHelper::getThousandsSeparator()
|
||||||
|
);
|
||||||
$value = preg_replace($number_regex, $value, $format);
|
$value = preg_replace($number_regex, $value, $format);
|
||||||
|
} else {
|
||||||
|
if (preg_match('/[0#]E[+-]0/i', $format)) {
|
||||||
|
// Scientific format
|
||||||
|
$value = sprintf('%5.2E', $value);
|
||||||
|
} elseif (preg_match('/0([^\d\.]+)0/', $format)) {
|
||||||
|
$value = self::complexNumberFormatMask($value, $format);
|
||||||
|
} else {
|
||||||
|
$sprintf_pattern = "%0$minWidth." . strlen($right) . 'f';
|
||||||
|
$value = sprintf($sprintf_pattern, $value);
|
||||||
|
$value = preg_replace($number_regex, $value, $format);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
||||||
if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
|
// Currency or Accounting
|
||||||
// Currency or Accounting
|
$currencyCode = $m[1];
|
||||||
$currencyCode = $m[1];
|
list($currencyCode) = explode('-', $currencyCode);
|
||||||
list($currencyCode) = explode('-', $currencyCode);
|
if ($currencyCode == '') {
|
||||||
if ($currencyCode == '') {
|
$currencyCode = StringHelper::getCurrencyCode();
|
||||||
$currencyCode = StringHelper::getCurrencyCode();
|
}
|
||||||
|
$value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
|
||||||
}
|
}
|
||||||
$value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,4 +72,27 @@ return [
|
||||||
43332,
|
43332,
|
||||||
'[$-1010409]m/d/yyyy',
|
'[$-1010409]m/d/yyyy',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'27:15',
|
||||||
|
1.1354166666667,
|
||||||
|
'[h]:mm',
|
||||||
|
],
|
||||||
|
// Percentage
|
||||||
|
[
|
||||||
|
'12%',
|
||||||
|
0.12,
|
||||||
|
'0%',
|
||||||
|
],
|
||||||
|
// Simple color
|
||||||
|
[
|
||||||
|
'12345',
|
||||||
|
12345,
|
||||||
|
'[Green]General',
|
||||||
|
],
|
||||||
|
// Multiple colors
|
||||||
|
[
|
||||||
|
'12345',
|
||||||
|
12345,
|
||||||
|
'[Blue]0;[Red]0',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in New Issue