Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHPToExcel::timestampToExcel uses GMT timezone #2347

Closed
Bhoft opened this issue Oct 21, 2021 · 2 comments
Closed

PHPToExcel::timestampToExcel uses GMT timezone #2347

Bhoft opened this issue Oct 21, 2021 · 2 comments

Comments

@Bhoft
Copy link

Bhoft commented Oct 21, 2021

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?

I am using the Date::PHPToExcel($ts); with a unix timestamp and was wondering why the timezone which i set wasn't used.

What is the current behavior?

My current timezone set in my application is 'Europe\Berlin' i add some cells with datetimes to the Excel file and the Timezone of those fields isn't correct when i used unix timestamps.

What are the steps to reproduce?

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

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

// add code that show the issue here...
date_default_timezone_set('Europe/Berlin');

\PhpOffice\PhpSpreadsheet\Shared\Date::setDefaultTimezone(date_default_timezone_get());
\PhpOffice\PhpSpreadsheet\Shared\TimeZone::setTimeZone(date_default_timezone_get());

$dateString = '2020-04-01 00:00:00';
$unixTimestamp = strtotime($dateString);
var_dump($unixTimestamp);

$dateObject = DateTime::createFromFormat("U", $unixTimestamp)->setTimeZone(new DateTimeZone(date_default_timezone_get()));
var_dump($dateObject);

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


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

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

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


var_dump(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateString));
var_dump(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($dateObject));
var_dump(\PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($unixTimestamp));

$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->setPreCalculateFormulas(false);

if (isset($_GET['debug']) && ! $_GET['debug']) {
    ob_end_clean();
    $filename = 'test.xlsx';
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-type: ' . 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment; filename="' . $filename .'"');
    header('Cache-Control: max-age=0');
    $writer->save('php://output');
} else {
    $out = ob_get_contents();
    ob_end_clean();
    echo $out;
}
      

Output

$unixTimestamp
test.php:19:int 1585692000

$dateObject
test.php:22:
object(DateTime)[93]
  public 'date' => string '2020-04-01 00:00:00.000000' (length=26)
  public 'timezone_type' => int 3
  public 'timezone' => string 'Europe/Berlin' (length=13)

Date::PHPToExcel($dateString)
test.php:53:float 43922

Date::PHPToExcel($dateObject)
test.php:54:float 43922

Date::PHPToExcel($unixTimestamp)
test.php:55:float 43921.916666667

Problem comes from this function in Shared/Date.php which creates the dates when a timestamp is given to PHPToExcel.

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

        return self::dateTimeToExcel(new \DateTime('@' . $dateValue));
    }

The DateTime constructor ignores any Timezones for UNIX timestamp.

Wouldn't it be more correct if the function creates the Datetime in the timezone which was set by Date::setDefaultTimezone and is returned by Date::getDefaultTimezone?

 /**
 * Return the Default timezone being used for dates.
 *
 * @return DateTimeZone The timezone being used as default for Excel timestamp to PHP DateTime object
 */
public static function getDefaultTimezone()
{
    if (self::$defaultTimeZone === null) {
        self::$defaultTimeZone = new DateTimeZone('UTC');
    }

    return self::$defaultTimeZone;
}

e.g. modify the timestampToExcel to use this default Timezone or PHP's default Timezone?

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

            return self::dateTimeToExcel(\DateTime::createFromFormat("U", $dateValue)->setTimeZone(Date::getDefaultTimezone()));
            // or PHP default timezone?
            return self::dateTimeToExcel(\DateTime::createFromFormat("U", $dateValue)->setTimeZone(new \DateTimeZone(date_default_timezone_get())));
        }

This issue was also discussed here:
#611

Or at least add some text to the documentation that the function does work differently for Unix Timestamps as nothing is mentioned there:
https://phpoffice.github.io/PhpSpreadsheet/classes/PhpOffice-PhpSpreadsheet-Shared-Date.html#method_PHPToExcel

@oleibman
Copy link
Collaborator

The use of Unix timestamps rather than DateTime objects is problematic on many levels. I even suggested deprecating such usage (see PR #1870), although we decided that they were probably still too much in use to do so at this time. Nevertheless, your suggestion to update the doc block might be worthwhile; I will look into it.

oleibman added a commit to oleibman/PhpSpreadsheet that referenced this issue Oct 24, 2021
This was suggested by issue PHPOffice#2347. Unix timestamps have clear disadvantages compared with the alternate methods of supplying date and time to PhpSpreadsheet - DateTime objects, Excel date time fields, and strings. In particular, Unix timestamp is not Y2038-safe on a 32-bit system, and it reflects the time in UTC, which may come as a surprise to the end-user (as it did in the cited issue). The alternate methods do not come with such baggage. This change updates some doc blocks to note that Unix timestamps are discoburage (N.B. - not deprecated). No executable code is changed.
oleibman added a commit that referenced this issue Nov 4, 2021
* Update Doc Blocks to Discourage Use of Unix Timestamps

This was suggested by issue #2347. Unix timestamps have clear disadvantages compared with the alternate methods of supplying date and time to PhpSpreadsheet - DateTime objects, Excel date time fields, and strings. In particular, Unix timestamp is not Y2038-safe on a 32-bit system, and it reflects the time in UTC, which may come as a surprise to the end-user (as it did in the cited issue). The alternate methods do not come with such baggage. This change updates some doc blocks to note that Unix timestamps are discoburage (N.B. - not deprecated). No executable code is changed.

* Document in Code As Well as Commmit Message

Per suggestion from @PowerKiKi.

* Missed One DocBlock

Including it now.

Co-authored-by: Adrien Crivelli <adrien.crivelli@gmail.com>
@oleibman
Copy link
Collaborator

oleibman commented Dec 4, 2021

Documentation updated by 2350, as requested. Closing.

@oleibman oleibman closed this as completed Dec 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants