Skip to content

[WIP] Add timezone selector to DateTimeWidget for event start and end#8304

Draft
davisagli wants to merge 2 commits into
mainfrom
event-timezones
Draft

[WIP] Add timezone selector to DateTimeWidget for event start and end#8304
davisagli wants to merge 2 commits into
mainfrom
event-timezones

Conversation

@davisagli

Copy link
Copy Markdown
Member

Refs plone/Products.CMFPlone#4115

This updates the existing DateTimeWidget to display the selected timezone for event start and end dates, and provide a selector for changing it.

Screenshot 2026-05-30 at 2 07 03 PM Screenshot 2026-05-30 at 2 07 22 PM

Depends on the REST API support in plone/plone.restapi#2018

Notes:

  • If there's an existing value, its timezone is displayed.
  • If there's no existing value (on the add form, for example) we use the browser's local timezone as the initial timezone.
  • When the timezone is changed, the absolute datetime value is also adjusted (so for example if I see 1pm in Europe/Vienna and change the timezone to America/Los_Angeles, the raw value is set 9 hours earlier so the user still sees 1pm in the new timezone)

To do:

  • Test how timezones work with whole-day events
  • Add support for AoE ("anywhere on earth")
  • Update the display widget, and other components that show event datetimes
  • Update tests
  • Review usability
  • Review a11y
  • Update storybook

Intentionally out of scope for this PR:

  • Other changes to the widgets (like improving the help text and the whole day and open end fields)
  • Switch to new widgets. We still should switch to the date and time pickers from @plone/components in the long term (for better a11y and avoiding momentjs), but that was a bigger project than I could tackle today, and I wanted to implement this in a way that can potentially be merged for Volto 19 without breaking changes.
  • Show the timezone selector for other datetime fields. We can do this if we want, it will require a few more changes in the plone.restapi branch.
  • Per-user timezone preference

@davisagli

Copy link
Copy Markdown
Member Author

@stevepiercy Any feedback before I spend more time on this?

@stevepiercy

Copy link
Copy Markdown
Collaborator
  • When the timezone is changed, the absolute datetime value is also adjusted (so for example if I see 1pm in Europe/Vienna and change the timezone to America/Los_Angeles, the raw value is set 9 hours earlier so the user still sees 1pm in the new timezone)

Thanks for picking this up.

This is hella tricky to implement, especially during those periods when one timezone has DST in effect and the other doesn't. I'd avoid automatic adjustment, keeping it consistent for the user.

My concern is that moment.js will still screw up the displayed datetime. The most common use case is for Plone Conference, where it shows the converted local time of an event to the page visitor, like 1:30 AM for the keynote, which is just wrong.

https://2025.ploneconf.org/schedule/keynotes
Screenshot 2026-05-30 at 5 57 13 AM

And I'm not able to download an .ics file that has the correct timezone. But now I'm feature creeping out of scope...

@davisagli

Copy link
Copy Markdown
Member Author

This is hella tricky to implement, especially during those periods when one timezone has DST in effect and the other doesn't. I'd avoid automatic adjustment, keeping it consistent for the user.

So in this scenario:

  1. Start is set to 12pm in Europe/Berlin.
  2. User changes start timezone to America/Los_Angeles
    Do you think it's more consistent for the user if the time now shows as 3am on the same day (i.e. keep the same value and show it in the new timezone) or 12pm (change the value so that the displayed time doesn't change once it's in the new timezone).

I don't have a strong opinion on this one myself, but I thought the second option is less surprising (and it's what google calendar does).

My concern is that moment.js will still screw up the displayed datetime. The most common use case is for Plone Conference, where it shows the converted local time of an event to the page visitor, like 1:30 AM for the keynote, which is just wrong.

We have to be explicit about what timezone to use now that we have that information available, whether we do that using momentjs or not. For display use cases like the conference schedule, it's easy to switch to formatting the time using Intl.DateTimeFormat, which provides easier control over the timezone. I'm just keeping momentjs for the input widget, because it currently uses react-dates which depends on momentjs. And I think I've figured out a way to set the offset on the momentjs object so that it appears in the correct timezone in the widget.

And I'm not able to download an .ics file that has the correct timezone. But now I'm feature creeping out of scope...

That one is definitely a separate issue. The ICS download is implemented as a browser view in plone.app.event so isn't even in the same packages I've been touching. The changes in plone.restapi should make sure that the datetime value is stored with the correct timezone on the backend, so then it should be pretty straightforward to make the ics export use that information.

@stevepiercy

Copy link
Copy Markdown
Collaborator

Do you think it's more consistent for the user if the time now shows as 3am on the same day (i.e. keep the same value and show it in the new timezone) or 12pm (change the value so that the displayed time doesn't change once it's in the new timezone).

If the user changes the timezone, then they should manually change the time and possibly the date. Automatic adjustment should be avoided.

Otherwise, the sooner we kick moment.js to the curb, the better. I understand that it's not altogether possible yet.

@davisagli

Copy link
Copy Markdown
Member Author

If the user changes the timezone, then they should manually change the time and possibly the date. Automatic adjustment should be avoided.

So just to be clear, you would expect the time to show as 3am after the user changes the timezone in the scenario I gave? (We're either adjusting the display or adjusting the value, so I'm not certain which one you mean by no automatic adjustment.)

@stevepiercy

Copy link
Copy Markdown
Collaborator

So just to be clear, you would expect the time to show as 3am after the user changes the timezone in the scenario I gave? (We're either adjusting the display or adjusting the value, so I'm not certain which one you mean by no automatic adjustment.)

Now I'm not sure. I thought that the user was creating or editing an event. Whatever the user inputs is what the user should get, and there should be no "helpful" automatic adjustment to the values that the user enters.

For example:

  1. I enter May 30, 2026, 12:00 PM.
  2. My default timezone, whether it comes from the browser or a user or site configuration value, is Pacific/Los Angeles is initially selected.
  3. I change the timezone to Eastern/New York.
  4. The entered date and time remain May 30, 2026, 12:00 PM, without any "help".
  5. I save the event and publish it.
  6. The event is saved internally with the appropriate datetime and timezone.
  7. All other users around the planet can now view the event as occurring May 30, 2026, 12:00 PM, Eastern/New York, without any "help" from moment.js nonsense.

I hope that clarifies the complete scope of the matter. If this PR resolves only parts of it, then please clarify. Thank you!

@thet

thet commented May 31, 2026

Copy link
Copy Markdown
Member

@davisagli @stevepiercy I agree with that scenario just outlined by steve above. The user enters the date and time and changing the timezone would not change the values entered in the date and time widget.
It's not only much easier to implement but also less surprising.

In the z3c.form datetime widget data converter we're first creating a native datetime value and then pop the timezone information onto it, which is consistent to the outlined scenario: https://github.com/plone/plone.app.z3cform/blob/2d9cfdfc8ab3544aa7135b89210a100e7ed2436e/src/plone/app/z3cform/converters.py#L125

@davisagli

Copy link
Copy Markdown
Member Author

@stevepiercy Yep, that's what I'm aiming for. (Sorry if I made it confusing earlier. We have to update the internal value so that the value shown to the user is the same once the new timezone offset is applied.) Items 1-6 in your example are already implemented with the combination of this PR and the plone.restapi PR that it depends on. Item 7 is not implemented yet, but that's what I plan to do.

@stevepiercy

Copy link
Copy Markdown
Collaborator

@thet and @davisagli I just want to say thank you for verifying, and for your efforts on this much needed improvement.

onBlur={() => setOpen(false)}
options={Intl.supportedValuesOf('timeZone').map((tz) => ({
value: tz,
label: tz,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the future:‌ we should translate this - TIL from Gemini - using CLDR: https://en.wikipedia.org/wiki/Common_Locale_Data_Repository
For example "Europe/Vienna" -> "Mitteleuropäische Zeit - Wien (GMT+2)".

An example Python Code:

from babel.dates import get_timezone_name
from zoneinfo import ZoneInfo

tz = ZoneInfo("Europe/Vienna")
# Liefert "Mitteleuropäische Sommerzeit" für das aktuelle Datum
print(get_timezone_name(tz, locale="de_AT", width="long"))

However, I'd still like to find timezones by it's English identifier, as I'm used to do so.

But this is definitely for a future improvement. I'm just mentioning this here.


const renderWidget = !(id === 'end' && formData?.open_end);

const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

@thet thet Jun 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was chewing on the Intl timezone, because we have a portal timezone and potentially a timezone set by the user in the @@personal-preferences.
But using Intl here makes a lot of sense!

I think the timezone setting in the @@personal-preferences is obsolete and should be removed.
I have created a draft PLIP here: plone/Products.CMFPlone#4337

@thet thet left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants