在Rails模型中使用持续时间字段

36
我正在寻找在Rails模型中使用持续时间字段的最佳方法。 我希望格式为HH: MM: SS(例如:01:30:23)。 使用的数据库是sqlite本地和Postgres在生产中。
我还想使用此字段,以便查看该模型中所有对象的字段,并计算出所有对象的总时间,最终得到如下结果:
“30条记录共计45小时25分34秒。”
那么什么方法最适合呢?
1.迁移的字段类型
2.CRUD表单的表单字段(小时、分钟和秒钟下拉菜单?)
3.计算模型中所有记录的总持续时间的最便宜的方法。
4个回答

46
  • 在数据库中存储为整数(可能是秒数)。
  • 您的输入表单将取决于确切的用例。下拉框很麻烦;最好使用小文本字段表示以小时+分钟+秒为单位的持续时间。
  • 只需对持续时间列运行SUM查询即可生成总计。如果使用整数,这很容易和快速。

此外:

  • 使用助手在视图中显示持续时间。您可以轻松地将持续时间转换为ActiveSupport::Duration整数秒数,方法是使用123.seconds(将123替换为数据库中的整数)。 对结果Duration使用inspect进行美化格式。 (它不完美。您可能需要自己编写一些内容。)
  • 在模型中,您可能希望属性读者和编写器返回/接受ActiveSupport::Duration对象,而不是整数。只需定义内部调用具有整数参数的read_attribute/write_attributeduration=(new_duration)duration即可。

我从来没有考虑过使用秒,谢谢你的提示! - mwilliams
1
你如何使用 inspect 格式化输出?有示例吗? - Fabiano Arruda

28
在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

我在Rails引擎中尝试了这个操作,当我运行"db:migrate"时,命令没有报错,但是生成的架构包含以下内容:# 由于以下标准错误,无法转储表“mynamespace_mytable”: # 对于列“duration_column”,未知的类型'duration' - Harry Lime
我的错误在于尝试将 :duration 作为列类型使用。 - Harry Lime

6

我曾尝试使用ActiveSupport::Duration,但无法获得清晰的输出。

你可能会喜欢ruby-duration,这是一个不可变类型,以秒为精度表示一定量的时间。它有许多测试和Mongoid模型字段类型。

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

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

4

我们为Rails 5.1编写了一个更新的要点:https://gist.github.com/vollnhals/a7d2ce1c077ae2289056afdf7bba094a - lion.vollnhals
@Envek 谢谢伙计,你帮我省了很多时间。这是最终的 PR:https://github.com/rails/rails/commit/0475215d4fa1a6db2a92a0065081fe19c64cc124 - BenKoshy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接