Skip to content

Commit 5c6f2d0

Browse files
committed
Micro-optimization for excelToDateTimeObject
Fix #4438. Do some optimization if Excel value is an integer. This is unlikely to make much of a difference, but the use case seems pretty common (cell represents a date rather than date-time), so we may as well do it. There had been no tests for negative integer values, because Excel does not handle those well, but OpenOffice and Gnumeric handle them just fine, so add some tests for them.
1 parent 86cca12 commit 5c6f2d0

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

src/PhpSpreadsheet/Shared/Date.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ public static function excelToDateTimeObject(float|int $excelTimestamp, null|Dat
214214
$baseDate = new DateTime('1899-12-30', $timeZone);
215215
}
216216

217+
if (is_int($excelTimestamp)) {
218+
if ($excelTimestamp >= 0) {
219+
return $baseDate->modify("+ $excelTimestamp days");
220+
}
221+
222+
return $baseDate->modify("$excelTimestamp days");
223+
}
217224
$days = floor($excelTimestamp);
218225
$partDay = $excelTimestamp - $days;
219226
$hms = 86400 * $partDay;

tests/PhpSpreadsheetTests/Style/NumberFormatTest.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,36 @@
44

55
namespace PhpOffice\PhpSpreadsheetTests\Style;
66

7+
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
78
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
89
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
910
use PhpOffice\PhpSpreadsheet\Style\NumberFormat\NumberFormatter;
11+
use PHPUnit\Framework\Attributes\DataProvider;
1012
use PHPUnit\Framework\TestCase;
1113

1214
class NumberFormatTest extends TestCase
1315
{
16+
private string $compatibilityMode;
17+
1418
protected function setUp(): void
1519
{
1620
StringHelper::setDecimalSeparator('.');
1721
StringHelper::setThousandsSeparator(',');
22+
$this->compatibilityMode = Functions::getCompatibilityMode();
1823
}
1924

2025
protected function tearDown(): void
2126
{
2227
StringHelper::setCurrencyCode(null);
2328
StringHelper::setDecimalSeparator(null);
2429
StringHelper::setThousandsSeparator(null);
30+
Functions::setCompatibilityMode($this->compatibilityMode);
2531
}
2632

2733
/**
2834
* @param null|bool|float|int|string $args string to be formatted
2935
*/
30-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNumberFormat')]
36+
#[DataProvider('providerNumberFormat')]
3137
public function testFormatValueWithMask(mixed $expectedResult, mixed ...$args): void
3238
{
3339
$result = NumberFormat::toFormattedString(...$args);
@@ -42,7 +48,7 @@ public static function providerNumberFormat(): array
4248
/**
4349
* @param null|bool|float|int|string $args string to be formatted
4450
*/
45-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNumberFormatFractions')]
51+
#[DataProvider('providerNumberFormatFractions')]
4652
public function testFormatValueWithMaskFraction(mixed $expectedResult, mixed ...$args): void
4753
{
4854
$result = NumberFormat::toFormattedString(...$args);
@@ -57,7 +63,7 @@ public static function providerNumberFormatFractions(): array
5763
/**
5864
* @param null|bool|float|int|string $args string to be formatted
5965
*/
60-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNumberFormatDates')]
66+
#[DataProvider('providerNumberFormatDates')]
6167
public function testFormatValueWithMaskDate(mixed $expectedResult, mixed ...$args): void
6268
{
6369
$result = NumberFormat::toFormattedString(...$args);
@@ -69,6 +75,23 @@ public static function providerNumberFormatDates(): array
6975
return require 'tests/data/Style/NumberFormatDates.php';
7076
}
7177

78+
public function testDatesOpenOfficeGnumericNonPositive(): void
79+
{
80+
Functions::setCompatibilityMode(
81+
Functions::COMPATIBILITY_OPENOFFICE
82+
);
83+
$fmt1 = 'yyyy-mm-dd';
84+
$rslt = NumberFormat::toFormattedString(0, $fmt1);
85+
self::assertSame('1899-12-30', $rslt);
86+
$rslt = NumberFormat::toFormattedString(-2, $fmt1);
87+
self::assertSame('1899-12-28', $rslt);
88+
$rslt = NumberFormat::toFormattedString(-2.4, $fmt1);
89+
self::assertSame('1899-12-27', $rslt);
90+
$fmt2 = 'yyyy-mm-dd hh:mm:ss AM/PM';
91+
$rslt = NumberFormat::toFormattedString(-2.4, $fmt2);
92+
self::assertSame('1899-12-27 02:24:00 PM', $rslt);
93+
}
94+
7295
public function testCurrencyCode(): void
7396
{
7497
// "Currency symbol" replaces $ in some cases, not in others
@@ -83,7 +106,7 @@ public function testCurrencyCode(): void
83106
StringHelper::setCurrencyCode($cur);
84107
}
85108

86-
#[\PHPUnit\Framework\Attributes\DataProvider('providerNoScientific')]
109+
#[DataProvider('providerNoScientific')]
87110
public function testNoScientific(string $expectedResult, string $numericString): void
88111
{
89112
$result = NumberFormatter::floatStringConvertScientific($numericString);

0 commit comments

Comments
 (0)