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

mwi*_*ams 28 ruby time duration ruby-on-rails

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

我还想使用这个字段,这样我就可以看一下该字段中的所有对象,并得出该模型中所有对象的总时间,最后得到如下结果:

30条记录共计45小时25分34秒.

那么什么最适合?

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

mol*_*olf 42

  • 在数据库中以整数存储(可能是秒数).
  • 您的报名表将取决于具体的用例.下拉令人痛苦; 最好在小时+分钟+秒内使用小文本字段.
  • 只需SUM在持续时间列上运行查询即可生成总计.如果使用整数,这很容易,也很快.

另外:

  • 使用帮助程序在视图中显示持续时间.您可以ActiveSupport::Duration通过使用123.seconds(替换123为数据库中的整数)轻松地将持续时间转换为整数秒.inspect在结果上使用以获得Duration良好的格式.(这不完美.你可能想自己写点东西.)
  • 在您的模型中,您可能希望属性读取器和编写器返回/获取ActiveSupport::Duration对象,而不是整数.只需定义duration=(new_duration)duration,内部调用read_attribute/ write_attribute使用整数参数.


Ant*_*ang 12

在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)
Run Code Online (Sandbox Code Playgroud)

移民

create_table :somethings do |t|
  t.string :duration
end
Run Code Online (Sandbox Code Playgroud)

模型

class Something < ApplicationRecord
  attribute :duration, :duration
end
Run Code Online (Sandbox Code Playgroud)

用法

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
Run Code Online (Sandbox Code Playgroud)


Tur*_*adg 5

我尝试使用ActiveSupport :: Duration但是无法清除输出.

你可能喜欢ruby-duration,一种不可变的类型,代表一些精确的时间,以秒为单位.它有很多测试和Mongoid模型字段类型.

我也想轻松地解析人类持续时间的字符串,所以我选择了慢性持续时间.以下是将其添加到具有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
Run Code Online (Sandbox Code Playgroud)