There was a request to save the time of an event such as Meeting considering all time zones.
For example Meeting1 will be held in Tokyo from 11:00. Meeting2 will be held in UTC from 10:00.
I want to express something like.
--Fix the default Timezone of Rails API server. --Save Timezone in DB --Determine where to insert the logic that dynamically changes the Timezone. --Check if time type is saved in DB considering timezone --Validation to prevent passing strings without timezone information when saving
First, Rails has multiple time zones to consider.
https://qiita.com/joker1007/items/2c277cca5bd50e4cce5e
This time, to avoid confusion, I set all Timezones below to UTC.
Save the variable that is the basis for changing the timezone. Since the timezone is changed via ActiveSupport, you can write the following validation so that the valid value is saved.
# tz :string
class Meeting < ApplicationRecord
validates :tz, inclusion: { in: ActiveSupport::TimeZone::MAPPING.values }, presence: true
end
When returning a value in the API, add logic that dynamically changes the Timezone and returns the time. In this case, I incorporated it into Serialzer. Since this logic is related to display, as a result, we decided to put it in a layer close to View.
# tz :string
class Meeting < ApplicationRecord
~abridgement~
def timezone
ActiveSupport::TimeZone.new(tz)
end
def use_timezone
Time.use_zone(timezone) do
yield
end
end
end
class MeetingSerializer
attribute :start_time do |record|
record.use_timezone do
record.start_time
end
end
end
Save is with timezone information, if you pass a string, ActiveRecord will do it nicely.
# actieve_The timezone of record is UTC
[47] pry(main)> meeting
=> #<Meeting:0x00007ff2c7ad4618
id: "162b1ed7-5502-42f8-8bed-592d2dae7db1",
start_at: Mon, 05 Oct 2020 16:00:00 UTC +00:00,
created_at: Tue, 06 Oct 2020 06:28:39 UTC +00:00,
updated_at: Thu, 08 Oct 2020 02:11:54 UTC +00:00>
[48] pry(main)> meeting.start_at="2020-10-06T01:00:00+0900"
=> "2020-10-06T01:00:00+0900"
[49] pry(main)> meeting.start_at
=> Mon, 05 Oct 2020 16:00:00 UTC +00:00
I created a controller module like the one below, and made validation bite with before_action.
module ValidateTimeFormat
class TimeFormatError < StandardError
end
extend ActiveSupport::Concern
included do
rescue_from ValidateTimeFormat::TimeFormatError, with: :render_time_format_error
end
TIME_FORMAT = '%FT%T%z'
def render_time_format_error(msg)
render json: { message: msg }, status: :bad_request
end
def validate_time_format(time_str)
DateTime.strptime(time_str, TIME_FORMAT).zone
rescue Date::Error
raise ValidateTimeFormat::TimeFormatError, :time_format_is_invalid
end
end
Rails had an image that there are many points to consider in the Time system. With this implementation, I fully understood ()
https://twitter.com/yoshixj
Recommended Posts