我在客户端使用tsjzt:http://pellepim.bitbucket.org/jstz/来获取我存储在用户对象中的当前用户时区。
这很好用,给了我时区如"欧洲/伦敦"。我想验证何时将其传递给模型,它是一个有效的时区,因为发生了一些不好的事情。
所以我发现了这个问题:Issue validating user time zone for Rails app on Heroku并尝试了这个验证:
validates_inclusion_of :timezone, :in => { in: ActiveSupport::TimeZone.zones_map(&:name) }
但是名称与tzinfo不同。我认为我的客户端检测到时区字符串"欧洲/伦敦"本质上是TimeZone类中TimeZone映射的值组件而不是名称 - 在本例中将设置为"伦敦"。
所以我尝试了这个:
validates_inclusion_of :timezone, :in => { in: ActiveSupport::TimeZone.zones_map(&:tzinfo) }
其他SO问题的原始答案或我:tzinfo
的修改后的答案都没有起作用,因为它们在以下情况下均未通过验证:时区为"欧洲/伦敦"当显然这是一个有效的时区!
我对这个时区验证做错了什么,我该如何解决?
答案 0 :(得分:15)
看起来你想要这个:
validates_inclusion_of :timezone,
:in => ActiveSupport::TimeZone.all.map { |tz| tz.tzinfo.name }
在我的机器上,该列表包含以下名称:
...
"Europe/Istanbul",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
...
但是,更清洁的解决方案是自定义验证方法,如下所示:
validates_presence_of :timezone
validate :timezone_exists
private
def timezone_exists
return if timezone? && ActiveSupport::TimeZone[timezone].present?
errors.add(:timezone, "does not exist")
end
它接受的值更灵活:
ActiveSupport::TimeZone["London"].present? # => true
ActiveSupport::TimeZone["Europe/London"].present? # => true
ActiveSupport::TimeZone["Pluto"].present? # => false
答案 1 :(得分:5)
一种更轻便,性能更高的解决方案将使用TZInfo::Timezone.all_identifiers
而不是处理ActiveSupport::TimeZone.all
中的列表。
validates :timezone, presence: true, inclusion: { in: TZInfo::Timezone.all_identifiers }
答案 2 :(得分:1)
所有其他答案都很好,尤其是 Matt Brictson 的。当有人将诸如“Paris”之类的内容传递给时区时,此方法会起作用,该时区可由 rails 使用,但不在正在检查的列表 (ActiveSupport::TimeZone) 中。此验证检查它是否对 rails 有效,并允许“Paris”有效:
validates_each :timezone do |record, attr, value|
!!DateTime.new(2000).in_time_zone(value)
rescue
record.errors.add(attr, 'was not valid, please select one from the dropdown')
end