diff --git a/app/javascript/js/controllers/fields/date_field_controller.js b/app/javascript/js/controllers/fields/date_field_controller.js index 6a204b0dff..59c624a758 100644 --- a/app/javascript/js/controllers/fields/date_field_controller.js +++ b/app/javascript/js/controllers/fields/date_field_controller.js @@ -204,18 +204,22 @@ export default class extends Controller { return } + const timezonedDate = DateTime.fromISO(selectedDates[0].toISOString()) + .setZone(this.displayTimezone, { keepLocalTime: true }) + .setZone('UTC', { keepLocalTime: !this.relativeValue }) + let value switch (this.fieldTypeValue) { case 'time': // For time values, we should maintain the real value and format it to a time-friendly format. - value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: !this.relativeValue }).toFormat(RAW_TIME_FORMAT) + value = timezonedDate.toFormat(RAW_TIME_FORMAT) break case 'date': - value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: true }).toFormat(RAW_DATE_FORMAT) + value = timezonedDate.toFormat(RAW_DATE_FORMAT) break default: case 'dateTime': - value = DateTime.fromISO(selectedDates[0].toISOString()).setZone('UTC', { keepLocalTime: !this.relativeValue }).toISO() + value = timezonedDate.toISO() break } diff --git a/lib/avo/fields/date_time_field.rb b/lib/avo/fields/date_time_field.rb index efb453fcc6..c7b24c81e4 100644 --- a/lib/avo/fields/date_time_field.rb +++ b/lib/avo/fields/date_time_field.rb @@ -43,19 +43,26 @@ def fill_field(model, key, value, params) end def utc_time(value) - if timezone.present? - ActiveSupport::TimeZone.new(timezone).local_to_utc(Time.parse(value)) + time = Time.parse(value) + + if timezone.present? && !time.utc? + ActiveSupport::TimeZone.new(timezone).local_to_utc(time) else value end end def timezone - if @timezone.respond_to?(:call) + timezone = if @timezone.respond_to?(:call) return Avo::Hosts::ResourceViewRecordHost.new(block: @timezone, record: resource.model, resource: resource, view: view).handle + else + @timezone end - @timezone + # Fix for https://github.com/moment/luxon/issues/1358#issuecomment-2017477897 + return "Etc/UTC" if timezone&.downcase == "utc" && view.in?([:new, :create, :edit, :update]) + + timezone end end end diff --git a/spec/system/avo/date_time_fields/timezone_spec.rb b/spec/system/avo/date_time_fields/timezone_spec.rb new file mode 100644 index 0000000000..46894653a9 --- /dev/null +++ b/spec/system/avo/date_time_fields/timezone_spec.rb @@ -0,0 +1,128 @@ +require "rails_helper" + +# Please use the reset_browser helpers before the first and after the last spec. +RSpec.describe "timezone", type: :system do + let!(:project) { create :project, started_at: Time.new(2024, 3, 25, 8, 23, 0, "UTC") } # "March 25, 2024 08:23:00 UTC" + + subject(:text_input) { find '[data-field-id="started_at"] [data-controller="date-field"] input[type="text"]' } + + after do + ProjectResource.restore_items_from_backup + end + + describe "On Romania (EET) with CET timezone configured", tz: "Europe/Bucharest" do + before do + ProjectResource.with_temporary_items do + field :started_at, as: :date_time, timezone: "CET", time_24hr: true, format: "MMMM dd, y HH:mm:ss z" + end + end + + it { reset_browser } + + context "index" do + it "displays the date in CET tz" do + visit "/admin/resources/projects" + + expect(field_element_by_resource_id(:started_at, project.id).text).to eq "March 25, 2024 09:23:00 CET" + end + end + + context "show" do + it "displays the date in CET tz" do + visit "/admin/resources/projects/#{project.id}" + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 09:23:00 CET" + end + end + + context "edit" do + describe "when keeping the value" do + it "saves the valid date" do + visit "/admin/resources/projects/#{project.id}/edit" + + expect(text_input.value).to eq "2024-03-25 09:23:00" + + save + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 09:23:00 CET" + end + end + + describe "when changing the value" do + it "saves the valid date" do + visit "/admin/resources/projects/#{project.id}/edit" + + expect(text_input.value).to eq "2024-03-25 09:23:00" + + open_picker + set_picker_minute 24 + set_picker_second 17 + + close_picker + + save + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 09:24:17 CET" + end + end + end + end + + describe "On Romania (EET) with UTC timezone configured", tz: "Europe/Bucharest" do + before do + ProjectResource.with_temporary_items do + field :started_at, as: :date_time, timezone: "UTC", time_24hr: true, format: "MMMM dd, y HH:mm:ss z" + end + end + + it { reset_browser } + + context "index" do + it "displays the date in UTC tz" do + visit "/admin/resources/projects" + + expect(field_element_by_resource_id(:started_at, project.id).text).to eq "March 25, 2024 08:23:00 UTC" + end + end + + context "show" do + it "displays the date in UTC tz" do + visit "/admin/resources/projects/#{project.id}" + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 08:23:00 UTC" + end + end + + context "edit" do + describe "when keeping the value" do + it "saves the valid date" do + visit "/admin/resources/projects/#{project.id}/edit" + + expect(text_input.value).to eq "2024-03-25 08:23:00" + + save + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 08:23:00 UTC" + end + end + + describe "when changing the value" do + it "saves the valid date" do + visit "/admin/resources/projects/#{project.id}/edit" + + expect(text_input.value).to eq "2024-03-25 08:23:00" + + open_picker + set_picker_minute 24 + set_picker_second 17 + + close_picker + + save + + expect(find_field_value_element(:started_at).text).to eq "March 25, 2024 08:24:17 UTC" + end + end + end + end +end