|
1 | 1 | <?php |
2 | 2 | namespace codemix\excelexport; |
3 | 3 |
|
| 4 | +use Yii; |
4 | 5 | use yii\helpers\ArrayHelper; |
5 | 6 |
|
6 | 7 | /** |
@@ -186,12 +187,29 @@ public function getFormatters() |
186 | 187 | foreach ($attrs as $c => $attr) { |
187 | 188 | switch ($types[$c]->type) { |
188 | 189 | case 'date': |
| 190 | + $this->_formatters[$c] = function ($v) { |
| 191 | + if (empty($v)) { |
| 192 | + return null; |
| 193 | + } else { |
| 194 | + // Set the correct timezone before converting to a UNIX timestamp. |
| 195 | + // This prevents dates from being altered due to timezone |
| 196 | + // conversion, e.g. |
| 197 | + // '2017-12-05 00:00:00' could become |
| 198 | + // '2017-12-04 23:00:00' |
| 199 | + $timezone = date_default_timezone_get(); |
| 200 | + date_default_timezone_set(Yii::$app->formatter->defaultTimeZone); |
| 201 | + $timestamp = strtotime($v); |
| 202 | + date_default_timezone_set($timezone); |
| 203 | + return \PHPExcel_Shared_Date::PHPToExcel($timestamp); |
| 204 | + } |
| 205 | + }; |
| 206 | + break; |
189 | 207 | case 'datetime': |
190 | 208 | $this->_formatters[$c] = function ($v) { |
191 | 209 | if (empty($v)) { |
192 | 210 | return null; |
193 | 211 | } else { |
194 | | - return \PHPExcel_Shared_Date::PHPToExcel(strtotime($v)); |
| 212 | + return \PHPExcel_Shared_Date::PHPToExcel($this->toExcelTime($v)); |
195 | 213 | } |
196 | 214 | }; |
197 | 215 | break; |
@@ -249,6 +267,43 @@ protected function renderRow($data, $row, $formats, $formatters, $callbacks, $ty |
249 | 267 | return parent::renderRow($values, $row, $formats, $formatters, $callbacks, $types); |
250 | 268 | } |
251 | 269 |
|
| 270 | + /** |
| 271 | + * Convert a datetime to the right excel timestamp |
| 272 | + * |
| 273 | + * This method will use [[\yii\i18n\Formatter::defaultTimeZone]] and |
| 274 | + * [[\yii\base\Application::timeZone]] to convert the given datetime |
| 275 | + * from DB to application timezone. |
| 276 | + * |
| 277 | + * @param string $value the datetime value |
| 278 | + * @return int timezone offset in seconds |
| 279 | + * @see [[yii\i18n\Formatter::defaultTimeZone]] |
| 280 | + * @see [[yii\base\Application::timeZone]] |
| 281 | + */ |
| 282 | + protected function toExcelTime($value) |
| 283 | + { |
| 284 | + // "Cached" timezone instances |
| 285 | + static $defaultTimezone; |
| 286 | + static $timezone; |
| 287 | + |
| 288 | + if (Yii::$app->formatter->defaultTimeZone === Yii::$app->timeZone) { |
| 289 | + return strtotime($value); |
| 290 | + } else { |
| 291 | + if ($timezone === null) { |
| 292 | + $defaultTimezone = new \DateTimeZone(Yii::$app->formatter->defaultTimeZone); |
| 293 | + $timezone = new \DateTimeZone(Yii::$app->timeZone); |
| 294 | + } |
| 295 | + |
| 296 | + // Offset can depend on given datetime due to DST |
| 297 | + $defaultDatetime = new \DateTime($value, $defaultTimezone); |
| 298 | + $offset = $timezone->getOffset($defaultDatetime); |
| 299 | + |
| 300 | + // PHPExcel_Shared_Date::PHPToExcel() method expects a |
| 301 | + // "pseudo-timestamp": Something like a UNIX timestamp but |
| 302 | + // including local timezone offset. |
| 303 | + return $defaultDatetime->getTimestamp() + $offset; |
| 304 | + } |
| 305 | + } |
| 306 | + |
252 | 307 | /** |
253 | 308 | * Returns either the ColumnSchema or a new instance of the related model |
254 | 309 | * for the given attribute name. |
|
0 commit comments