Skip to content

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

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

Closed
goncons opened this issue Nov 4, 2022 · 8 comments
Closed

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

goncons opened this issue Nov 4, 2022 · 8 comments

Comments

@goncons
Copy link

goncons commented Nov 4, 2022

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

@cmb69 cmb69 changed the title DateTime modify with unixtimestamp (@) must works like setTimestamp DateTime modify with unixtimestamp (@) must work like setTimestamp Nov 4, 2022
@cmb69
Copy link
Member

cmb69 commented Nov 4, 2022

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?

@hormus
Copy link

hormus commented Nov 4, 2022

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

@goncons
Copy link
Author

goncons commented Nov 4, 2022

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.

@hormus
Copy link

hormus commented Nov 4, 2022

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.

@goncons
Copy link
Author

goncons commented Nov 5, 2022

@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).

@hormus
Copy link

hormus commented Nov 5, 2022

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

@ciaranmcnulty
Copy link

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...

@derickr
Copy link
Member

derickr commented Dec 20, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
@derickr @ciaranmcnulty @cmb69 @goncons @hormus and others