Skip to content

Commit ef4029d

Browse files
author
MarkBaker
committed
Refactor ISO data type validation from cell to shared date; add extra checks for invalid dates; and appropriate unit tests
1 parent 9cf526a commit ef4029d

File tree

5 files changed

+75
-15
lines changed

5 files changed

+75
-15
lines changed

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Cell;
44

5-
use DateTime;
65
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
76
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
87
use PhpOffice\PhpSpreadsheet\Collection\Cells;
@@ -240,18 +239,7 @@ public function setValueExplicit($value, $dataType)
240239

241240
break;
242241
case DataType::TYPE_ISO_DATE:
243-
if (!is_string($value)) {
244-
throw new Exception('Non-string supplied for datatype Date');
245-
}
246-
$date = new DateTime($value);
247-
$newValue = SharedDate::PHPToExcel($date);
248-
if ($newValue === false) {
249-
throw new Exception("Invalid string $value supplied for datatype Date");
250-
}
251-
if (preg_match('/^\\d\\d:\\d\\d:\\d\\d/', $value) == 1) {
252-
$newValue = fmod($newValue, 1.0);
253-
}
254-
$this->value = $newValue;
242+
$this->value = SharedDate::convertIsoDate($value);
255243
$dataType = DataType::TYPE_NUMERIC;
256244

257245
break;

src/PhpSpreadsheet/Shared/Date.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
1010
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
1111
use PhpOffice\PhpSpreadsheet\Cell\Cell;
12+
use PhpOffice\PhpSpreadsheet\Exception;
1213
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
14+
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
1315
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
1416

1517
class Date
@@ -158,6 +160,36 @@ private static function validateTimeZone($timeZone)
158160
throw new PhpSpreadsheetException('Invalid timezone');
159161
}
160162

163+
/**
164+
* @param mixed $value
165+
*
166+
* @return float|int
167+
*/
168+
public static function convertIsoDate($value)
169+
{
170+
if (!is_string($value)) {
171+
throw new Exception('Non-string value supplied for Iso Date conversion');
172+
}
173+
174+
$date = new DateTime($value);
175+
$dateErrors = DateTime::getLastErrors();
176+
177+
if (is_array($dateErrors) && ($dateErrors['warning_count'] > 0 || $dateErrors['error_count'] > 0)) {
178+
throw new Exception("Invalid string $value supplied for datatype Date");
179+
}
180+
181+
$newValue = SharedDate::PHPToExcel($date);
182+
if ($newValue === false) {
183+
throw new Exception("Invalid string $value supplied for datatype Date");
184+
}
185+
186+
if (preg_match('/^\\d\\d:\\d\\d:\\d\\d/', $value) == 1) {
187+
$newValue = fmod($newValue, 1.0);
188+
}
189+
190+
return $newValue;
191+
}
192+
161193
/**
162194
* Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
163195
*

tests/PhpSpreadsheetTests/Cell/CellTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Cell;
44

5+
use PhpOffice\PhpSpreadsheet\Cell\DataType;
56
use PhpOffice\PhpSpreadsheet\Exception;
67
use PhpOffice\PhpSpreadsheet\Spreadsheet;
78
use PhpOffice\PhpSpreadsheet\Style\Color;
@@ -33,6 +34,19 @@ public function providerSetValueExplicit(): array
3334
return require 'tests/data/Cell/SetValueExplicit.php';
3435
}
3536

37+
public function testInvalidIsoDateSetValueExplicit(): void
38+
{
39+
$spreadsheet = new Spreadsheet();
40+
$cell = $spreadsheet->getActiveSheet()->getCell('A1');
41+
42+
$dateValue = '2022-02-29'; // Invalid leap year
43+
$this->expectException(Exception::class);
44+
$this->expectExceptionMessage("Invalid string {$dateValue} supplied for datatype Date");
45+
$cell->setValueExplicit($dateValue, DataType::TYPE_ISO_DATE);
46+
47+
$spreadsheet->disconnectWorksheets();
48+
}
49+
3650
/**
3751
* @dataProvider providerSetValueExplicitException
3852
*

tests/PhpSpreadsheetTests/Shared/DateTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PhpOffice\PhpSpreadsheetTests\Shared;
44

55
use DateTimeZone;
6+
use PhpOffice\PhpSpreadsheet\Exception;
67
use PhpOffice\PhpSpreadsheet\Shared\Date;
78
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
89
use PHPUnit\Framework\TestCase;
@@ -209,6 +210,15 @@ public function providerDateTimeExcelToTimestamp1900Timezone(): array
209210
return require 'tests/data/Shared/Date/ExcelToTimestamp1900Timezone.php';
210211
}
211212

213+
public function testConvertIsoDateError(): void
214+
{
215+
Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
216+
217+
$this->expectException(Exception::class);
218+
$this->expectExceptionMessage('Non-string value supplied for Iso Date conversion');
219+
Date::convertIsoDate(false);
220+
}
221+
212222
public function testVarious(): void
213223
{
214224
Date::setDefaultTimeZone('UTC');

tests/data/Cell/SetValueExplicit.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
34
use PhpOffice\PhpSpreadsheet\Cell\DataType;
45

56
return [
@@ -39,12 +40,27 @@
3940
DataType::TYPE_NUMERIC,
4041
],
4142
[
42-
'#DIV/0!',
43+
44613.43090277778,
44+
'2022-02-21 10:20:30',
45+
DataType::TYPE_ISO_DATE,
46+
],
47+
[
48+
44613.0,
49+
'2022-02-21',
50+
DataType::TYPE_ISO_DATE,
51+
],
52+
[
53+
-30879.0,
54+
'1815-06-15', // Dates outside the Excel Range should fail really
55+
DataType::TYPE_ISO_DATE,
56+
],
57+
[
58+
ExcelError::DIV0(),
4359
'#DIV/0!',
4460
DataType::TYPE_ERROR,
4561
],
4662
[
47-
'#NULL!',
63+
ExcelError::null(),
4864
'NOT A VALID ERROR TYPE VALUE',
4965
DataType::TYPE_ERROR,
5066
],

0 commit comments

Comments
 (0)