Refactor ISO data type validation from cell to shared date; add extra checks for invalid dates; and appropriate unit tests

This commit is contained in:
MarkBaker 2022-02-26 13:43:35 +01:00
parent 9cf526a920
commit ef4029df63
5 changed files with 75 additions and 15 deletions

View File

@ -2,7 +2,6 @@
namespace PhpOffice\PhpSpreadsheet\Cell; namespace PhpOffice\PhpSpreadsheet\Cell;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Collection\Cells; use PhpOffice\PhpSpreadsheet\Collection\Cells;
@ -240,18 +239,7 @@ class Cell
break; break;
case DataType::TYPE_ISO_DATE: case DataType::TYPE_ISO_DATE:
if (!is_string($value)) { $this->value = SharedDate::convertIsoDate($value);
throw new Exception('Non-string supplied for datatype Date');
}
$date = new DateTime($value);
$newValue = SharedDate::PHPToExcel($date);
if ($newValue === false) {
throw new Exception("Invalid string $value supplied for datatype Date");
}
if (preg_match('/^\\d\\d:\\d\\d:\\d\\d/', $value) == 1) {
$newValue = fmod($newValue, 1.0);
}
$this->value = $newValue;
$dataType = DataType::TYPE_NUMERIC; $dataType = DataType::TYPE_NUMERIC;
break; break;

View File

@ -9,7 +9,9 @@ use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class Date class Date
@ -158,6 +160,36 @@ class Date
throw new PhpSpreadsheetException('Invalid timezone'); throw new PhpSpreadsheetException('Invalid timezone');
} }
/**
* @param mixed $value
*
* @return float|int
*/
public static function convertIsoDate($value)
{
if (!is_string($value)) {
throw new Exception('Non-string value supplied for Iso Date conversion');
}
$date = new DateTime($value);
$dateErrors = DateTime::getLastErrors();
if (is_array($dateErrors) && ($dateErrors['warning_count'] > 0 || $dateErrors['error_count'] > 0)) {
throw new Exception("Invalid string $value supplied for datatype Date");
}
$newValue = SharedDate::PHPToExcel($date);
if ($newValue === false) {
throw new Exception("Invalid string $value supplied for datatype Date");
}
if (preg_match('/^\\d\\d:\\d\\d:\\d\\d/', $value) == 1) {
$newValue = fmod($newValue, 1.0);
}
return $newValue;
}
/** /**
* Convert a MS serialized datetime value from Excel to a PHP Date/Time object. * Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
* *

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Cell; namespace PhpOffice\PhpSpreadsheetTests\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Color;
@ -33,6 +34,19 @@ class CellTest extends TestCase
return require 'tests/data/Cell/SetValueExplicit.php'; return require 'tests/data/Cell/SetValueExplicit.php';
} }
public function testInvalidIsoDateSetValueExplicit(): void
{
$spreadsheet = new Spreadsheet();
$cell = $spreadsheet->getActiveSheet()->getCell('A1');
$dateValue = '2022-02-29'; // Invalid leap year
$this->expectException(Exception::class);
$this->expectExceptionMessage("Invalid string {$dateValue} supplied for datatype Date");
$cell->setValueExplicit($dateValue, DataType::TYPE_ISO_DATE);
$spreadsheet->disconnectWorksheets();
}
/** /**
* @dataProvider providerSetValueExplicitException * @dataProvider providerSetValueExplicitException
* *

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Shared; namespace PhpOffice\PhpSpreadsheetTests\Shared;
use DateTimeZone; use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -209,6 +210,15 @@ class DateTest extends TestCase
return require 'tests/data/Shared/Date/ExcelToTimestamp1900Timezone.php'; return require 'tests/data/Shared/Date/ExcelToTimestamp1900Timezone.php';
} }
public function testConvertIsoDateError(): void
{
Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
$this->expectException(Exception::class);
$this->expectExceptionMessage('Non-string value supplied for Iso Date conversion');
Date::convertIsoDate(false);
}
public function testVarious(): void public function testVarious(): void
{ {
Date::setDefaultTimeZone('UTC'); Date::setDefaultTimeZone('UTC');

View File

@ -1,5 +1,6 @@
<?php <?php
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\DataType;
return [ return [
@ -39,12 +40,27 @@ return [
DataType::TYPE_NUMERIC, DataType::TYPE_NUMERIC,
], ],
[ [
'#DIV/0!', 44613.43090277778,
'2022-02-21 10:20:30',
DataType::TYPE_ISO_DATE,
],
[
44613.0,
'2022-02-21',
DataType::TYPE_ISO_DATE,
],
[
-30879.0,
'1815-06-15', // Dates outside the Excel Range should fail really
DataType::TYPE_ISO_DATE,
],
[
ExcelError::DIV0(),
'#DIV/0!', '#DIV/0!',
DataType::TYPE_ERROR, DataType::TYPE_ERROR,
], ],
[ [
'#NULL!', ExcelError::null(),
'NOT A VALID ERROR TYPE VALUE', 'NOT A VALID ERROR TYPE VALUE',
DataType::TYPE_ERROR, DataType::TYPE_ERROR,
], ],