Option to return formatted dates instead of PHP objects (#226)

When reading spreadsheets, Spout should be able to return formatted dates, as shown when opened with Excel for instance.
It currently only returns DateTime/DateInterval objects, making it impossible to read + write, as the Writer does not accept objects.
This commit is contained in:
Adrien Loison committed 2016-05-20 16:08:35 -07:00
1 parent a6a6b158de
commit 104cd9b811
21 files changed
+389 -51

No files matched your search

@@ -44,17 +44,22 @@ class CellValueFormatter
/** @var StyleHelper Helper to work with styles */
protected $styleHelper;
/** @var bool Whether date/time values should be returned as PHP objects or be formatted as strings */
protected $shouldFormatDates;
/** @var \Box\Spout\Common\Escaper\XLSX Used to unescape XML data */
protected $escaper;
/**
* @param SharedStringsHelper $sharedStringsHelper Helper to work with shared strings
* @param StyleHelper $styleHelper Helper to work with styles
* @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings
*/
public function __construct($sharedStringsHelper, $styleHelper)
public function __construct($sharedStringsHelper, $styleHelper, $shouldFormatDates)
{
$this->sharedStringsHelper = $sharedStringsHelper;
$this->styleHelper = $styleHelper;
$this->shouldFormatDates = $shouldFormatDates;
/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
$this->escaper = new \Box\Spout\Common\Escaper\XLSX();
@@ -168,7 +173,7 @@ class CellValueFormatter
$shouldFormatAsDate = $this->styleHelper->shouldFormatNumericValueAsDate($cellStyleId);
if ($shouldFormatAsDate) {
return $this->formatExcelTimestampValue(floatval($nodeValue));
return $this->formatExcelTimestampValue(floatval($nodeValue), $cellStyleId);
} else {
$nodeIntValue = intval($nodeValue);
return ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
@@ -181,9 +186,10 @@ class CellValueFormatter
* NOTE: The timestamp can also represent a time, if it is a value between 0 and 1.
*
* @param float $nodeValue
* @param int $cellStyleId 0 being the default style
* @return \DateTime|null The value associated with the cell or NULL if invalid date value
*/
protected function formatExcelTimestampValue($nodeValue)
protected function formatExcelTimestampValue($nodeValue, $cellStyleId)
{
// Fix for the erroneous leap year in Excel
if (ceil($nodeValue) > self::ERRONEOUS_EXCEL_LEAP_YEAR_DAY) {
@@ -192,10 +198,10 @@ class CellValueFormatter
if ($nodeValue >= 1) {
// Values greater than 1 represent "dates". The value 1.0 representing the "base" date: 1900-01-01.
return $this->formatExcelTimestampValueAsDateValue($nodeValue);
return $this->formatExcelTimestampValueAsDateValue($nodeValue, $cellStyleId);
} else if ($nodeValue >= 0) {
// Values between 0 and 1 represent "times".
return $this->formatExcelTimestampValueAsTimeValue($nodeValue);
return $this->formatExcelTimestampValueAsTimeValue($nodeValue, $cellStyleId);
} else {
// invalid date
return null;
@@ -207,9 +213,10 @@ class CellValueFormatter
* Only the time value matters. The date part is set to Jan 1st, 1900 (base Excel date).
*
* @param float $nodeValue
* @return \DateTime The value associated with the cell
* @param int $cellStyleId 0 being the default style
* @return \DateTime|string The value associated with the cell
*/
protected function formatExcelTimestampValueAsTimeValue($nodeValue)
protected function formatExcelTimestampValueAsTimeValue($nodeValue, $cellStyleId)
{
$time = round($nodeValue * self::NUM_SECONDS_IN_ONE_DAY);
$hours = floor($time / self::NUM_SECONDS_IN_ONE_HOUR);
@@ -220,7 +227,13 @@ class CellValueFormatter
$dateObj = new \DateTime('1900-01-01');
$dateObj->setTime($hours, $minutes, $seconds);
return $dateObj;
if ($this->shouldFormatDates) {
$styleNumberFormat = $this->styleHelper->getNumberFormat($cellStyleId);
$phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormat);
return $dateObj->format($phpDateFormat);
} else {
return $dateObj;
}
}
/**
@@ -228,9 +241,10 @@ class CellValueFormatter
* NOTE: The timestamp is a float representing the number of days since January 1st, 1900.
*
* @param float $nodeValue
* @return \DateTime|null The value associated with the cell or NULL if invalid date value
* @param int $cellStyleId 0 being the default style
* @return \DateTime|string|null The value associated with the cell or NULL if invalid date value
*/
protected function formatExcelTimestampValueAsDateValue($nodeValue)
protected function formatExcelTimestampValueAsDateValue($nodeValue, $cellStyleId)
{
// Do not use any unix timestamps for calculation to prevent
// issues with numbers exceeding 2^31.
@@ -242,7 +256,13 @@ class CellValueFormatter
$dateObj->modify('+' . intval($nodeValue) . 'days');
$dateObj->modify('+' . $secondsRemainder . 'seconds');
return $dateObj;
if ($this->shouldFormatDates) {
$styleNumberFormat = $this->styleHelper->getNumberFormat($cellStyleId);
$phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormat);
return $dateObj->format($phpDateFormat);
} else {
return $dateObj;
}
} catch (\Exception $e) {
return null;
}