diff --git a/CHANGELOG.md b/CHANGELOG.md index c8612cad6c..229a320227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Changed - On read, Xlsx Reader had been breaking up union ranges into separate individual ranges. It will now try to preserve range as it was read in. [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042) +- Xlsx/Xls spreadsheet calculation and formatting of dates will use base date of spreadsheet even when spreadsheets with different base dates are simultaneously open. [Issue #1036](https://github.com/PHPOffice/PhpSpreadsheet/issues/1036) [Issue #1635](https://github.com/PHPOffice/PhpSpreadsheet/issues/1635) [PR #4071](https://github.com/PHPOffice/PhpSpreadsheet/pull/4071) ### Deprecated diff --git a/src/PhpSpreadsheet/Cell/Cell.php b/src/PhpSpreadsheet/Cell/Cell.php index 03fa29c545..0f0075bcaa 100644 --- a/src/PhpSpreadsheet/Cell/Cell.php +++ b/src/PhpSpreadsheet/Cell/Cell.php @@ -187,10 +187,15 @@ public function getValueString(): string */ public function getFormattedValue(): string { - return (string) NumberFormat::toFormattedString( - $this->getCalculatedValueString(), + $currentCalendar = SharedDate::getExcelCalendar(); + SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar()); + $formattedValue = (string) NumberFormat::toFormattedString( + $this->getCalculatedValue(), (string) $this->getStyle()->getNumberFormat()->getFormatCode(true) ); + SharedDate::setExcelCalendar($currentCalendar); + + return $formattedValue; } protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, mixed $oldValue, mixed $newValue): void @@ -372,6 +377,8 @@ public function getCalculatedValue(bool $resetLog = true): mixed { if ($this->dataType === DataType::TYPE_FORMULA) { try { + $currentCalendar = SharedDate::getExcelCalendar(); + SharedDate::setExcelCalendar($this->getWorksheet()->getParent()?->getExcelCalendar()); $index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex(); $selected = $this->getWorksheet()->getSelectedCells(); $result = Calculation::getInstance( @@ -387,6 +394,7 @@ public function getCalculatedValue(bool $resetLog = true): mixed } } } catch (SpreadsheetException $ex) { + SharedDate::setExcelCalendar($currentCalendar); if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) { return $this->calculatedValue; // Fallback for calculations referencing external files. } elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) { @@ -399,6 +407,7 @@ public function getCalculatedValue(bool $resetLog = true): mixed $ex ); } + SharedDate::setExcelCalendar($currentCalendar); if ($result === '#Not Yet Implemented') { return $this->calculatedValue; // Fallback if calculation engine does not support the formula. diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 58a22835de..be9ccb2681 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -1927,8 +1927,10 @@ private function readDateMode(): void // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900); + $this->spreadsheet->setExcelCalendar(Date::CALENDAR_WINDOWS_1900); if (ord($recordData[0]) == 1) { Date::setExcelCalendar(Date::CALENDAR_MAC_1904); + $this->spreadsheet->setExcelCalendar(Date::CALENDAR_MAC_1904); } } diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 9fdf96aeca..882f29644f 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -712,12 +712,14 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet $xmlWorkbookNS = $this->loadZip($relTarget, $mainNS); // Set base date + $excel->setExcelCalendar(Date::CALENDAR_WINDOWS_1900); if ($xmlWorkbookNS->workbookPr) { Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900); $attrs1904 = self::getAttributes($xmlWorkbookNS->workbookPr); if (isset($attrs1904['date1904'])) { if (self::boolean((string) $attrs1904['date1904'])) { Date::setExcelCalendar(Date::CALENDAR_MAC_1904); + $excel->setExcelCalendar(Date::CALENDAR_MAC_1904); } } } diff --git a/src/PhpSpreadsheet/Shared/Date.php b/src/PhpSpreadsheet/Shared/Date.php index ed19534de0..b8feeb9c20 100644 --- a/src/PhpSpreadsheet/Shared/Date.php +++ b/src/PhpSpreadsheet/Shared/Date.php @@ -10,7 +10,6 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Exception; use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; -use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; class Date @@ -64,15 +63,15 @@ class Date /** * Set the Excel calendar (Windows 1900 or Mac 1904). * - * @param int $baseYear Excel base date (1900 or 1904) + * @param ?int $baseYear Excel base date (1900 or 1904) * * @return bool Success or failure */ - public static function setExcelCalendar(int $baseYear): bool + public static function setExcelCalendar(?int $baseYear): bool { if ( - ($baseYear == self::CALENDAR_WINDOWS_1900) - || ($baseYear == self::CALENDAR_MAC_1904) + ($baseYear === self::CALENDAR_WINDOWS_1900) + || ($baseYear === self::CALENDAR_MAC_1904) ) { self::$excelCalendar = $baseYear; @@ -173,7 +172,7 @@ public static function convertIsoDate(mixed $value): float|int throw new Exception("Invalid string $value supplied for datatype Date"); } - $newValue = SharedDate::PHPToExcel($date); + $newValue = self::PHPToExcel($date); if ($newValue === false) { throw new Exception("Invalid string $value supplied for datatype Date"); } diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 095507d7b7..e571cc4f6b 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\Document\Security; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; +use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Style\Style; @@ -31,6 +32,8 @@ class Spreadsheet implements JsonSerializable self::VISIBILITY_VERY_HIDDEN, ]; + protected int $excelCalendar = Date::CALENDAR_WINDOWS_1900; + /** * Unique ID. */ @@ -1553,4 +1556,26 @@ public function getTableByName(string $tableName): ?Table return $table; } + + /** + * @return bool Success or failure + */ + public function setExcelCalendar(int $baseYear): bool + { + if (($baseYear === Date::CALENDAR_WINDOWS_1900) || ($baseYear === Date::CALENDAR_MAC_1904)) { + $this->excelCalendar = $baseYear; + + return true; + } + + return false; + } + + /** + * @return int Excel base date (1900 or 1904) + */ + public function getExcelCalendar(): int + { + return $this->excelCalendar; + } } diff --git a/src/PhpSpreadsheet/Writer/Xls/Workbook.php b/src/PhpSpreadsheet/Writer/Xls/Workbook.php index 62d4b08d9d..a1a5faf8f4 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xls/Workbook.php @@ -910,9 +910,9 @@ private function writeDateMode(): void $record = 0x0022; // Record identifier $length = 0x0002; // Bytes to follow - $f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) - ? 1 - : 0; // Flag for 1904 date system + $f1904 = ($this->spreadsheet->getExcelCalendar() === Date::CALENDAR_MAC_1904) + ? 1 // Flag for 1904 date system + : 0; // Flag for 1900 date system $header = pack('vv', $record, $length); $data = pack('v', $f1904); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php index 5604be5443..0cdfb3cd87 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php @@ -40,7 +40,7 @@ public function writeWorkbook(Spreadsheet $spreadsheet, bool $recalcRequired = f $this->writeFileVersion($objWriter); // workbookPr - $this->writeWorkbookPr($objWriter); + $this->writeWorkbookPr($objWriter, $spreadsheet); // workbookProtection $this->writeWorkbookProtection($objWriter, $spreadsheet); @@ -81,11 +81,11 @@ private function writeFileVersion(XMLWriter $objWriter): void /** * Write WorkbookPr. */ - private function writeWorkbookPr(XMLWriter $objWriter): void + private function writeWorkbookPr(XMLWriter $objWriter, Spreadsheet $spreadsheet): void { $objWriter->startElement('workbookPr'); - if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) { + if ($spreadsheet->getExcelCalendar() === Date::CALENDAR_MAC_1904) { $objWriter->writeAttribute('date1904', '1'); } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/DateReaderTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/DateReaderTest.php new file mode 100644 index 0000000000..58bcbb95b4 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/DateReaderTest.php @@ -0,0 +1,127 @@ +load($filename); + + self::assertSame(Date::CALENDAR_WINDOWS_1900, $spreadsheet->getExcelCalendar()); + + $worksheet = $spreadsheet->getActiveSheet(); + self::assertSame(44562, $worksheet->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet->getCell('A2')->getFormattedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testReadExcel1904Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLS/1904_Calendar.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + + self::assertSame(Date::CALENDAR_MAC_1904, $spreadsheet->getExcelCalendar()); + + $worksheet = $spreadsheet->getActiveSheet(); + self::assertSame(43100, $worksheet->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet->getCell('A2')->getFormattedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testNewDateInLoadedExcel1900Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLS/1900_Calendar.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->getCell('A4')->setValue('=DATE(2023,1,1)'); + self::assertEquals(44927, $worksheet->getCell('A4')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testNewDateInLoadedExcel1904Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLS/1904_Calendar.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->getCell('A4')->setValue('=DATE(2023,1,1)'); + self::assertEquals(43465, $worksheet->getCell('A4')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testSwitchCalendars(): void + { + $filename1904 = 'tests/data/Reader/XLS/1904_Calendar.xls'; + $reader1904 = new Xls(); + $spreadsheet1904 = $reader1904->load($filename1904); + $worksheet1904 = $spreadsheet1904->getActiveSheet(); + $date1 = Date::convertIsoDate('2022-01-01'); + self::assertSame(43100.0, $date1); + + $filename1900 = 'tests/data/Reader/XLS/1900_Calendar.xls'; + $reader1900 = new Xls(); + $spreadsheet1900 = $reader1900->load($filename1900); + $worksheet1900 = $spreadsheet1900->getActiveSheet(); + $date2 = Date::convertIsoDate('2022-01-01'); + self::assertSame(44562.0, $date2); + + self::assertSame(44562, $worksheet1900->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1900->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet1900->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1900->getCell('A2')->getFormattedValue()); + self::assertSame(44561, $worksheet1900->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1900->getCell('B1')->getFormattedValue()); + self::assertSame(44927, $worksheet1900->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1900->getCell('B2')->getFormattedValue()); + + self::assertSame(43100, $worksheet1904->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1904->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet1904->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1904->getCell('A2')->getFormattedValue()); + self::assertSame(43099, $worksheet1904->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1904->getCell('B1')->getFormattedValue()); + self::assertSame(43465, $worksheet1904->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1904->getCell('B2')->getFormattedValue()); + + // Check that accessing date values from one spreadsheet doesn't break accessing correct values from another + self::assertSame(44561, $worksheet1900->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1900->getCell('B1')->getFormattedValue()); + self::assertSame(44927, $worksheet1900->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1900->getCell('B2')->getFormattedValue()); + self::assertSame(44562, $worksheet1900->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1900->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet1900->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1900->getCell('A2')->getFormattedValue()); + + self::assertSame(43099, $worksheet1904->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1904->getCell('B1')->getFormattedValue()); + self::assertSame(43465, $worksheet1904->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1904->getCell('B2')->getFormattedValue()); + self::assertSame(43100, $worksheet1904->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1904->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet1904->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1904->getCell('A2')->getFormattedValue()); + $spreadsheet1900->disconnectWorksheets(); + $spreadsheet1904->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/DateReaderTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/DateReaderTest.php new file mode 100644 index 0000000000..7eaee0df73 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/DateReaderTest.php @@ -0,0 +1,135 @@ +load($filename); + + self::assertSame(Date::CALENDAR_WINDOWS_1900, $spreadsheet->getExcelCalendar()); + + $worksheet = $spreadsheet->getActiveSheet(); + self::assertSame(44562, $worksheet->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet->getCell('A2')->getFormattedValue()); + self::assertSame(44561, $worksheet->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet->getCell('B1')->getFormattedValue()); + self::assertSame(44927, $worksheet->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet->getCell('B2')->getFormattedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testReadExcel1904Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLSX/1904_Calendar.xlsx'; + $reader = new Xlsx(); + $spreadsheet = $reader->load($filename); + + self::assertSame(Date::CALENDAR_MAC_1904, $spreadsheet->getExcelCalendar()); + + $worksheet = $spreadsheet->getActiveSheet(); + self::assertSame(43100, $worksheet->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet->getCell('A2')->getFormattedValue()); + self::assertSame(43099, $worksheet->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet->getCell('B1')->getFormattedValue()); + self::assertSame(43465, $worksheet->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet->getCell('B2')->getFormattedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testNewDateInLoadedExcel1900Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLSX/1900_Calendar.xlsx'; + $reader = new Xlsx(); + $spreadsheet = $reader->load($filename); + + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->getCell('A4')->setValue('=DATE(2023,1,1)'); + self::assertEquals(44927, $worksheet->getCell('A4')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testNewDateInLoadedExcel1904Spreadsheet(): void + { + $filename = 'tests/data/Reader/XLSX/1904_Calendar.xlsx'; + $reader = new Xlsx(); + $spreadsheet = $reader->load($filename); + + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->getCell('A4')->setValue('=DATE(2023,1,1)'); + self::assertEquals(43465, $worksheet->getCell('A4')->getCalculatedValue()); + $spreadsheet->disconnectWorksheets(); + } + + public function testSwitchCalendars(): void + { + $filename1904 = 'tests/data/Reader/XLSX/1904_Calendar.xlsx'; + $reader1904 = new Xlsx(); + $spreadsheet1904 = $reader1904->load($filename1904); + $worksheet1904 = $spreadsheet1904->getActiveSheet(); + $date1 = Date::convertIsoDate('2022-01-01'); + self::assertSame(43100.0, $date1); + + $filename1900 = 'tests/data/Reader/XLSX/1900_Calendar.xlsx'; + $reader1900 = new Xlsx(); + $spreadsheet1900 = $reader1900->load($filename1900); + $worksheet1900 = $spreadsheet1900->getActiveSheet(); + $date2 = Date::convertIsoDate('2022-01-01'); + self::assertSame(44562.0, $date2); + + self::assertSame(44562, $worksheet1900->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1900->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet1900->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1900->getCell('A2')->getFormattedValue()); + self::assertSame(44561, $worksheet1900->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1900->getCell('B1')->getFormattedValue()); + self::assertSame(44927, $worksheet1900->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1900->getCell('B2')->getFormattedValue()); + + self::assertSame(43100, $worksheet1904->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1904->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet1904->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1904->getCell('A2')->getFormattedValue()); + self::assertSame(43099, $worksheet1904->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1904->getCell('B1')->getFormattedValue()); + self::assertSame(43465, $worksheet1904->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1904->getCell('B2')->getFormattedValue()); + + // Check that accessing date values from one spreadsheet doesn't break accessing correct values from another + self::assertSame(44561, $worksheet1900->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1900->getCell('B1')->getFormattedValue()); + self::assertSame(44927, $worksheet1900->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1900->getCell('B2')->getFormattedValue()); + self::assertSame(44562, $worksheet1900->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1900->getCell('A1')->getFormattedValue()); + self::assertSame(44926, $worksheet1900->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1900->getCell('A2')->getFormattedValue()); + + self::assertSame(43099, $worksheet1904->getCell('B1')->getCalculatedValue()); + self::assertSame('2021-12-31', $worksheet1904->getCell('B1')->getFormattedValue()); + self::assertSame(43465, $worksheet1904->getCell('B2')->getCalculatedValue()); + self::assertSame('2023-01-01', $worksheet1904->getCell('B2')->getFormattedValue()); + self::assertSame(43100, $worksheet1904->getCell('A1')->getValue()); + self::assertSame('2022-01-01', $worksheet1904->getCell('A1')->getFormattedValue()); + self::assertSame(43464, $worksheet1904->getCell('A2')->getValue()); + self::assertSame('2022-12-31', $worksheet1904->getCell('A2')->getFormattedValue()); + $spreadsheet1900->disconnectWorksheets(); + $spreadsheet1904->disconnectWorksheets(); + } +} diff --git a/tests/data/Reader/XLS/1900_Calendar.xls b/tests/data/Reader/XLS/1900_Calendar.xls new file mode 100644 index 0000000000..714670a7dc Binary files /dev/null and b/tests/data/Reader/XLS/1900_Calendar.xls differ diff --git a/tests/data/Reader/XLS/1904_Calendar.xls b/tests/data/Reader/XLS/1904_Calendar.xls new file mode 100644 index 0000000000..cde7fc1931 Binary files /dev/null and b/tests/data/Reader/XLS/1904_Calendar.xls differ diff --git a/tests/data/Reader/XLSX/1900_Calendar.xlsx b/tests/data/Reader/XLSX/1900_Calendar.xlsx new file mode 100644 index 0000000000..dfd78efc7c Binary files /dev/null and b/tests/data/Reader/XLSX/1900_Calendar.xlsx differ diff --git a/tests/data/Reader/XLSX/1904_Calendar.xlsx b/tests/data/Reader/XLSX/1904_Calendar.xlsx new file mode 100644 index 0000000000..f3a8a12d8e Binary files /dev/null and b/tests/data/Reader/XLSX/1904_Calendar.xlsx differ