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

发布于 2024-07-25 18:40:48 字数 359 浏览 2 评论 0原文

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

我还想研究这个领域,这样我就可以查看该领域中的所有对象,并得出该模型中所有对象的总时间,最后得到如下结果:

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

那么什么最适合呢?

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

I'm looking for the best way to use a duration field in a Rails model. I would like the format to be HH:MM:SS (ex: 01:30:23). The database in use is sqlite locally and Postgres in production.

I would also like to work with this field so I can take a look at all of the objects in the field and come up with the total time of all objects in that model and end up with something like:

30 records totaling 45 hours, 25 minutes, and 34 seconds.

So what would work best for?

  • Field type for the migration
  • Form field for the CRUD forms (hour, minute, second drop downs?)
  • Least expensive method to generate the total duration of all records in the model

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不疑不惑不回忆 2024-08-01 18:40:49
  • 以整数形式存储在数据库中(可能是秒数)。
  • 您的报名表将取决于具体的用例。 下拉菜单很痛苦; 最好使用小文本字段来表示持续时间(以小时+分钟+秒为单位)。
  • 只需对持续时间列运行 SUM 查询即可生成总计。 如果您使用整数,这既简单又快速。

另外:

  • 使用助手在视图中显示持续时间。 您可以使用 123.seconds(将 123 替换为数据库中的整数)轻松将秒数整数形式的持续时间转换为 ActiveSupport::Duration )。 对生成的 Duration 使用 inspect 以获得良好的格式。 (它并不完美。您可能想自己编写一些东西。)
  • 在您的模型中,您可能需要返回/获取 ActiveSupport::Duration 对象而不是整数的属性读取器和写入器。 只需定义 duration=(new_duration)duration,它们会使用整数参数在内部调用 read_attribute / write_attribute
  • Store as integers in your database (number of seconds, probably).
  • Your entry form will depend on the exact use case. Dropdowns are painful; better to use small text fields for duration in hours + minutes + seconds.
  • Simply run a SUM query over the duration column to produce a grand total. If you use integers, this is easy and fast.

Additionally:

  • Use a helper to display the duration in your views. You can easily convert a duration as integer of seconds to ActiveSupport::Duration by using 123.seconds (replace 123 with the integer from the database). Use inspect on the resulting Duration for nice formatting. (It is not perfect. You may want to write something yourself.)
  • In your model, you'll probably want attribute readers and writers that return/take ActiveSupport::Duration objects, rather than integers. Simply define duration=(new_duration) and duration, which internally call read_attribute / write_attribute with integer arguments.
衣神在巴黎 2024-08-01 18:40:49

在 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

In Rails 5, you can use ActiveRecord::Attributes to store ActiveSupport::Durations as ISO8601 strings. The advantage of using ActiveSupport::Duration over integers is that you can use them for date/time calculations right out of the box. You can do things like Time.now + 1.month and it's always correct.

Here's how:

Add 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)

Migration

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

Model

class Something < ApplicationRecord
  attribute :duration, :duration
end

Usage

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
末骤雨初歇 2024-08-01 18:40:49

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

您可能喜欢 ruby-duration,它是一种不可变类型,表示一定的时间量,精确到秒。 它有很多测试和 Mongoid 模型字段类型。

我还想轻松解析人类持续时间字符串,因此我选择了 慢性持续时间。 以下是将其添加到具有 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

I tried using ActiveSupport::Duration but had trouble getting the output to be clear.

You may like ruby-duration, an immutable type that represents some amount of time with accuracy in seconds. It has lots of tests and a Mongoid model field type.

I wanted to also easily parse human duration strings so I went with Chronic Duration. Here's an example of adding it to a model that has a time_spent in seconds field.

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
淡墨 2024-08-01 18:40:49

我编写了一些存根来支持和使用 PostgreSQL 的 interval 类型作为 ActiveRecord::Duration

请参阅此要点(您可以在 Rails 4.1 中将其用作初始化程序): https://gist.github.com/ Envek/7077bfc36b17233f60ad

此外,我还向那里的 Rails 开放了拉取请求:
https://github.com/rails/rails/pull/16919

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文