Skip to content

Instantly share code, notes, and snippets.

@nsommer
Last active November 10, 2021 22:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nsommer/07fd029b5519237df37bdc7dfa58d7d1 to your computer and use it in GitHub Desktop.
Save nsommer/07fd029b5519237df37bdc7dfa58d7d1 to your computer and use it in GitHub Desktop.
Concern that adds _local attr methods to convert times to a given time zone on a per user basis
# ActiveRecord stores timestamps in UTC and converts them into the application's time zone
# (see config.time_zone in config/application.rb).
#
# This module provides a simple to use API to automatically generate '_local' suffix methods
# for model attribute readers that convert dates and times according to a per model time zone.
module LocalDateTimeAttrReaders
extend ActiveSupport::Concern
included do
include ActiveModel::AttributeMethods
end
class_methods do
# Defines the name of an instance method that returns the time zone
# to use for time conversions.
#
# class User
# include LocalDateTimeAttrReaders
#
# attr_accessor :time_zone
#
# time_zone_attr_reader :time_zone
# end
def time_zone_attr_reader(attr)
define_method :time_zone_reader do
self.send(attr)
end
end
# Registers attribute reader methods with +_local+ suffix to read
# +Date+, +Time+ and +DateTime+ typed attributes in local time.
#
# class User
# include LocalDateTimeAttrReaders
#
# attr_accessor :time_zone, :birthday
#
# time_zone_attr_reader :time_zone
# local_date_attr_reader :birthday
#
# def initialize(time_zone = "Europe/Berlin", birthday = Date.today)
# @time_zone = time_zone
# @birthday = birthday
# end
# end
#
# user = User.new
# user.birthday
# => Date instance using global application time zone
#
# user.birthday_local
# => Date instance using time zone specified in :time_zone attribute (here: "Europe/Berlin")
def local_attr_reader(*attrs)
# We use ActiveModel::AttributeMethods instead of manually registering methods
attribute_method_suffix '_local'
# Why does this not work?
# define_attribute_methods *attrs
attrs.each { |attr| define_attribute_method attr }
end
alias_method :local_date_attr_reader, :local_attr_reader
alias_method :local_time_attr_reader, :local_attr_reader
alias_method :local_datetime_attr_reader, :local_attr_reader
end
private
def attribute_local(attr)
send(attr).in_time_zone(time_zone_reader)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment