Problem/Motivation
As part of removing procedural code in includes, we should move drupal_get_user_timezone() from bootstrap.inc to a service in system module.
This process has uncovered the following:
- There is a dependency of core on system module in Drupal\Core\EventSubscriber\AuthenticationSubscriber and Drupal\Core\Session\AccountProxy
- There is a circular dependency in 'current_user' service: AccountProxy::setAccount() -> drupal_get_user_timezone() -> \Drupal::currentUser();
- In AccountProxy::setAccount() we are calling date_default_timezone_set(drupal_get_user_timezone()) which relies on a kind of global state that other code relies on.
Proposed resolution
- Create an event listener in system module which sets the timezone based on site and user configuation using the standard date_default_timezone_set().
- Replace usages of drupal_get_user_timezone() with date_default_timezone_get().
- This also includes a fix for a timezone bug that was exposed by having a more reliable active-timezone switch that did not work in some cases before and was therefor almost impossible to test and now the tests simply failed. See #68 and #2861986: Plus one day on Date only fields with custom display widget for pacific/auckland timezone
Remaining tasks
Do it
User interface changes
none
API changes
drupal_get_user_timezone() is deprecated.
Constructor and implementation changes for AccountProxy that are very hard to have 100% BC because there was previously no constructor and we know of at least one contributed module that completely overrides several methods and that will either break or cause broken timezone behavior. The maintainers were notified and plan to rewrite their code: https://github.com/acquia/commerce-manager/issues/117.
Data model changes
none
Release notes snippet
Drupal no longer uses its own function for getting the default time zone, and now relies on the PHP function date_default_timezone_get().
The logic for setting the default timezone has been moved to events that is handled by the TimeZoneResolver which uses date_default_timezone_set().
The events are:
- A request via KernelEvents::REQUEST
- When time zone config changes, via ConfigCrudEvent
- When a user is set via a a new AccountSetEvent
The TimeZoneResolver handles these events and uses the same fall back logic prior to 8.7.0:
- Use the user-specified timezone, or if not set
- Use the site default configured timezone, or if not set
- Use the system default timezone
Comment | File | Size | Author |
---|---|---|---|
#127 | 3009377-127-interdiff.txt | 1.17 KB | kim.pepper |
#127 | 3009377-127.patch | 33.4 KB | kim.pepper |
Comments
Comment #2
kim.pepperI've put this in system module. Not sure if it should be in a sub-dir or not?
Comment #4
larowlanGreat work!
sob, singleton access in a value object constructor. this is why we can't have nice things
We almost need a factory service for date time objects (follow up) or a follow up to deprecate this codepath so that in d9 we can make the timezone required.
In core the only place calling it without a timezone is in a test.
we can inject these
c/p?
and this one can be injected too
Comment #5
kim.pepperThanks @larowlan. I expect there to be a few tests breaks after making these changes, but here goes...
Comment #7
kim.pepperHmm. Seems almost every test fail is due to needing to depend on system module now.
Comment #8
kim.pepperAh, it's actually because of...
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The service "authentication_subscriber" has a dependency on a non-existent service "system.user_timezone_handler".
'authentication_subscriber' is in core.services.yml and is trying to inject 'system.user_timezone_handler'
Comment #9
larowlanSo its a bit of rats' nest you're unravelling here, and basically you've discovered a hidden dependency from core to the system module.
I'm thinking we'll need a default timezone handler in core namespace, that doesn't support configuration options and then a service provider in system module that swaps out the default implementation for the configurable one - thoughts?
Great work!
Comment #10
kim.pepperYep that makes sense. I think there are a few usages which actually just want the default timezone but are calling drupal_get_user_timezone() for convenience (see \Drupal\Tests\Core\Session\AccountProxyTest)
Comment #11
kim.pepperI have moved the interface and a default system implementation to \Drupal\Core\Datetime\SystemTimeZoneHandler then injected that into \Drupal\system\UserTimeZoneHandler
For anything under core/lib, I've switched to using a new 'date.timezone_handler' service. I've put @todo's next to the core/lib usages which I think were incorrectly depending on system module.
Let's see what breaks.
Comment #13
kim.pepperHmm.
Comment #14
kim.pepperI've sidestepped the circular dependency by calling \Drupal::service('date.timezone_handler')->getTimeZone() in AccountProxy instead of injecting the service.
I also created SystemServiceProvider to replace the simple core 'date.timezone_handler' with the configurable one in the system module.
Comment #16
kim.pepperFixes SqlTest fails.
I'm still perplexed by why we are setting this here? This is like setting a global variable that other code depends on. It would be better for that code to use the timezone service?
I've removed it to see what breaks. :-)
Comment #18
kim.pepperUpdating issue summary with research done so far.
Comment #19
larowlanI'm +1 for the resolver approach, have asked @alexpott and @catch their thoughts too.
Comment #20
alexpottA few thoughts...
Removing this feels tricky. Is this responsible for setting the system timezone correctly?
I think we set the timezone in tests so this can probably assert against an expected value rather than NotNull().
Whilst views is probably dependent on user - should this be called timeZoneHandler?
Comment #21
catchI don't think the core/system divide is a fiction of our own making, we're still in the process of removing direct, hard, circular dependencies between things like the extension system, installer, and system module itself.
https://api.drupal.org/api/drupal/core%21includes%21install.inc/function... (still a hard dependency)
https://api.drupal.org/api/drupal/core%21modules%21system%21system.modul... (only just deprecated in 8.6.x).
Having said that, configuration objects which might or might not exist and are handled gracefully if they aren't are a bit different to having to install a module in order to have a functional module installation system, so don't have a massively strong opinion on 2 vs. 3 services.
I also wonder though, why not move the configuration to core like config.extension.yml? i.e. core.timezone.yml or similar?
Comment #22
andypostComment #23
andypostComment #24
dawehnerOne thing which confuses me is that we still have the actual configuration in system module. Given the timezone seems essentially for booting up Drupal, couldn't we place the configuration file inside core itself, and system module just provides a UI for it? This way we would have to have one less service involved.
System module for me its just a excuse for things which could be part of core anyway.
Comment #25
kim.pepperI'm still not sure how we resolve the circular dependency in the 'current_user' service:
AccountProxy::setAccount() calls drupal_get_user_timezone() which calls \Drupal::currentUser();
Comment #26
kim.pepperComment #27
kim.pepperWorked with @larowlan on this, and came up with an alternative approach:
Comment #29
kim.pepperComment #31
kim.pepperI reverted the ConfigEvents::SAVE events as it seemed to cause other issues.
I've added DependencySerializationTrait to AccountProxy as that was the cause of a few fails.
Comment #33
andypostThe approach looks nice and reminds me how we did configurable language manager
Comment #34
BerdirLets make sure we are profiling this, because we are adding quite a bit of stuff/services of something that happens pretty early in bootstrap on all (authenticated?) requests.
Comment #35
kim.pepperFixes the DateTime test.
Re #34 Good idea! Any tips?
Comment #37
kim.pepperTrying again...
Comment #38
kim.pepperRe: #24 my profiling on Blackfire.io didn't show any significant difference. https://blackfire.io/profiles/compare/683295ae-0fc4-4b44-9c67-d1c85868fe...
Comment #39
kim.pepperI realised I profiled with xdebug enabled. Here's a diff with xdebug disabled. I still don't see any significant difference https://blackfire.io/profiles/compare/4c443cc0-46f5-4e5f-9902-dd9ac93ee5...
Comment #40
BerdirYes, profiling this low-level stuff is tricky, the first link had huge differences (20%) but as you said, there was xdebug and also quite a few other changes that seem to be irrelevant, like different amount of queries. However, the second link doesn't have any difference at all, the amount of calls is identical, so that doesn't seem right either.
Comment #41
kim.pepperI seem to have borked my local Blackfire setup. If anyone else feels like posting profiling data, please go ahead.
Comment #42
kim.pepperOK. I think I got it sorted. Here's another comparison @Berdir Let me know if this one makes sense https://blackfire.io/profiles/compare/93320c35-fd05-44c6-a0d0-f66fb7b284...
Comment #43
BerdirAs discussed, that result still didn't show any call differences.
I did now try it on a extremely minimal route, I picked system.timezone for that, specifically this request: http://d8/system/timezone/nzdt/-1/1.
The blackfire extension somehow misbehaved on that page, probably because of the way my firefox shows the json data there. So I did the request on the console, as anon as well as with a cookie for uid 1. I had the page cache disabled on purpose.
In all cases, the patch was between 6-8% faster but we're talking about response times of 21-23ms, so the difference is 1-2ms.
https://blackfire.io/profiles/compare/65cf67d3-7012-4a10-876a-ea343fc6e6...
Things that we can see
* 3 more classes being loaded
* However, we also save a bunch of things, one event subscriber goes away and with it the call to drupal_get_user_timezone and its \Drupal::config() call.
this->currentUser is a required constructor argument, so I don't think it can ever be NULL?
However, I would suggest we move the is authenticated check first and also only inject the config factory and not already load the config.
The advantage is that we need the config load call only if the user is actually authenticated
The thing that I'm not fully convinced about is whether we really need this to be a tagged service. That is a pattern that is useful if we have to expect multiple service providers that have different weights and on different requests, different services that act on it. In this case, it seems very unlikely that contrib will add additional implementations, it's really just a pattern to decouple core and system and in this case, the pattern that the LanguageManager/ConfigurableLanguageManager are using seems like a better match. That would mean we'd just have a single service in core and we replace/override that in the system module, either by just redefining it in system.services.yml (still not sure how reliable that is) or using ServiceProvider.
Personally, I don't really think that we would even need that level of decoupling, I agree that there is a distinction between system.module and core, but 90% of that is for historical reasons, e.g. all the configuration in system.module is mostly just there because the ability to have config in the core namespace (and especially config entities) only came pretty late in the D8 development cycle if I remember correctly. And the long-term goal is IMHO to move things *out* of it and not adding more to it, but that's just me :) And I'm OK with this approach if it means everyone else is OK with it too.
Comment #44
Berdir> The advantage is that we need the config load call only if the user is actually authenticated
Correction: That is wrong as the else case also reads from configuration, so we do always need it.
There *would* be a way to avoid the config read at runtime for the non-configurable case (which I suppose is 90% of the sites?), if we would handle this in a similar way as LanguageServiceProvider does with some of the language configuration. Read it in the service provider and then set it there on the container/service, could be two constructor arguments for example, so at runtime, we'd only need to do something like this:
I'm not quite sure about that, the downside is that we need a config listener that forces a container rebuild if either of those two settings change. Similar to the check in \Drupal\language\Entity\ConfigurableLanguage::postSave(). On the other side, doing that could actually even result in a performance *improvement* compared to HEAD (small, but every config we *don't* have to read during bootstrap is a good thing) which would be an additional argument for getting this committed compared to just being able deprecate an old include file function. And I guess that 99% of the sites out there never change that setting anymore after the initial configuration.
Comment #45
kim.pepperThanks for the review.
> this->currentUser is a required constructor argument, so I don't think it can ever be NULL?
Fixed.
I think the logic in \Drupal\Core\Datetime\TimeZoneResolver::resolveTimeZone() is the important design change. We're iterating through providers, calling setting date_default_timezone_set($timeZone). All other code is then using date_default_timezone_get() instead of drupal_get_user_timezone().
TimeZoneResolver is also an event listener, so can be triggered to reset it if something changes.
I'm not sure if the ServiceProvider pattern handles that without needing additional logic for handling events?
Comment #47
kim.pepperDrupalCI hiccup. Back to needs review.
Comment #48
Berdir> I think the logic in \Drupal\Core\Datetime\TimeZoneResolver::resolveTimeZone() is the important design change. We're iterating through providers, calling setting date_default_timezone_set($timeZone). All other code is then using date_default_timezone_get() instead of drupal_get_user_timezone().
Yes, I get this part, my proposal would by to simply call this->getTimezone() or so and then system.module would replace the service with its own class that extends the default implementation and overrides getTimezone(). The advantage is that we don't need (yet another) service tag collection process, weight handling/sorting and calling out to other services.
Nothing would change with the events that class implements.
The tagged service approach has to instantiate 3 services (and load the interface), a simple service that's replaced by system.module would have to load just one (+ the interface).
Comment #49
andypost@Berdir your implementation is what I supposed in #33 btw it will require to rebuild container on setting (config) change (like locale doing)
OTOH this change will affect caching - if no configurable time zones allowed we get less cache variations
Comment #50
kim.pepperI think I've got the approach @Berdir was suggesting.
This doesn't require a ServiceProvider because there is no core implementation to override.
Let's see what the bot says!
Comment #52
alexpottWhilst there is no way to set it via the UI there is code to suggest that a NULL is possible and it is not a explicitly required base field. The original code check if $user->getTimeZone() returns a value. I think we should continue to do that here and open a follow-up to discuss the required-ness. Also see
\Drupal\user\UserInterface::TIMEZONE_EMPTY
(apparently dead code) - ah no - bad deprecated code :( and system_user_login()Comment #53
kim.pepperThanks @alexpott I've re-added the NULL user timezone check, and replaced a couple of deprecated usages.
Comment #54
BerdirHm, so no we got rid of the interface completely, that did go one step further than I imagined :)
Not sure if we should keep that or not. Also, now that we no longer have a non-configurable implementation, does it make sense to name this one "Configurable..."?
Now you merged it together completely, I would still keep a separate protected method that returns the value, then it would still be an option to sublclass if someone wants to do something crazy.
My (crazy) idea about keeping the config value as a pre-calculated service argument would still require a ServiceProvider, but we could still consider that in a second phase, it's probably too much for a single issue.
Comment #55
kim.pepperComment #56
kim.pepperUpdated title, issue summary and change record to reflect current solution.
Comment #57
BerdirI think our current coding standard still requires $time_zone for local variables?
AFAIK the first word should be third-person, so Gets (or Returns).
Also not sure about the Resolve on the resolveTimeZone()
And while we're on naming, not sure if it should be TimeZone or Timezone, I think the second because that's what PHP and our own existing methods does as well: http://php.net/manual/de/datetime.gettimezone.php.
Comment #58
kim.pepperWe agreed to change this to setDefaultTimeZone()
Agreed that although both are correct, TimeZone was inline with \Drupal\Core\Session\AccountInterface::getTimeZone()
Comment #59
BerdirI think this looks great now, the only thing I'm not sure about is these necessary test changes.
It will only affect the current request, so it might be OK, but we could also implement/extend a config save event subscriber that would call out to setDefaultTimeZone() if one of those two values change. Since we already have \Drupal\system\SystemConfigSubscriber, I expect it would only require a few additional lines for that.
Setting to RTBC to get feedback from the committers about this.
Comment #60
alexpottDoing things in config listeners is great - it solves a whole class of bugs in config import and using drush or console to make changes so +1 to @Berdir's idea in #59
Comment #61
catchI think we should do that, CNW for that change.
I agree with this - system.module should have as little code as possible. However in that case, why not move this configuration to somewhere in core* here too?
Comment #62
kim.pepperThanks @alexpott and @catch
Re #61
Comment #64
BerdirWondering if those test fails are caused because we immediately read the new value *and* we do so on the inject config object. I suspect that still sees the old value? So if we really do this, we need to only read it directly from configFactory, to make sure we always have the latest information.
2. Not sure, but changing config is something we need to be very careful about. I've done things like that in pathauto and paragraphs recently and we got a lot of support requests/bug reports because people are using incorrect workflows and are immediately reverting that config change by re-importing old configuration after running updates. So I would be -1 on doing that just to make things a bit "nicer".
Comment #65
kim.pepperComment #66
kim.pepperThank you @Berdir. Let's try that.
Comment #68
BerdirI'm so confused about these tests and how our date formatting works, we're converting so many times between timezone and not-timezone and timestamps and date objects, it's absolutely insane :)
But, as far as I can tell, this identified an actual, real bug.
Interestingly, the test only failed on plain and custom date formatter and not the default. They are almost identical, but not *quite*. They didn't get the change/bugfix in #2739290: UTC+12 is broken on date only fields.
So what's happening is that we are rendering a date as just a date, without time, so \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimeFormatterBase::setTimeZone() skips the timezone and forces UTC. But then \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimePlainFormatter::formatDate() calls format() with NULL as timezone, which makes it then fall back to the default. Which is Pacific/Kwajalein, which then adds 12h and makes it switch over to the next date.
The fun part is why we haven't seen this before. Unlike other places, \Drupal\Core\Datetime\DateFormatter::format() already called date_default_timezone_get() directly, and not through our wrapper that returns the configured timezone. So it did not *actually* see the timezone set by \Drupal\Tests\datetime\Functional\DateTestBase::setSiteTimezone() because before, we only set that again when changing the current user. So we've really always been testing that with the default sydney timezone. Now we automatically update it for the current process. It's also fun how that test loops over all the different timezones, but what we're really doing is just ensuring that we are enforcing UTC over and over again :)
@jhedstrom also pointed out there is an existing issue for this problem and I came up with the same fix. Except there we needed additional changes to the test to make it fail: #2861986: Plus one day on Date only fields with custom display widget for pacific/auckland timezone.
Comment #69
BerdirCross-referencing stuff, see #2946826: Add a method to format a DrupalDateTime object to DateFormatter on ideas to make this whole date/timezone/timestamp converting easier to follow.
Comment #70
kim.pepperGreat find @Berdir!! All feedback addressed so back to RTBC
Comment #71
Wim LeersThis bug is very similar to the one reported at and fixed in #3002164: DateTimeIso8601::getDateTime() does not specify the storage timezone when constructing DrupalDateTime object. And I think that issue may be solving the same bug as #68's interdiff at a lower level: in a
@DataType
class rather than a formatter.I don't have time to verify right now whether that is indeed solving the same problem at a lower level like I suspect, but we definitely should investigate it.
We discovered it from an API-First POV. You say that @jhedstrom discovered it through a widget. And you discovered the same thing here. So it seems like we're all discovering the same problems deep in our date handling…
Comment #72
Berdir@WimLeers, no, I don't think that will help.
The problem is the mentioned disconnect between our date fields/formatters, which uses DateTime objects, and the date formatting API which has a timestamp + timezone API only. So we have to deal with the timezone explicitly.
What's possible is that it would allow to simplify \Drupal\datetime\Plugin\Field\FieldFormatter\DateTimeFormatterBase::setTimeZone as the explicit call to setting the storage timezone then might not be needed anymore. But I that isn't related to this issue/problem.
Comment #73
mpdonadioI think
is the correct assessment here. There is way, way too much shuffling amongst \DateTime, DrupalDateTime, DateTimePlus, ISO strings, and timestamps everywhere in core, but a lot of that is because of what is advertised on public APIs.
Comment #74
alexpottShould this happen on a config event? Ah it is already done... I guess this is not needed.
<3 ... however thinking about this some more it might be even neater to subscribe to the config event in TimeZoneResolver since that is an event subscriber too. It keeps everything nicely together.
Comment #75
kim.pepperThanks @alexpott
Comment #76
BerdirShould we remove the interface again? With the config event change, we again have no calls to this except the evens.
Comment #77
kim.pepperAh yes of course.
Comment #78
BerdirNice, I really like having this all contained in one place.
Comment #79
Wim Leers🎉 Having worked on some datetime issues lately, anything that simplifies it is a good step forward!
"Wraps a configuration event" is a copy/paste leftover.
s/A/a/
Out of scope change.
Wrong FQCN.
We only use camelCase for class members.
Nice!
Comment #80
alexpottNot needed - the listener does this.
Comment #81
Wim LeersThanks to #80, we can go one step further: we can mark
system.timezone_resolver
private 😀However … when I rolled a patch to do exactly that, I noticed that nothing in core is doing this…
Comment #82
BerdirNo, our event subscriber implementation does not alllow private services.
Also, our coding standard documentation allows apparently camel case as long as it is consistently done in a file or so
Comment #83
Wim LeersBut … making it private still allows tests to pass 🤔
TIL
Comment #84
kim.pepperLooks like @alexpott fixed all of @Wim Leers feedback in #80. 👍
Comment #85
alexpott@Wim Leers to make a service in our container private doing
private: TRUE
doesn't work. The only way to make a service private is to dopublic: false
and if you do that tests fail.Comment #86
alexpottSo I pondered what this might break and I found http://grep.xnddx.ru/search?text=extends+AccountProxy&filename= which leads us to https://cgit.drupalcode.org/acquia_commercemanager/tree/modules/acm/src/... / https://www.drupal.org/project/acquia_commercemanager
I'm not sure anything will really break because they override the constructor and use the new class in other services and don't override the real current user as far as I can tell. But I've not looked that hard. I've looked for test coverage of the user features it offers which appear to be the ability to log in from an external ecommerce system but I can't find any :(
Not sure what to do here.
\Drupal\Core\Session\AccountProxy
does not itself feel like an extension point. Sure inject the service places and consume the current user but there are many more supported ways of adding new ways to log in to Drupal. Plus I have no idea of the security implications of doing things the way this module does.Why is this change necessary? Ah this is explained in #68 - nice. Fixing bugs ftw.
Comment #87
Berdir1. Yeah, OK, that point goes to you on the final-discussion, overriding that service/class for alternative authentication mechanisms seems like a bad idea when we have a pluggable authentication system.
Since it didn't have a constructor before this, they couldn't call it, but I guess they could have still used a setter-based injection.
I guess the only thing we can do to avoid breaking that is to get the event dispatcher through a getEventDispatcher() method that does a @trigger_error() when it's not already injected?
Comment #88
alexpott@Berdir well they've also overridden
setAccount()
so that's not going to fire. And yeah stuff like this is why I think we need to usefinal
more. We need to wrangle our extension points. It'll help everyone.Comment #89
BerdirYeah, but the question is, what exactly would have happened if AccountProxy would have been final? The *only* thing that does is prevent subclasses. What would have likely happened is that the module author copied the AccountProxy class as so much was changed compared to the original anyway. And the result would have been the same, we would not have the new extension point/event that we are introducing here and the timezone handling would be silently broken for sites using that module.
Which is exactly my argument against using final too much, that it leads to copying whole classes and resulting in maybe less frequent but much more subtle and harder-to-identify bugs than a missing constructor argument, which will either result in a deprecated message or even a hard error. I get how that is a deal-breaker for automated updates, but for anyone with a managed process and testing environments, the second is IMHO preferable.
But we're getting off track with that discussion here. So maybe we should on purpose leave it like it is, reach out to the module authors (which both seem to be fairly active, and the module only has 2 reported/active installs) and hope that not too many custom sites did something similar.
Comment #90
kim.pepperI created an issue in the commerce-manager issue queue https://github.com/acquia/commerce-manager/issues/117 and invited the maintainers to participate in this discussion.
Comment #91
mpdonadioRe #68, "Interestingly, the test only failed on plain and custom date formatter and not the default.", I dug into this and it looks like the root cause goes all the way back to #2399211: Support all options from views fields in DateTime formatters.
Comment #92
andypostThis issue solves bug #2861986: Plus one day on Date only fields with custom display widget for pacific/auckland timezone
Maybe instead of AccountEvents class just add const $eventName to To AccoutSetEvent class? Related #2825358: Event class constants for event names can cause fatal errors when a module is installed
Comment #93
andypostNot sure about constant naming
Comment #94
BerdirThat issue hasn't been updated in more than a year and I don't think there are any existing events that follow what you just did? the discussion there isn't to *move* the constant, it's to remove it completely.
I'd rather not hold up an issue that was RTBC for so long about something like that.
Comment #95
andypost@Berdir I see no rush here to get it in, but downsides of extra API-addition & useless autoloader call
Meanwhile no reason to block this change on #2825358: Event class constants for event names can cause fatal errors when a module is installed
Comment #96
kim.pepperMiro Michalicka (@mirom) replied on that issue:
Comment #97
larowlanNice
Comment #98
andypostBack to rtbc #80 abd commented other issue - let's keep current way
Comment #100
mpdonadioSpurious from the not-correct patch. Reuploading #80 so it is the one tested.
Comment #102
kim.pepperI believe this was mean to be rtbc
Comment #104
xjmSo this previously fetched the user's configured timezone and fell back to the site configured timezone followed by the results of the PHP function if there was none configured. Is this correct? If so, is there a behavior change when the user's configured timezone or the site configured timezone does not match that result?
If there is a behavior change, we need to at a minimum document what it is in the CR. Right now it just tells you to replace the Drupal function use with the PHP base function, without explaining what impacts this might have. It might also need to be mentioned in the release notes for that reason and so we should add a release notes snippet.
There's also a change where
@
was previously being used with the function when it was the fallback, and it is not anymore. I'm guessing this is related to:So that's another thing that changes besides the behavior (notices no longer suppressed), and probably also worth mentioning.
What behavior changes might result from the removal of this fallback? Is it now returning NULL in cases where it wouldn't previously? Also might need to be documented in the CR?
Not going to make a fuss, but I still don't think we should start peppering the codebase with
final
when there's disagreement about how we should use it.Is there a reason we didn't try to provide some BC for this as described in #87? And also this should perhaps be mentioned in the CR if we've spotted actual contrib modules that might have been affected (even if upon further inspection they weren't).
Meanwhile this is changing behavior in he other direction. What impacts might that have and why is this one necessary?
Comment #105
kim.pepperThe TimeZoneResolver handles these events and uses the same fall back logic:
I've updated the change record with this info.
Comment #107
alexpottAll the event constant classes are already
final
in core. This would be an outlier. They don't contain functionality and extending or overriding them makes no sense.Comment #108
BerdirOn point 5, see #86 where alexpott asked the same and then he found #68 where I explained those changes in depth. In short, this makes timezone switching more reliable than before and that uncovered existing bugs where our test coverage didn't work because the timezone wasn't actually switched.
There was an existing bug about it that was closed as a duplicate because that issue was struggling to write a test for it and this has to fix it: #2861986: Plus one day on Date only fields with custom display widget for pacific/auckland timezone
Comment #109
kim.pepperOK. I've restored #105.1 final and #105.5
I should have re-read the comment history!
Comment #110
kim.pepperUpdated IS with Release notes snippet.
Comment #111
BerdirThanks, I've also added a note about the date/timezone bugfix that's included here to the issue sommary and also extended the API changes section with the AccountProxy stuff.
Back to RTBC, the feedback has been addressed.
I did ask in Slack if committing this to 8.7 is still an option, which would be nice as it does also fix bugs and isn't just a deprecation. But that is pretty unlikely as it contains the AccountProxy changes that will break at least one contrib module.
Comment #112
kim.pepperUpdates deprecation format and bumps to 8.8.0.
Comment #113
BerdirComment #115
andypostReroll with version fix
EDIT: not related failure of tests
Comment #116
kim.pepperComment #117
c7bamford CreditAttribution: c7bamford commentedIt would be nice to be able to pass in a user to get that user's timezone or default instead of the current user.
Comment #118
BerdirIf you want the timezone of a user then you can simply call $user->getTimezone(). The service that is added here is *not* an API, you are not supposed to call it, ever.
Comment #120
BerdirRandom fail?
Comment #121
larowlanI opened https://github.com/acquia/commerce-manager/pull/148/ for the commerce manager issue, their sub-class implementation is identical and therefore not needed
Comment #122
larowlanhttps://github.com/acquia/commerce-manager/pull/148 is in, so this has no contrib impact now
Comment #123
kim.pepperComment #124
acbramley CreditAttribution: acbramley at PreviousNext commentedRerolled #115
Comment #125
kim.pepperBack to RTBC.
Comment #126
larowlanDown to uber-nits now
as per #107 and other event classes in core, I think we should keep this as final as per earlier patches
nit ===
Comment #127
kim.pepperFixes nits in #126
Comment #128
kim.pepperBack to RTBC for @larowlan's review.
Comment #129
andypostRtbc++
Comment #130
larowlanComment #131
larowlanCommitted e47700f and pushed to 8.8.x. Thanks!
Published change record
Comment #134
jhedstromThis seems to have caused some fairly serious regressions in terms of form entry for datetimes. See #3087606: Datetime::getInfo() caches user's timezone causing unpredictable timestamps. (core) and #3085947: Entered date is stored using cached timezone from previous user (scheduler). In manual testing, the Authored on node form entry now only displays in whatever the server's date is, even though the date/time displays correctly when viewing a node.
Comment #135
jhedstromAs noted in #3087606-8: Datetime::getInfo() caches user's timezone causing unpredictable timestamps., I think this issue exacerbated an existing bug, rather than causing it outright.