Timezone controls (optional, default turned on)#1327
Conversation
…zone override. (Part 1 of timezone controls.)
(Part 2 of timezone controls.)
(Part 3 of timezone controls.)
(Part 4 of timezone controls.)
…gem). (Part 5 of timezone controls.)
Also, localize dates for longer events. (Part 7 of timezone controls.)
… specified time zone. (Otherwise they have to do mental math to add times in GMT.)
|
Many thanks @cwant for taking the time to do this PR – really appreciated and really helpful! Before this PR, I tried on my side to cherry-pick some of your commits from your forked TeSS, so far I picked up almost the same one :
I'll test on my side if everything seems ok for me and if I don't need the last I've added on my side. I can definitely help review this PR @fbacall – if you want to throw a copilot, please do so. |
There was a problem hiding this comment.
Pull request overview
Ports “timezone controls” UI and server-side support into TeSS so event times can be displayed either in each event’s own timezone (default) or in the browser-detected timezone, and allows editing event start/end in the event’s specified timezone.
Changes:
- Added timezone toggle UI + AJAX endpoint to re-render event time blocks and controls.
- Updated event edit/create flow to interpret entered datetimes as being in the selected event timezone.
- Added ICU-based localized timezone naming (
ffi-icu) and new i18n strings.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| test/controllers/events_controller_test.rb | Adds controller test asserting edit/create respect event timezone for entered datetimes. |
| Gemfile | Adds ffi-icu dependency for localized timezone display names. |
| Gemfile.lock | Locks ffi-icu and dependency graph updates. |
| config/tess.example.yml | Introduces feature.timezone_controls default configuration. |
| config/routes.rb | Adds events#event_time_data route for AJAX timezone updates. |
| config/locales/en.yml | Adds new strings for timezone UI and event form labels. |
| app/views/static/home/_upcoming_events.html.erb | Adds timezone controls to the home “upcoming events” section and localizes the header. |
| app/views/events/show.html.erb | Uses timezone-aware partials when the feature is enabled. |
| app/views/events/index.html.erb | Renders timezone controls above results when enabled. |
| app/views/events/_form.html.erb | Changes labels and assumes start/end are entered in selected timezone. |
| app/views/events/_event.html.erb | Uses timezone-aware time rendering in event cards when enabled. |
| app/views/events/_event_timezone_control.html.erb | New timezone control widget (collapse/choice UI). |
| app/views/events/_event_time_div.html.erb | New per-event “time + timezone” rendering block used for AJAX replacement. |
| app/views/common/_associated_events.html.erb | Adds timezone controls to associated-events blocks when enabled. |
| app/models/event.rb | Updates start_local/end_local conversion logic. |
| app/helpers/events_helper.rb | Localizes date formatting and adds ICU-based timezone display helper. |
| app/controllers/events_controller.rb | Shifts times for edit/save and adds event_time_data JSON endpoint. |
| app/controllers/application_controller.rb | Adds session timezone selection from params/session. |
| app/assets/stylesheets/application.scss | Adds styling for timezone controls. |
| app/assets/javascripts/events.js | Adds client-side AJAX to fetch updated HTML and wire up timezone control behaviors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| include PublicActivity::StoreController | ||
|
|
||
| before_action :configure_permitted_parameters, if: :devise_controller? | ||
| before_action :timezone_from_params_or_session |
| # Render times for the following event ids | ||
| out = (event_ids || []).map do |event_id| | ||
| event = Event.find(event_id.to_i) | ||
| html = render_to_string partial: 'events/event_time_div', | ||
| formats: [:html], | ||
| locals: { event: event, timezone: timezone } | ||
| { id: "#event-time-#{event.id}", | ||
| html: html} | ||
| end |
| def shift_times_to_timezone_for_editing | ||
| if (@event&.timezone) | ||
| @event.start = @event.start&.in_time_zone(@event.timezone.to_s) | ||
| @event.end = @event.end&.in_time_zone(@event.timezone.to_s) | ||
| end | ||
| end | ||
|
|
||
| def shift_times_to_utc_for_saving | ||
| if (@event&.timezone) | ||
| @event.start = @event.start&.change(zone: @event.timezone.to_s) | ||
| @event.end = @event.end&.change(zone: @event.timezone.to_s) | ||
| end | ||
| end |
| def start_local | ||
| set_to_local start | ||
| start&.in_time_zone(self&.timezone) | ||
| end |
| def end_local | ||
| set_to_local self.end | ||
| self.end&.in_time_zone(self&.timezone) | ||
| end |
| @@ -1,7 +1,14 @@ | |||
| <% cache(['home', 'upcoming_events', @events]) do %> | |||
| <a data-toggle="collapse" href="#timezone-select" class="show-timezone-controls" | ||
| role="button" aria-expanded="false" aria-controls="collapseExample"> | ||
| <%= t('events.timezone_controls.controls_show') %> | ||
| </a> | ||
| <% end %> | ||
| </p> | ||
|
|
||
| <% if local_assigns[:browser_timezone] %> | ||
| <div class="collapse" id="timezone-select"> | ||
| <hr> | ||
| <i class="fa fa-window-close close-timezone-controls" | ||
| aria-label="<%= t('events.timezone_controls.close') %>"></i> | ||
|
|
| # The ICU library we use want's underscores not spaces ... | ||
| tz_name = tz_name.gsub(' ', '_') |
| $.ajax({ | ||
| url: "/events/event_time_data.json", | ||
| data: data, |
| <% unless event.timezone %> | ||
| <%= t('events.no_timezone_provided_gmt_assumed') %> | ||
| <% end %> |
UI functionalityWhat I checked in the UI
Testing ingestion
Other Issues
Questions
Code review
|


Summary of changes
This ports over the timezone controls from Explora to Elixir/TeSS.
Motivation and context
It's helpful to see events in your own timezone. This PR gives a toggle between showing times in the timezone of each event, and showing times in the timezone detected by the webbrowser.
The timezone control widget shows up on any page where there is an event listed (e.g.
events#index,events#show, upcoming events, etc.).The timezone controls can be opened and closed for visibility.
An added feature: the event edit page allows you to enter times in the timezone of the event (rather than having to do the mental math of entering it in GMT and hoping you've done that correctly).
Note : this uses the
ffi-icugem. For our TeSS we use this so that the timezone names appear in the language that is selected in our user interface. E.g.heure d’été du Pacifique nord-américainwhen French is selected instead ofPacific Daylight Time.Screenshots
Here is the initial hit on
event#index, with option "event timezones" selected by default. Two events with different timezones are shown.Here is the same page, with the timezones controls displayed via the
Change...link. (Clicking thexhides the controls again.)Here is the page with my timezone (detected by the browser) is clicked:
Here is the editing link for start/end times, done in the timezone selected for the event (converted to GMT when saved to the database):
Checklist