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
| Comment | File | Size | Author |
|---|---|---|---|
| #46 | core_2825845-46.patch | 1.95 KB | krzysztof domański |
| #23 | 2825845-demo.patch | 2.45 KB | mpdonadio |
| #7 | 2825845-vancouver-FAIL.patch | 941 bytes | xjm |
| #7 | 2825845-chicago-PASS.patch | 1.17 KB | xjm |
| #7 | 2825845-chicago-FAIL.patch | 1.25 KB | xjm |
Comments
Comment #2
xjm(Fixing nonsensical sentence in the summary.)
Comment #3
mpdonadioI 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.
Comment #4
xjmComment #5
xjmComment #6
xjmComment #7
xjmThese demonstrate the fail locally. Using Vancouver, in
setUp(), it appears the date is formatted as2016-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.Comment #8
xjmComment #9
xjmAh! In
setUp(), the timezone used to format the date isDATETIME_STORAGE_TIMEZONE(UTC), but in the test, it'sstatic::$timezone(Vancouver in HEAD). The comment above the second says:So that explains the apparent offset.
Comment #10
xjmAlso curious as to what happens if we do something like this.
Comment #15
mpdonadio@xjm, thanks for making this reproducible!
The patches in #10 won't help, because many/most classes get the time from something like
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.
Comment #16
xjmOkay #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. :)
Comment #17
mpdonadioTwo possible related issues.
Comment #18
mpdonadioOK, 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.
Comment #19
mpdonadioAppears to be default behavior in PHP, https://3v4l.org/q5400
Still digging.
Comment #20
mpdonadioHere 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.
Comment #23
mpdonadioFWIW, 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.
Comment #25
xjmNice, the unit test expresses the issue very clearly.
Comment #26
mpdonadioThought 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.
Comment #27
sugaroverflow commentedWe are triaging this: @cilefen
Comment #29
cilefen commentedI am crediting ourselves on the triage.
Comment #30
xjmToday @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.
Comment #31
xjmAlso of note: https://time.is/time_zone_news/chile_reintroduces_dst
Comment #32
xjmHm 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.
Comment #33
xjmThanks @sugaroverflow and @cilefen for helping triage this issue!
Comment #34
xjmRegarding 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.
Comment #35
mpdonadioThe 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.
Comment #36
mpdonadio@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
Comment #37
lendudeIt's back, but now in
\Drupal\datetime\Tests\Views\FilterDateTesthttps://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 samestatic::$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.
Comment #38
lendudeThis #2865992: Convert Datetime module Views tests to Kerneltest converts these tests to kernel tests.
Comment #40
xjmThese failed again on schedule:
https://www.drupal.org/pift-ci-job/774179
https://www.drupal.org/pift-ci-job/774166
Comment #41
xjm#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!
Comment #42
mpdonadioIf 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.
?
Comment #44
xanoHi 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.
Comment #46
krzysztof domańskiThe 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').
Comment #50
mandclu commentedBecause 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.
Comment #51
dwwRe: #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
Comment #52
krzysztof domański[edited]
Comment #53
krzysztof domańskiPer #30 back to major.
Comment #55
krzysztof domańskiThe
America/Vancouvertime 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.On October 31th because the tested date is set to the day after the
REQUEST_TIME(86400 seconds -> 1 day).Comment #56
krzysztof domańskiSee #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.
Comment #57
krzysztof domańskiI 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.Comment #58
krzysztof domańskiComment #59
mpdonadioI 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).
Comment #60
krzysztof domański1.
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.3. What when the clock hand is moved one hour forwards?
static::$date += 3600;?Comment #65
krzysztof domańskiIt 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.
Comment #67
znerol commentedAccording to #65 this is fixed in PHP. Closing.
Comment #68
xjmAdding to saved credits.