【问题标题】:Using a duration field in a Rails model在 Rails 模型中使用持续时间字段
【发布时间】:2010-11-06 07:01:19
【问题描述】:
我正在寻找在 Rails 模型中使用持续时间字段的最佳方式。我希望格式为 HH:MM:SS(例如:01:30:23)。使用的数据库是本地的 sqlite 和生产中的 Postgres。
我也想使用这个字段,这样我就可以查看该字段中的所有对象,并得出该模型中所有对象的总时间,并最终得到如下结果:
30 条记录,总计 45 小时 25 分 34 秒。
那么什么最适合呢?
- 迁移的字段类型
- CRUD 表单的表单域(小时、分钟、秒下拉菜单?)
- 生成模型中所有记录的总持续时间的成本最低的方法
【问题讨论】:
标签:
ruby-on-rails
ruby
time
duration
【解决方案1】:
- 在数据库中存储为整数(可能是秒数)。
- 您的输入表单将取决于具体的用例。下拉菜单很痛苦;最好在小时 + 分钟 + 秒内使用小文本字段。
- 只需对持续时间列运行
SUM 查询即可生成总计。如果您使用整数,这将简单快捷。
另外:
- 使用帮助程序在视图中显示持续时间。您可以使用
123.seconds(将123 替换为数据库中的整数)轻松地将持续时间(以秒为整数)转换为ActiveSupport::Duration。在生成的Duration 上使用inspect 以获得良好的格式。 (它并不完美。您可能想自己写一些东西。)
- 在您的模型中,您可能需要返回/获取
ActiveSupport::Duration 对象而不是整数的属性读取器和写入器。只需定义 duration=(new_duration) 和 duration,它们在内部使用整数参数调用 read_attribute / write_attribute。
【解决方案2】:
在 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
【解决方案3】:
我尝试使用 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