Merge pull request #2625 from PHPOffice/Iso-Date-Datatype-Additional-Unit-Tests
Minor refactoring of ISO Date checking
This commit is contained in:
commit
d88e8f85ba
|
|
@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Nothing
|
- Gnumeric Reader now loads number formatting for cells.
|
||||||
|
- Gnumeric Reader now correctly identifies selected worksheet.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -284,12 +284,8 @@ class Gnumeric extends BaseReader
|
||||||
$row = (int) $cellAttributes->Row + 1;
|
$row = (int) $cellAttributes->Row + 1;
|
||||||
$column = (int) $cellAttributes->Col;
|
$column = (int) $cellAttributes->Col;
|
||||||
|
|
||||||
if ($row > $maxRow) {
|
$maxRow = max($maxRow, $row);
|
||||||
$maxRow = $row;
|
$maxCol = max($maxCol, $column);
|
||||||
}
|
|
||||||
if ($column > $maxCol) {
|
|
||||||
$maxCol = $column;
|
|
||||||
}
|
|
||||||
|
|
||||||
$column = Coordinate::stringFromColumnIndex($column + 1);
|
$column = Coordinate::stringFromColumnIndex($column + 1);
|
||||||
|
|
||||||
|
|
@ -300,38 +296,7 @@ class Gnumeric extends BaseReader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ValueType = $cellAttributes->ValueType;
|
$this->loadCell($cell, $worksheetName, $cellAttributes, $column, $row);
|
||||||
$ExprID = (string) $cellAttributes->ExprID;
|
|
||||||
$type = DataType::TYPE_FORMULA;
|
|
||||||
if ($ExprID > '') {
|
|
||||||
if (((string) $cell) > '') {
|
|
||||||
$this->expressions[$ExprID] = [
|
|
||||||
'column' => $cellAttributes->Col,
|
|
||||||
'row' => $cellAttributes->Row,
|
|
||||||
'formula' => (string) $cell,
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$expression = $this->expressions[$ExprID];
|
|
||||||
|
|
||||||
$cell = $this->referenceHelper->updateFormulaReferences(
|
|
||||||
$expression['formula'],
|
|
||||||
'A1',
|
|
||||||
$cellAttributes->Col - $expression['column'],
|
|
||||||
$cellAttributes->Row - $expression['row'],
|
|
||||||
$worksheetName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$type = DataType::TYPE_FORMULA;
|
|
||||||
} else {
|
|
||||||
$vtype = (string) $ValueType;
|
|
||||||
if (array_key_exists($vtype, self::$mappings['dataType'])) {
|
|
||||||
$type = self::$mappings['dataType'][$vtype];
|
|
||||||
}
|
|
||||||
if ($vtype === '20') { // Boolean
|
|
||||||
$cell = $cell == 'TRUE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sheet->Styles !== null) {
|
if ($sheet->Styles !== null) {
|
||||||
|
|
@ -349,10 +314,21 @@ class Gnumeric extends BaseReader
|
||||||
|
|
||||||
$this->processDefinedNames($gnmXML);
|
$this->processDefinedNames($gnmXML);
|
||||||
|
|
||||||
|
$this->setSelectedSheet($gnmXML);
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return $this->spreadsheet;
|
return $this->spreadsheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function setSelectedSheet(SimpleXMLElement $gnmXML): void
|
||||||
|
{
|
||||||
|
if (isset($gnmXML->UIData)) {
|
||||||
|
$attributes = self::testSimpleXml($gnmXML->UIData->attributes());
|
||||||
|
$selectedSheet = (int) $attributes['SelectedTab'];
|
||||||
|
$this->spreadsheet->setActiveSheetIndex($selectedSheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function processMergedCells(?SimpleXMLElement $sheet): void
|
private function processMergedCells(?SimpleXMLElement $sheet): void
|
||||||
{
|
{
|
||||||
// Handle Merged Cells in this worksheet
|
// Handle Merged Cells in this worksheet
|
||||||
|
|
@ -530,4 +506,51 @@ class Gnumeric extends BaseReader
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadCell(
|
||||||
|
SimpleXMLElement $cell,
|
||||||
|
string $worksheetName,
|
||||||
|
SimpleXMLElement $cellAttributes,
|
||||||
|
string $column,
|
||||||
|
int $row
|
||||||
|
): void {
|
||||||
|
$ValueType = $cellAttributes->ValueType;
|
||||||
|
$ExprID = (string) $cellAttributes->ExprID;
|
||||||
|
$type = DataType::TYPE_FORMULA;
|
||||||
|
if ($ExprID > '') {
|
||||||
|
if (((string) $cell) > '') {
|
||||||
|
$this->expressions[$ExprID] = [
|
||||||
|
'column' => $cellAttributes->Col,
|
||||||
|
'row' => $cellAttributes->Row,
|
||||||
|
'formula' => (string) $cell,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$expression = $this->expressions[$ExprID];
|
||||||
|
|
||||||
|
$cell = $this->referenceHelper->updateFormulaReferences(
|
||||||
|
$expression['formula'],
|
||||||
|
'A1',
|
||||||
|
$cellAttributes->Col - $expression['column'],
|
||||||
|
$cellAttributes->Row - $expression['row'],
|
||||||
|
$worksheetName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$type = DataType::TYPE_FORMULA;
|
||||||
|
} else {
|
||||||
|
$vtype = (string) $ValueType;
|
||||||
|
if (array_key_exists($vtype, self::$mappings['dataType'])) {
|
||||||
|
$type = self::$mappings['dataType'][$vtype];
|
||||||
|
}
|
||||||
|
if ($vtype === '20') { // Boolean
|
||||||
|
$cell = $cell == 'TRUE';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type);
|
||||||
|
if (isset($cellAttributes->ValueFormat)) {
|
||||||
|
$this->spreadsheet->getActiveSheet()->getCell($column . $row)
|
||||||
|
->getStyle()->getNumberFormat()
|
||||||
|
->setFormatCode((string) $cellAttributes->ValueFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||||
|
|
||||||
use DateTime;
|
|
||||||
use DOMAttr;
|
use DOMAttr;
|
||||||
use DOMDocument;
|
use DOMDocument;
|
||||||
use DOMElement;
|
use DOMElement;
|
||||||
|
|
@ -478,21 +477,7 @@ class Ods extends BaseReader
|
||||||
case 'date':
|
case 'date':
|
||||||
$type = DataType::TYPE_NUMERIC;
|
$type = DataType::TYPE_NUMERIC;
|
||||||
$value = $cellData->getAttributeNS($officeNs, 'date-value');
|
$value = $cellData->getAttributeNS($officeNs, 'date-value');
|
||||||
|
$dataValue = Date::convertIsoDate($value);
|
||||||
$dateObj = new DateTime($value);
|
|
||||||
[$year, $month, $day, $hour, $minute, $second] = explode(
|
|
||||||
' ',
|
|
||||||
$dateObj->format('Y m d H i s')
|
|
||||||
);
|
|
||||||
|
|
||||||
$dataValue = Date::formattedPHPToExcel(
|
|
||||||
(int) $year,
|
|
||||||
(int) $month,
|
|
||||||
(int) $day,
|
|
||||||
(int) $hour,
|
|
||||||
(int) $minute,
|
|
||||||
(int) $second
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($dataValue != floor($dataValue)) {
|
if ($dataValue != floor($dataValue)) {
|
||||||
$formatting = NumberFormat::FORMAT_DATE_XLSX15
|
$formatting = NumberFormat::FORMAT_DATE_XLSX15
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue