Problem/Motivation

When the clock pointer is moved backwards, date formatting is incorrect.

\Drupal::service('date.formatter')->format($timestamp, 'custom', 'c', 'America/Vancouver')

Example

In the 'America/Vancouver' time zone, the clock pointer was moved back to November 4, 2018 at 2:00 am. When universal time indicates 2018-11-04T09:15:00+00:00 in the America/Vancouver time zone, we expect 2018-11-04T01:15:00-08:00. The incorrect date is returned (2018-11-04T01:15:00-07:00).

-07:00 => -08:00
Return: 2018-11-04T01:15:00-07:00 ('America/Vancouver')
Expected: 2018-11-04T01:15:00-08:00 ('America/Vancouver')

How to reproduce

Use 'c' format: Full time format (ISO 8601 date)

$timestamp = 1541315700; // 2018-11-04T07:15:00+00:00
for ($i = 1; $i <= 5; $i++) {
  $utc = \Drupal::service('date.formatter')->format($timestamp, 'custom', 'c', 'UTC');
  $vancouver = \Drupal::service('date.formatter')->format($timestamp, 'custom', 'c', 'America/Vancouver');
  $timestamp += 3600;
  echo $utc . ' ' . $vancouver;
}

Result (Drupal 8.6.3, PHP 7.2.11)

2018-11-04T07:15:00+00:00 2018-11-04T00:15:00-07:00
2018-11-04T08:15:00+00:00 2018-11-04T01:15:00-07:00
2018-11-04T09:15:00+00:00 2018-11-04T01:15:00-07:00 // Expected -08:00
2018-11-04T10:15:00+00:00 2018-11-04T02:15:00-08:00
2018-11-04T11:15:00+00:00 2018-11-04T03:15:00-08:00

See #7. Drupal\Core\Datetime\DateFormatter::format() returns incorrect result for format 'c' and 'I'.

Notice:The reason is a bug in PHP
new DateTime('now') return incorrect timezone during DST transitions and `DateTimePlus::createFromTimestamp()`.

It was fixed in PHP 8.1.7. Now it works in Drupal 10 which uses in PHP 8.1. See PHP requirements. It still doesn't work in the older version e.g. Drupal 9.5 and PHP 8.0.

DateTime doesn't handle the transition from Daylight Saving Time back to Standard Time correctly.

Bug #74274 Handling DST transitions correctly
Bug #77103 new DateTime('now') return incorrect timezone during DST transitions

Notice: The same problem in other zones in the Eastern Hemisphere ('Australia/Sydney' or 'Europe/Warsaw').

Proposed resolution

Fix it for Drupal 9.5 under PHP 8.1 or add only tests for Drupal 10.

Remaining tasks

Add test

Comments

Krzysztof Domański created an issue. See original summary.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

krzysztof domański’s picture

Title: Once a year, when the clock hand is moved backwards, the service date.formatter returns an incorrect result. » Once a year, when the clock hand is moved backwards, the service date.formatter returns an incorrect result

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

krzysztof domański’s picture

StatusFileSize
new2.79 KB
$time = strtotime('2020-11-01T06:15:00+00:00');
$timezone = "America/Vancouver";
$date_formatter = \Drupal::service('date.formatter');

echo 'Drupal\Core\Datetime\DateFormatter::format()<br>';
for ($i = 0; $i < 5; $i++) {
  $timestamp = $time + $i * 3600;
  echo $date_formatter->format($timestamp, 'custom', 'c', $timezone) . ' ' . $date_formatter->format($timestamp, 'custom', 'I', $timezone) . '<br>';
}
echo '\DateTime::format()<br>';
for ($i = 0; $i < 5; $i++) {
  $timestamp = $time + $i * 3600;
  $date = new \DateTime();
  $date->setTimestamp($timestamp);
  $date->setTimezone(new \DateTimeZone($timezone));
  echo $date->format('c') . ' ' . $date->format('I') . '<br>';
}

Returns

Drupal\Core\Datetime\DateFormatter::format()
2020-10-31T23:15:00-07:00 1
2020-11-01T00:15:00-07:00 1
2020-11-01T01:15:00-07:00 1
2020-11-01T01:15:00-07:00 1 // incorrect
2020-11-01T02:15:00-08:00 0
\DateTime::format()
2020-10-31T23:15:00-07:00 1
2020-11-01T00:15:00-07:00 1
2020-11-01T01:15:00-07:00 1
2020-11-01T01:15:00-08:00 0 // correct
2020-11-01T02:15:00-08:00 0
krzysztof domański’s picture

Issue summary: View changes

Drupal\Core\Datetime\DateFormatter::format() returns incorrect result for format 'c' and 'I'.

krzysztof domański’s picture

$timestamp = strtotime('2020-11-01T09:15:00+00:00');
$timezone = "America/Vancouver";
$date = new \DateTime();
$date->setTimestamp($timestamp);
$date->setTimezone(new \DateTimeZone($timezone));
echo 'setTimestamp before setTimezone<br>';
echo $date->format('c') . ' ' . $date->format('I') . '<br>';
$date = new \DateTime();
$date->setTimezone(new \DateTimeZone($timezone));
$date->setTimestamp($timestamp);
echo 'setTimestamp after setTimezone<br>';
echo $date->format('c') . ' ' . $date->format('I') . '<br>';

Returns

setTimestamp before setTimezone
2020-11-01T01:15:00-08:00 0 // correct
setTimestamp after setTimezone
2020-11-01T01:15:00-07:00 1 // incorrect

Perhaps the reason is in \Drupal\Component\Datetime\DateTimePlus::format().

      // Clone the date/time object so we can change the time zone without
      // disturbing the value stored in the object.
      $dateTimeObject = clone $this->dateTimeObject;
      if (isset($settings['timezone'])) {
        $dateTimeObject->setTimezone(new \DateTimeZone($settings['timezone']));
      }
      $value = $dateTimeObject->format($format);
mpdonadio’s picture

Title: Once a year, when the clock hand is moved backwards, the service date.formatter returns an incorrect result » DateTimePlus does not create dates with proper timestamps during DST transitions
Status: Active » Needs review
Issue tags: +Needs issue summary update
StatusFileSize
new2.42 KB
new3.59 KB

This sample shows it better:


$time = strtotime('2020-11-01T06:15:00+00:00');
$timezone = 'America/Vancouver';
$date_formatter = \Drupal::service('date.formatter');

print 'Drupal\Core\Datetime\DateFormatter::format()' . PHP_EOL;
for ($i = 0; $i < 5; $i++) {
  $timestamp = $time + $i * 3600;
  print $timestamp . ': ' . $date_formatter->format($timestamp, 'custom', 'U c e I', $timezone) . PHP_EOL;
}

print '\DateTime::format()' . PHP_EOL;
for ($i = 0; $i < 5; $i++) {
  $timestamp = $time + $i * 3600;
  $date = new \DateTime();
  $date->setTimestamp($timestamp);
  $date->setTimezone(new \DateTimeZone($timezone));
  print $timestamp . ': ' . $date->format('U c e I') . PHP_EOL;
}

Looks like the problem is https://bugs.php.net/bug.php?id=77103 and `DateTimePlus::createFromTimestamp()`.

My local setup is a little busted, but I think this will fix it. DateFormatterTest and DateTimePlusTest both pass locally.

The last submitted patch, 10: 3018996-10-test-only.patch, failed testing. View results

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

krzysztof domański’s picture

Looks good.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

krzysztof domański’s picture

Retested, unexpectedly test only pass on D10. See https://www.drupal.org/pift-ci-job/2565225

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Could this issue be fixed in D10?

As pointed out the test-only passed on 9.4 and up.

NW for the issue summary but not an issue please close out or find the issue it was fixed in so we can move over credit.

krzysztof domański’s picture

1/ PHP Bug #77103 new DateTime('now') return incorrect timezone during DST transitions was fixed in PHP 8.1.7. It should work in Drupal 10. See PHP requirements.

2/ The tests from https://www.drupal.org/files/issues/2020-11-29/3018996-10.patch still make sense

3/ Not sure if the fix should be added. However D9.5 with old PHP 7 will not work without fix.
https://www.drupal.org/node/3060/qa

krzysztof domański’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update
smustgrave’s picture

Status: Needs review » Needs work

Brought this up with @catch to see how best to proceed

If this is fixed in php8 we should add the test coverage still, just the tests

Then we think about backporting the full patch to 9.5.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alexpott’s picture

This is not fixed in the versions of PHP we're using.... the test is failing today :) https://git.drupalcode.org/issue/drupal-229778/-/pipelines/646294/test_r...

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.