Problem/Motivation

See: https://www.drupal.org/pift-ci-job/523337

Note the time of the test run. It's displayed to me as:
Created November 5, 2016 - 04:02. jibran added test. Updated November 5, 2016 - 05:00.

My timezone is CST, which would make this 2:00a to 3:00a PST. When we add one day, as the test does, that's the official time when DST ended in the US.

From @jhedstrom:

the timezone for that test is set to `America/Vancouver`, so if they have daylight savings time, and the test was running at exactly the crossover, that could explain a failure...

This might be a bug that could occur only under rare circumstances, but whatever bug introduces test failures in HEAD at the least, and who knows what else could happen if there is an actual DST bug. So splitting the difference for issue priority at major, at least until we determine an underlying cause and whether it's related to DST or not.

2020
The America/Vancouver time zone changed from daylight savings to standard time at 2:00 AM on Sunday, Nov 1, 2020. The GMT offset is currently UTC/GMT -8 hours (PST).

https://www.drupal.org/pift-ci-job/1868691
Created 31 Oct 2020 at 10:32 CET. Krzysztof Domański added custom test. Updated 31 Oct 2020 at 11:27 CET.
https://www.drupal.org/pift-ci-job/1868692
Created 31 Oct 2020 at 10:32 CET. Krzysztof Domański added custom test. Updated 31 Oct 2020 at 11:27 CET.

timestamp, UTC, America/Vancouver
1604218500, 2020-11-01T07:15:00+00:00, 2020-11-01T00:15:00-07:00
1604222100, 2020-11-01T08:15:00+00:00, 2020-11-01T01:15:00-07:00
1604225700, 2020-11-01T09:15:00+00:00, 2020-11-01T01:15:00-07:00 // expected -08:00
1604229300, 2020-11-01T10:15:00+00:00, 2020-11-01T02:15:00-08:00
1604232900, 2020-11-01T11:15:00+00:00, 2020-11-01T03:15:00-08:00

Proposed resolution

Once a year, when the clock hand is moved backwards, it is not possible to distinguish between the first hour before (2020-11-01T01:15:00-07:00) and after (2020-11-01T01:15:00-07:00) the time change. The reason is a bug in PHP. See #3018996: DateTimePlus does not create dates with proper timestamps during DST transitions.

+    // Change variable $date, in the first hour after the time change. When you
+    // move the clock back, the time is the same again. However, it is treated
+    // as a date before the change. In such a situation, comparing it with the
+    // universal time ('UTC') causes errors.
+    // https://www.drupal.org/project/drupal/issues/2825845
+    $a = $formatter->format(static::$date, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, static::$timezone);
+    $b = $formatter->format(static::$date - 3600, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, static::$timezone);
+    if ($a == $b) {
+      static::$date -= 3600;
+    }

Remaining tasks

TBD

User interface changes

N/A

API changes

TBD

Data model changes

Comments

xjm created an issue. See original summary.

xjm’s picture

Issue summary: View changes

(Fixing nonsensical sentence in the summary.)

mpdonadio’s picture

Assigned: Unassigned » mpdonadio

I have two proof-of-concepts for this in my head that I will post later to see if this could be the one-in-a-million fail from a test running during the DST switch. (one is to change the TZ during the test, the other is to sledgehammer REQUEST_TIME to be the same as when that fail ran...).

At a minimum, though, we could update this test do the timezone loop that we use in other places.

xjm’s picture

xjm’s picture

xjm’s picture

xjm’s picture

Status: Active » Needs review
StatusFileSize
new941 bytes
new1.17 KB
new1.25 KB

These demonstrate the fail locally. Using Vancouver, in setUp(), it appears the date is formatted as 2016-11-06T09:02:00, but in _testExact(), it is calculated as 2016-11-06T01:02:00, eight hours earlier. The offset for Vancouver during non-DST is -0800.

If I only change the timezone to America/Chicago, it passes. But if I also make the request time 2h earlier, so that the test date is when DST would have been ending in Chicago instead, it again ails, this time with a six-hour difference instead of eight, which corresponds to the -0600 offset for Chicago.

So it looks like maybe we have a DST bug related to the date formatter service, where somehow it gives a UTC time the second time around?

It's also worth noting that I've commented out the _testOffset() and _testBetween() methods in the attached to avoid confusion, but I also see fails with _testOffset() for this timestamp locally.

xjm’s picture

Issue summary: View changes

 

xjm’s picture

Ah! In setUp(), the timezone used to format the date is DATETIME_STORAGE_TIMEZONE (UTC), but in the test, it's static::$timezone (Vancouver in HEAD). The comment above the second says:

// Use the date from node 3. Use the site timezone (mimics a value entered  
// through the UI).        

So that explains the apparent offset.

xjm’s picture

Also curious as to what happens if we do something like this.

The last submitted patch, 10: 2825845-request-time-pacific.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 10: 2825845-request-time-eastern.patch, failed testing.

The last submitted patch, 7: 2825845-vancouver-FAIL.patch, failed testing.

The last submitted patch, 7: 2825845-chicago-FAIL.patch, failed testing.

mpdonadio’s picture

@xjm, thanks for making this reproducible!

The patches in #10 won't help, because many/most classes get the time from something like

$request_time = $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');

The problem is that Drupal\datetime\Plugin\views\filter\Date is using a different timezone offset than the test. It is either because of a rollover during the test, or because REQUEST_TIME in the test is different than $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') in the views plugin. I know what is happening, but not 100% why (it's lines 115-116 of Date::opSimple).

We either touched those lines of code recently, or have an in-progress patch about this right now. Trying to find the related issue.

Actively working on a fix.

xjm’s picture

Okay #10 is not going to work as a way of demonstrating the bug, because it causes 403s everywhere. But #7 is pretty clear.

Edit: Crosspost, yep. :)

mpdonadio’s picture

mpdonadio’s picture

OK, this is a a weird one that I am still unravelling.

1478422920 is 2016-11-06T09:02:00 in UTC.

2016-11-06T09:02:00 in UTC is 2016-11-06T01:02:00 in America/Vancouver.

1:02am in Vancouver happened twice: before the DST switch, and then again after the fall back.

This is what is happening. The test simulates the user entering 2016-11-06T01:02:00 in Vancouver for the node value via the timestamp value (the formatter service wants timestamps). This goes into storage as 2016-11-06T09:02:00. The test simulates the user entering 2016-11-06T01:02:00 in Vancouver for the filter. This gets converted to the timestamp 1478419320, which is 2016-11-06T08:02:00 in UTC.

In other words, the timestamp we are operating at is the one after the DST switch. The string 2016-11-06T01:02:00 is getting converted to the one before the DST switch. Test gets confused and fails.

I am debugging this against #2627512-139: Datetime Views plugins don't support timezones, thinking that would solve it (uh, it doesn't). Will reroll my debug patch against HEAD and post tomorrow so everyone can cry with me. Will also investigate why this process isn't symmetric.

mpdonadio’s picture

Appears to be default behavior in PHP, https://3v4l.org/q5400

Still digging.

mpdonadio’s picture

Status: Needs work » Needs review
StatusFileSize
new2.39 KB

Here is the problem expressed as a unit test, because that is what developers do.

I think we need to expressly thread the timezone offset through when doing UI (not just the timezone name). Also need to do some manual testing (or add test coverage) to see if this happens elsewhere, both during DST spring-forward and fall-back.

Status: Needs review » Needs work

The last submitted patch, 20: 2825845-demo.patch, failed testing.

The last submitted patch, 20: 2825845-demo.patch, failed testing.

mpdonadio’s picture

Status: Needs work » Needs review
StatusFileSize
new2.45 KB

FWIW, manually testing datetime fields seems to work. We should be able to update DateTimeFieldTest and DateRangeFieldTest to cover it. The method is basically the same as the date-only hunk where we save off the same field values a few times and make sure they don't change and make sure they render out properly.

Status: Needs review » Needs work

The last submitted patch, 23: 2825845-demo.patch, failed testing.

xjm’s picture

Nice, the unit test expresses the issue very clearly.

mpdonadio’s picture

Thought about this some more. I think if we can get #2648950: [PP-2] Use form element of type date instead textfield when selecting a date in an exposed filter and #2799987: Datetime and Datelist element's timezone handling is fragile, buggy and wrongly documented in, then this will become a non-issue, as the TZ is part of the \Drupal\Core\Datetime\Element\Datetime form element. We would just need to adjust FilterDateTimeTest to properly simulate the TZ handling, or do it as a proper integration test rather a kernel-ish test.

sugaroverflow’s picture

Version: 8.2.x-dev » 8.4.x-dev
Issue tags: +Triaged for D8 major current state, +DrupalCampNJ2017

We are triaging this: @cilefen

  • The issue summary makes sense.
  • Normally random test failures are critical, but as mentioned in the issue summary this happens rarely (only twice a year).
  • We made sure the patch still applies to 8.4.x-dev so this doesn't need a re-roll.

cilefen credited cilefen.

cilefen’s picture

I am crediting ourselves on the triage.

xjm’s picture

Title: Unexpected test failure in FilterDateTimeTest » DST-related test failures in FilterDateTimeTest, UserTimeZoneTest
Issue summary: View changes
Issue tags: +Triaged core major

Today @catch, @alexpott, @cilefen, @Cottser, and I agreed that this is a major issue. We discussed whether to downgrade it further because it only will happen once or twice a year, but it was disruptive at the time that it happened, so we agreed to keep it major.

https://www.drupal.org/pift-ci-job/617892 just happened and also looks like it might be DST-related. Not sure if it is caused by the same code path or not.

xjm’s picture

xjm’s picture

Title: DST-related test failures in FilterDateTimeTest, UserTimeZoneTest » DST-related test failures in FilterDateTimeTest
Issue summary: View changes

Hm I actually doubt https://www.drupal.org/pift-ci-job/617892 was actually related, it's still more than a day too early to have cross-continental DST issues.

xjm’s picture

Thanks @sugaroverflow and @cilefen for helping triage this issue!

xjm’s picture

Regarding the user timezone failure, it also happened in https://www.drupal.org/pift-ci-job/617894, so it's possible that a recent PHP commit regressed Chile somehow. I'll file a separate issue for that test if it fails again tomorrow.

mpdonadio’s picture

The 2017a tzdb introduced a change to Chile, but I forget the details. I'm thinking we need to add a message in system_requirenents to say what database is being used, and also log it in one of the date related tests.

mpdonadio’s picture

@cilefen pointed me to this today, which I had forgotten about: http://symfony.com/blog/new-in-symfony-2-8-clock-mocking-and-time-sensit...

I'm wondering if with the push to BTB, and more of the testing framework leveraging PHPUnit, we can see if can leverage any of this to stabilize the time / DST related randoms

lendude’s picture

It's back, but now in \Drupal\datetime\Tests\Views\FilterDateTest https://www.drupal.org/pift-ci-job/636971 from #2858159: The "User has a revision" views argument handler generates a SQL error. Different test, but uses the same static::$date = REQUEST_TIME; logic, so I guess it would run into the same type of fails.

If moving to PHPUnit helps, these tests have no business being a webtest anyway, they never use the browser, so we should convert them to a kernel test for the PHPUnit Initiative anyway.

lendude’s picture

This #2865992: Convert Datetime module Views tests to Kerneltest converts these tests to kernel tests.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

xjm’s picture

#2915664: Sites installed by InstallerTestBase should have a timezone of 'Australia/Sydney' may have resolved this. It'd be exciting to see if our demo patch passes!

mpdonadio’s picture

If we know the forward/backwards conversion during the hour of DST change is the problem (see #23), can we just wrap the failing assertion with a check to not run it during this hour? Maybe even explicitly skip (not sure if phpunit lets you skip assertions and not whole test methods). We would still catch a regression the next day.

?

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xano’s picture

Hi all! I dug through this yesterday and today, and I think I have finally come to a conclusion: Named time zones are ambiguous during the periods clocks are adjusted backward, as we have no way to find the exact UTC offset using just the date-time (no time zone info) and the named time zone such as America/Vancouver, which during that 1 hour most clocks are adjusted when DST ends, can be one of two UTC offsets. This would explain why we did not see any failures last weekend when the EU switched to DST, and I predict we won't see any when North America starts DST soon either, since no date-times will be repeated or ambiguous.

Apart from extending date-times with time zone offsets (needed to disambiguate named time zones), I'm not sure if there is any solution to this.

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

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

krzysztof domański’s picture

Assigned: mpdonadio » Unassigned
Status: Needs work » Needs review
Related issues: +#3018996: DateTimePlus does not create dates with proper timestamps during DST transitions
StatusFileSize
new1.95 KB

The case of errors caused by changing the time one hour back will be repeated systematically.

2017

Created 4 Nov 2017 at 09:20 UTC. mairi posted patch. Updated 4 Nov 2017 at 10:12 UTC.
https://www.drupal.org/pift-ci-job/803185

2018

Created 3 Nov 2018 at 08:55 UTC. Automatic re-testing. Updated 3 Nov 2018 at 09:48 UTC.
https://www.drupal.org/pift-ci-job/1111364

Created 3 Nov 2018 at 09:55 UTC. Automatic re-testing. Updated 3 Nov 2018 at 10:43 UTC.
https://www.drupal.org/pift-ci-job/1111369

Created 3 Nov 2018 at 09:25 UTC. Automatic re-testing. Updated 3 Nov 2018 at 10:22 UTC.
https://www.drupal.org/pift-ci-job/1111365

It is wise to omit the first hour after the time change when the clock hand is moved backwards from the tests. This is necessary because of the confirmed effect on automatic tests.

In such cases it is not possible to distinguish between the first hour before and after the time change.

#3018996: DateTimePlus does not create dates with proper timestamps during DST transitions

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

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.

mandclu’s picture

Because of the suggestion this is what is preventing the test for #2868014: [PP-1] Views Date Filter Datetime Granularity Option from passing, it would be great to see this resolved.

dww’s picture

Issue tags: +Bug Smash Initiative

Re: #50: Sadly, this patch doesn't help the tests in #2868014 (see #2868014-95: [PP-1] Views Date Filter Datetime Granularity Option). Still probably worth landing this in its own right, but unfortunately not because it moves that one forward. :(

I haven't totally wrapped my head around this issue, so I'm not going to bump to RTBC.

It does seem like a bit of magic for the test itself to have this much conditional logic. Perhaps this would be better if that conditional logic lived in the formatter itself, and then have tests that show it does the right thing, even when "now" is in the middle of the weird periods of DTS fallback? Or something. Need to read the whole discussion here and dig deeper to really have a clear opinion...

Thanks/sorry,
-Derek

krzysztof domański’s picture

Priority: Major » Normal
Issue tags: -Triaged for D8 major current state, -Triaged core major

[edited]

krzysztof domański’s picture

Priority: Normal » Major

Per #30 back to major.

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

The America/Vancouver time zone changed from daylight savings to standard time at 2:00 AM on Sunday, Nov 1, 2020. The GMT offset is currently UTC/GMT -8 hours (PST).

The tests failed again https://www.drupal.org/pift-ci-job/1868691

Created 31 Oct 2020 at 10:32 CET. Krzysztof Domański added custom test. Updated 31 Oct 2020 at 11:27 CET.

Failed asserting that two arrays are identical.
--- Expected
+++ Actual
@@ @@
-Array &0 (
-    0 => Array &1 (
-        'nid' => '4'
-    )
-)
+Array &0 ()

On October 31th because the tested date is set to the day after the REQUEST_TIME (86400 seconds -> 1 day).

static::$date = REQUEST_TIME + 86400;

// Set the timezone.
date_default_timezone_set(static::$timezone);
$this->config('system.date')
  ->set('timezone.default', static::$timezone)
   ->save();

// Add some basic test nodes.
$dates = [
  '2000-10-10T00:01:30',
  '2001-10-10T12:12:12',
  '2002-10-10T14:14:14',
  // The date storage timezone is used (this mimics the steps taken in the
  // widget: \Drupal\datetime\Plugin\Field\FieldWidget::messageFormValues().
  \Drupal::service('date.formatter')->format(static::$date, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE),
];
krzysztof domański’s picture

See #3018996: DateTimePlus does not create dates with proper timestamps during DST transitions. In such cases it is not possible to distinguish between the first hour before and after the time change. The reason is a bug in PHP. I don't expect that issue to be resolved in the coming years :( so IMO #46 makes sense to go.

krzysztof domański’s picture

I found two such cases this year. I ran these tests at the wrong hour... :(
https://www.drupal.org/pift-ci-job/1868692
Created 31 Oct 2020 at 10:32 CET. Krzysztof Domański added custom test. Updated 31 Oct 2020 at 11:32 CET.

https://www.drupal.org/pift-ci-job/1868691
Created 31 Oct 2020 at 10:32 CET. Krzysztof Domański added custom test. Updated 31 Oct 2020 at 11:27 CET.

krzysztof domański’s picture

Issue summary: View changes
mpdonadio’s picture

+++ b/core/modules/datetime/tests/src/Kernel/Views/FilterDateTimeTest.php
@@ -38,6 +38,20 @@ protected function setUp($import_test_views = TRUE) {
+    // Change variable $date, in the first hour after the time change. When you
+    // move the clock back, the time is the same again. However, it is treated
+    // as a date before the change. In such a situation, comparing it with the
+    // universal time ('UTC') causes errors.
+    // https://www.drupal.org/project/drupal/issues/2825845
+    $a = $formatter->format(static::$date, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, static::$timezone);
+    $b = $formatter->format(static::$date - 3600, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, static::$timezone);
+    if ($a == $b) {
+      static::$date -= 3600;
+    }
+

I think something less fragile is to the use 'I' format (see https://www.php.net/manual/en/datetime.format.php) and check whether the test is in the middle of the DST fallback. ($a would be 0 and $b would be 1).

krzysztof domański’s picture

Status: Needs review » Needs work

1. Drupal\Core\Datetime\DateFormatter::format() returns incorrect result for format 'c' and 'I'.
See #3018996: DateTimePlus does not create dates with proper timestamps during DST transitions.

2. Instead of Drupal\Core\Datetime\DateFormatter::format() we need \DateTime::format(); here.

$date = new \DateTime();
$date->setTimestamp($timestamp);
$date->setTimezone(new \DateTimeZone("America/Vancouver"));
$custom = $date->format('c');
$isDST = $date->format('I');

3. What when the clock hand is moved one hour forwards? static::$date += 3600;?

+    if ($a == $b) {
+      static::$date -= 3600;
+    }

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.

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

It was fixed in PHP 8.1.7. Now it works in Drupal 10 and PHP 8.1.7. It still doesn't work in the older version e.g. Drupal 9.5 and PHP 8.0. See #3018996: DateTimePlus does not create dates with proper timestamps during DST transitions.

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.

znerol’s picture

Status: Needs work » Closed (outdated)

According to #65 this is fixed in PHP. Closing.

xjm’s picture

Adding to saved credits.