profile
viewpoint

Ask questionsPHPToExcel::timestampToExcel - Forces GMT timezone

This is:

  • [x] a bug report
  • [ ] a feature request
  • [X] not a usage question (ask them on https://stackoverflow.com/questions/tagged/phpspreadsheet or https://gitter.im/PHPOffice/PhpSpreadsheet)

What is the expected behavior?

When supplying a unix tImestamp it should be displayed in the specified application timezone and function identically to the dateTimeToExcel and stringToExcel functions.

When specifying a unix timestamp (strtotime('2018-01-01 13:00:00')), the Excel date should be represented as 2018-01-01 13:00:00. Identically to using a DateTime object (\DateTime('2018-01-01 13:00:00'))

What is the current behavior?

PHPToExcel::timestampToExcel uses the new \DateTime('@' . $timestamp) syntax. Which ignores the specified application timezone, instead using the default GMT timezone, and produces unexpected results, where dateTimeToExcel uses the specified DateTime object timezone

The represented Excel date is 2018-01-01 18:00:00 See PHP example: https://3v4l.org/H50W7

The issue is compounded when an application specifies a timezone that observes Daylight Savings Time (DST), where the represented GMT time is displayed with the adjusted hour. Example: https://3v4l.org/mhIqn

What are the steps to reproduce?

<?php
require_once __DIR__ . '/vendor/autoload.php';

// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

// add code that show the issue here...
date_default_timezone_set('America/New_York');
$dateString = '2018-01-01 13:00:00';
$unixTimestamp = strtotime($dateString);
$dateObject = new \DateTime($dateString);

#draw the specified date time to excel
$worksheet = $spreadsheet->setActiveSheetIndex(0);

#displays 2018-01-01 13:00:00
$worksheet
    ->getCellByColumnAndRow(1, 1)
    ->setValue(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateString))
    ->getStyle()
    ->getNumberFormat()
    ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME);

#displays: 2018-01-01 13:00:00
$worksheet
    ->getCellByColumnAndRow(1, 2)
    ->setValue(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateObject))
    ->getStyle()
    ->getNumberFormat()
    ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME);

#displays: 2018-01-01 18:00:00
$worksheet
    ->getCellByColumnAndRow(1, 3)
    ->setValue(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($unixTimestamp))
    ->getStyle()
    ->getNumberFormat()
    ->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME);

Which versions of PhpSpreadsheet and PHP are affected?

  • PHP 5.6
  • PhpSpreadsheet 1.3.1

Suggested Resolutions

Goal: reproduce same results as dateTimeToExcel and stringToExcel

  1. Use setTimestamp instead of @ - correct usage (BC Break)
    public static function timestampToExcel($dateValue)
    {
        if (!is_numeric($dateValue)) {
            return false;
        }
        $dateObject = new \DateTime;

        return self::dateTimeToExcel($dateObject->setTimeStamp($dateValue));
    }
  1. Use self::stringToExcel instead of self::dateTimeToExcel (BC Break)

    public static function timestampToExcel($dateValue)
    {
        if (!is_numeric($dateValue)) {
            return false;
        }

        return self::stringToExcel(date('Y-m-d H:i:s', $dateValue));
    }
  1. Add desired timezone null, text or object argument, default to UTC. (BC compliant) null uses specified application timezone. Example: https://3v4l.org/l2jjV
    public static function timestampToExcel($dateValue, $tz = 'UTC')
    {
        if (!is_numeric($dateValue)) {
            return false;
        }
        if (null !== $tz) {
             if (is_string($tz)) {
                $tz = new \DateTimeZone($tz);
             }
        }
        
        $dateObject = new \DateTime('', $tz);
       
        return self::dateTimeToExcel($dateObject->setTimestamp($dateValue));
    }
  1. Add desired timezone null, text or object argument default to null. (BC compliant) null ignores specified application timezone
    public static function timestampToExcel($dateValue, $tz = null)
    {
        if (!is_numeric($dateValue)) {
            return false;
        }
        
         $dateObject = new \DateTime('@' . $dateValue);
         if (null !== $tz) {
             if (is_string($tz)) {
                $tz = new \DateTimeZone($tz);
             }
             $dateObject->setTimezone($tz);
        }
        return self::dateTimeToExcel($dateObject);
    }

If the forced use of a GMT timezone is expected behavior, it should be documented on the timestampToExcel and PHPToExcel methods, that unix timestamps will use a GMT timezone and not the specified application timezone.

TLDR:

Albeit it is best practice to utilize UTC as the default timezone for an application. When a developer specifies a timezone to be used throughout the application, the behavior should not be altered by third-party libraries. Using new DateTime('@' . $timestamp) ignores the specified application timezone, producing an unexpected behavior/output, that must be compensated for, and a resolution sought out unless the behavior is documented.

PHPOffice/PhpSpreadsheet

Answer questions fyrye

@ptrsz Correct, however strtotime honors the PHP default timezone, the issue is caused by Date::PHPToExcel() using new DateTime('@' . $dateValue), which sets a UTC timezone [sic], when Date::PHPToExcel receives an integer . Your solution will fix the issue only when using the Xml reader. I recommend changing src/PhpSpreadsheet/Shared/Date.php to ensure it functions the same throughout your application.

The generally accepted usage is use setTimestamp and replace new DateTime('@' . $dateValue) with (new DateTime())->setTimestamp($dateValue)

useful!
source:https://uonfu.com/
Github User Rank List