Skip to content

DateTime modify with unixtimestamp (@) must work like setTimestamp #9891

Closed

Description

@goncons

Description

The following code:

<?php
$a = new DateTime('2022-11-01 13:30:00', new DateTimezone('America/Lima'));
$b = clone $a;
echo '$a: ', $a->format(DateTime::ATOM), "\r\n";
echo '$b: ', $b->format(DateTime::ATOM), "\r\n";
echo '$a: @', $a->getTimestamp(), "\r\n";
echo '$b: setTimestamp(', $b->getTimestamp(), ")\r\n";
$a->modify('@' . $a->getTimestamp());
$b->setTimestamp($b->getTimestamp());
echo '$a: ', $a->format(DateTime::ATOM), "\r\n";
echo '$b: ', $b->format(DateTime::ATOM), "\r\n";

Resulted in this output:

$a: 2022-11-01T13:30:00-05:00
$b: 2022-11-01T13:30:00-05:00
$a: @1667327400
$b: setTimestamp(1667327400)
$a: 2022-11-01T18:30:00-05:00
$b: 2022-11-01T13:30:00-05:00

But I expected this output instead:

$a: 2022-11-01T13:30:00-05:00
$b: 2022-11-01T13:30:00-05:00
$a: @1667327400
$b: setTimestamp(1667327400)
$a: 2022-11-01T13:30:00-05:00
$b: 2022-11-01T13:30:00-05:00

PHP Version

PHP 8.1.12

Operating System

No response

Activity

  1. changed the title [-]DateTime modify with unixtimestamp (@) must works like setTimestamp[/-] [+]DateTime modify with unixtimestamp (@) must work like setTimestamp[/+] on Nov 4, 2022
  2. cmb69 commented on Nov 4, 2022

    @cmb69
    Member

    Hmm, the documentation says:

    The "Unix Timestamp" format sets the timezone to UTC.

    This seems to be a temporary measure only: https://3v4l.org/GR7Tc

    @derickr, can you please clarify?

  3. hormus commented on Nov 4, 2022

    @hormus

    Hi welcome @goncons when you use @ format you are using a UTC time zone, currently modify does not change the time zone. If timezone origin it's America/Lima modify add "Unix Timestamp" with @ format.

    DateTime::modify — Alters the timestamp

    The "Unix Timestamp" format sets the to UTC.
    https://www.php.net/manual/en/datetime.settimestamp.php

    timestamp
    Unix timestamp representing the date. Setting timestamps outside the range of int is possible by using [DateTimeImmutable::modify()](https://www.php.net/manual/en/datetimeimmutable.modify.php) with the @ format.
    

    substantially DateTime::__ construct, DateTime::modify and strtotime parse a date or a format, the construct if it is already present Unix Timestamp the second argument timezone not accepted.

    <?php
    
    $date = new DateTime('2022-01-01', new DateTimeZone('America/Lima'));
    $date = new DateTime('@' . $date->format('U'));
    echo $date->format('Y-m-d H:i:s e');
    echo "\r\n";
    $date->modify('2021-01-01 00:00:00 America/Lima');
    echo $date->format('Y-m-d H:i:s e'); // DateTime::modify Alters the timestamp, don't change timezone from UTC to America/Lima
    
    ?>

    Expected result:

    2022-01-01 05:00:00 +00:00
    2021-01-01 00:00:00 +00:00
    
  4. goncons commented on Nov 4, 2022

    @goncons
    Author

    Hi @hormus my point is that the Unix Timestamp it is always in UTC, but looks like the @ format is treating the timestamp as "local" Unix timestamp, see this example:

    <?php
    $dateTime = new DateTime('2022-11-01 13:30:00', new DateTimezone('America/Lima'));
    $unixTimestamp = time();
    echo "originalUnixTimestamp: $unixTimestamp\r\n";
    $dateTime->setTimestamp($unixTimestamp);
    echo 'setTimestampUnixTimestamp: ', $dateTime->format('U'), "\r\n";
    $dateTime->modify("@$unixTimestamp");
    echo 'modify@UnixTimestamp: ', $dateTime->format('U'), "\r\n";
    

    The expected result from my point of view must be:

    originalUnixTimestamp: 1667595977
    setTimestampUnixTimestamp: 1667595977
    modify@UnixTimestamp: 1667595977
    

    but the obtained result is:

    originalUnixTimestamp: 1667595977
    setTimestampUnixTimestamp: 1667595977
    modify@UnixTimestamp: 1667613977
    

    Why the @ format is changing the values of the timestamp ?.
    @cmb69 that comment in the documentation sounds weird, to me it sounds like the @ format changes the timezone of the instance of the DateTime class, that is not the case.

  5. hormus commented on Nov 4, 2022

    @hormus

    it is not a bug modify does not change timezone, the value is UTC obviously +5 hours in object created America/Lima -05:00
    i updated my previous comment inserted the explicit timezone in modify
    For me This documentation issue
    modify don't change timezone

    <?php
    
    $timestamp = 1667595977;
    //America/Lima -05:00 convert for Unix timestamp +05:00
    $date = new DateTime('now', new DateTimeZone('America/Lima'));
    $date->setTimestamp($timestamp); // $timestamp (UTC) automacamally convert for timezone America/Lima ex. $timestamp - (18000)
    echo $date->format('U');
    $date->modify('@' . $timestamp); // Set Unix timestamp $timestamp + timezone (18000)
    echo "\r\n";
    echo 18000 + ($timestamp) . ' = '  . $date->format('U');
    $date->modify('@' . 0); // Set Unix timestamp to 0 and timezone America/Lima -05:00
    echo "\r\n";
    $date->modify('+' . $timestamp . ' sec'); //Add seconds to America/Lima
    echo $date->format('U');
    
    ?>

    Expected result:

    1667595977
    1667613977 = 1667613977
    1667613977
    

    php 5.3.6 Absolute date/time statements now take effect. Previously, only relative parts were used.

  6. goncons commented on Nov 5, 2022

    @goncons
    Author

    @hormus If that is the intended behaviour then there is a "bug" in the documentation or the PHP/DateTime definition of Unix Timestamp for the @ format, bacause the Unix Timestamp is Always in UTC and the @ format is treating it like is a "local" timestamp (if you have to add or subtract the timezone offset).

  7. hormus commented on Nov 5, 2022

    @hormus

    Hi @goncons can you do an example of DateTime::modify which changes the timezone of how it was created?
    The Unix timestamp or @ format is the UTC time measurement, if the object uses an immutable timezone (for DateTime::modify) it makes sense to set Unix timestamp without calculating the timezone.
    Since exists DateTime::__construct or (DateTime::setTimestamp int) the current use of @ format inside the DateTime::modify function is not to be modified for two reasons 1) to avoid a useless check and 2) It exists since php 5.3.6 the change causes a break

    DateTime::__construct — Returns new DateTime object
    Note:

    The $timezone parameter and the current timezone are ignored when the $datetime parameter either is a UNIX timestamp (e.g. @946684800) or specifies a timezone (e.g. 2010-01-28T15:00:00+02:00).

    <?php
    date_default_timezone_set('UTC');
    $date1 = new DateTime('@0'); // UTC
    $date2 = new DateTime('now', new DateTimeZone('America/Lima'));
    $date2->modify('@0'); // php >= 5.3.6 Set Unix timestamp to 0
    var_dump($date1->format('Y-m-d\\TH:i:s\\Z'), $date2->format('Y-m-d\\TH:i:s\\Z')); // Output display UTC, Attention the forced output in UTC can differ from the input, if DST or if different from civil date
    ?>

    Epoch time (also known as the Unix time) 1970-01-01 00:00:00
    $date1 have UTC timezone and $date2 have America/Lima with same output display and different timezone offset (UTC +00:00 America/Lima -05:00 or -04:00 with DST active)

    the maintainer of the date @derickr will decide the desired behavior. I ask for a note for good code practice it is not recommended to enter the timezone as it is ignored by DateTime::modify if you want to change the timezone use DateTime::setTimeZone or if you use @ format use DateTime::__construct

  8. ciaranmcnulty commented on Dec 19, 2022

    @ciaranmcnulty

    This came up in discussion with @nicolas-grekas today.

    The only timezone that makes sense is UTC (unless there's some concept of 'timezoned timestamps' out there in the wild I've not come across)

    This is presumably why the constructor ignores timezones for these dates:

    $m = new DateTime('@1234567890', new DateTimeZone('Europe/Paris'));
    var_dump($m->getTimeStamp()); // int(1234567890)
    var_dump($m->getTimeZone()->getName()); // string(6) "+00:00" (not Europe/Paris)

    For me the only logical expectation would be that using modify() with the same string format would result in the same DateTime, but it does not:

    $m = new DateTime('now', new DateTimeZone('Europe/Paris'));
    $m->modify('@1234567890');
    var_dump($m->getTimeStamp()); // int(1234564290) (not 12345677890!)
    var_dump($m->getTimeZone()->getName()); // string(6) "Europe/Paris"

    Indeed the way the docs are structured, the formats are documented independently of the methods using them, and the relevant page states The "Unix Timestamp" format sets the timezone to UTC. without anything like not for modify

    To me this is clearly just a bug and the fix is obvious...

  9. derickr commented on Dec 20, 2022

    @derickr
    Member

    The bug is indeed that with modify() the timezone isn't reset, whereas it should. There is however no "obvious fix", nor trivial fix, for this.

    The "expected output" from the original report is also wrong though, the output should be:

    $a: 2022-11-01T13:30:00-05:00
    $b: 2022-11-01T13:30:00-05:00
    $a: @1667327400
    $b: setTimestamp(1667327400)
    $a: 2022-11-01T18:30:00+00:00
    $b: 2022-11-01T13:30:00-05:00
    

    As the @ format specifically says that the timezone is reset to UTC.

  10. added a commit that references this issue on Dec 20, 2022
    d19a70c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions