验证时区在rails中有效

时间:2015-08-03 16:26:54

标签: ruby-on-rails ruby validation datetime ruby-on-rails-4

我在客户端使用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的修改后的答案都没有起作用,因为它们在以下情况下均未通过验证:时区为"欧洲/伦敦"当显然这是一个有效的时区!

我对这个时区验证做错了什么,我该如何解决?

3 个答案:

答案 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