34

我正在寻找在 Rails 模型中使用持续时间字段的最佳方法。我希望格式为 HH:MM:SS(例如:01:30:23)。使用的数据库是本地的 sqlite 和生产中的 Postgres。

我还想使用该字段,以便查看该字段中的所有对象,并得出该模型中所有对象的总时间,并最终得到如下结果:

30 条记录,总计 45 小时 25 分 34 秒。

那么什么最适合呢?

  • 迁移的字段类型
  • CRUD 表单的表单字段(小时、分钟、秒下拉菜单?)
  • 生成模型中所有记录的总持续时间的最便宜的方法
4

4 回答 4

44
  • 在数据库中存储为整数(可能是秒数)。
  • 您的参赛表格将取决于具体的用例。下拉菜单很痛苦;最好在小时 + 分钟 + 秒内使用小文本字段。
  • 只需在持续时间列上运行SUM查询即可生成总计。如果你使用整数,这很容易和快速。

此外:

  • 使用帮助器在视图中显示持续时间。ActiveSupport::Duration您可以使用123.seconds(替换为数据库中的整数)轻松地将持续时间转换为以秒为单位123的整数。inspect在结果上使用以获得Duration漂亮的格式。(它并不完美。您可能想自己写一些东西。)
  • 在您的模型中,您可能需要返回/获取ActiveSupport::Duration对象而不是整数的属性读取器和写入器。只需定义duration=(new_duration)and duration,它在内部调用read_attribute/write_attribute带有整数参数。
于 2009-06-26T22:32:37.413 回答
22

在 Rails 5 中,您可以使用 ActiveRecord::Attributes 将 ActiveSupport::Durations 存储为 ISO8601 字符串。使用 ActiveSupport::Duration 优于整数的优点是您可以直接使用它们进行日期/时间计算。你可以做类似的事情Time.now + 1.month,而且总是正确的。

就是这样:

添加config/initializers/duration_type.rb

class DurationType < ActiveRecord::Type::String
  def cast(value)
    return value if value.blank? || value.is_a?(ActiveSupport::Duration)

    ActiveSupport::Duration.parse(value)
  end

  def serialize(duration)
    duration ? duration.iso8601 : nil
  end
end

ActiveRecord::Type.register(:duration, DurationType)

移民

create_table :somethings do |t|
  t.string :duration
end

模型

class Something < ApplicationRecord
  attribute :duration, :duration
end

用法

something = Something.new
something.duration = 1.year    # 1 year
something.duration = nil
something.duration = "P2M3D"   # 2 months, 3 days (ISO8601 string)
Time.now + something.duration  # calculation is always correct
于 2017-07-27T19:11:08.643 回答
6

我尝试使用 ActiveSupport::Duration 但无法使输出清晰。

你可能喜欢ruby​​-duration,一种不可变的类型,它以秒为单位表示一定量的时间。它有很多测试和一个 Mongoid 模型字段类型。

我还想轻松解析人类持续时间字符串,所以我选择了Chronic Duration。这是将其添加到具有 time_spent in seconds 字段的模型的示例。

class Completion < ActiveRecord::Base
  belongs_to :task
  belongs_to :user

  def time_spent_text
    ChronicDuration.output time_spent
  end

  def time_spent_text= text
    self.time_spent = ChronicDuration.parse text
    logger.debug "time_spent: '#{self.time_spent_text}' for text '#{text}'"
  end

end
于 2011-02-01T03:26:33.267 回答
4

I've wrote a some stub to support and use PostgreSQL's interval type as ActiveRecord::Duration.

See this gist (you can use it as initializer in Rails 4.1): https://gist.github.com/Envek/7077bfc36b17233f60ad

Also I've opened pull requests to the Rails there: https://github.com/rails/rails/pull/16919

于 2014-09-12T10:44:22.807 回答