我正在寻找在 Rails 模型中使用持续时间字段的最佳方法。我希望格式为 HH:MM:SS(例如:01:30:23)。使用的数据库是本地的 sqlite 和生产中的 Postgres。
我还想使用该字段,以便查看该字段中的所有对象,并得出该模型中所有对象的总时间,并最终得到如下结果:
30 条记录,总计 45 小时 25 分 34 秒。
那么什么最适合呢?
- 迁移的字段类型
- CRUD 表单的表单字段(小时、分钟、秒下拉菜单?)
- 生成模型中所有记录的总持续时间的最便宜的方法
我正在寻找在 Rails 模型中使用持续时间字段的最佳方法。我希望格式为 HH:MM:SS(例如:01:30:23)。使用的数据库是本地的 sqlite 和生产中的 Postgres。
我还想使用该字段,以便查看该字段中的所有对象,并得出该模型中所有对象的总时间,并最终得到如下结果:
30 条记录,总计 45 小时 25 分 34 秒。
那么什么最适合呢?
SUM
查询即可生成总计。如果你使用整数,这很容易和快速。此外:
ActiveSupport::Duration
您可以使用123.seconds
(替换为数据库中的整数)轻松地将持续时间转换为以秒为单位123
的整数。inspect
在结果上使用以获得Duration
漂亮的格式。(它并不完美。您可能想自己写一些东西。)ActiveSupport::Duration
对象而不是整数的属性读取器和写入器。只需定义duration=(new_duration)
and duration
,它在内部调用read_attribute
/write_attribute
带有整数参数。在 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
我尝试使用 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
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