0

如何在红宝石中计算秒、分钟、天、月和年的relative_time?

Given a DateTime object, implement relative time
d = DateTime.now
d.relative_time # few seconds ago

Possible outputs
# few seconds ago
# few minutes ago - till 3 minutes
# x minutes ago (here x starts from 3 till 59)
# x hours ago (x starts from 1 - 24) and so on for the below outputs
# x days ago
# x weeks ago
# x months ago
# x years ago

如何在红宝石中实现这一点?请帮忙

4

3 回答 3

2

使用 GNU 日期

在 Ruby 的核心和标准库中,没有针对您想要的输出类型的便捷方法,因此您必须实现自己的逻辑。如果没有 ActiveSupport 扩展,您最好调用 GNU(而不是 BSD)日期实用程序。例如:

now       = Time.now
last_week = %x(date -d "#{now} - 1 week")

如果您将格式标志传递给 GNU date,例如+"%s", date 命令将提供日期为自纪元以来的秒数。这允许您为要比较的任何时间单位创建案例语句,例如超过 360 秒前还是不到 86,400 秒。

请参阅 GNU coreutils 定义的日期输入格式,以更全面地了解如何使用各种日期字符串选项。

使用 Rails 扩展

如果您愿意在代码中安装并需要几个 Rails 扩展,可以使用ActionView::Helpers::DateHelper#distance_of_time_in_words。例如:

require "active_support/core_ext/integer/time"
require "active_support/core_ext/numeric/time"
require "action_view"

include ActionView::Helpers::DateHelper

2.weeks.ago
#=> 2020-07-12 09:34:20.178526 -0400

distance_of_time_in_words Date.current, 1.month.ago
#=> "30 days"

sprintf "%s ago", distance_of_time_in_words(Time.now, 1.hour.ago)
#=> "about 1 hour ago"

各种#ago 方法与日期/时间计算相结合,以及对距离助手的调用将做你想做的事,但如果你想以不同的方式转换结果(例如指定您希望将大距离的输出作为几周或几个月而不是几年)。

其他宝石

当然还有其他 Ruby gem 可以提供帮助。Ruby Toolbox 显示了许多用于确定时差的 gem,但似乎都没有得到很好的维护。买者自负,您的里程可能会有所不同。

也可以看看

于 2020-07-26T14:32:10.720 回答
1

首先,DateTime用于历史日期,而Time用于当前日期,因此您可能需要后者。(请参阅何时应使用 DateTime 以及何时应使用 Time?

给定 45 分钟前的时间实例: (45 × 60 = 2,700)

t = Time.now - 2700

您可以通过以下方式获得与当前时间的秒差Time#-

Time.now - t
#=> 2701.360482

# wait 10 seconds

Time.now - t
#=> 2711.148882

这种差异可以在case表达式中与范围一起使用,以生成相应的持续时间字符串:

diff = Time.now - t

case diff
when 0...60       then "few seconds ago"
when 60...180     then "few minutes ago"
when 180...3600   then "#{diff.div(60)} minutes ago"
when 3600...7200  then "1 hour ago"
when 7200...86400 then "#{diff.div(3600)} hours ago"
# ...
end

您可能需要调整数字,但这应该可以让您继续前进。

于 2020-07-27T10:48:02.680 回答
1

我将构造一个relative_time有两个参数的方法:

  • date_timeDateTime的一个实例,它指定过去的时间;和
  • time_unit, :SECONDS, :MINUTES, :HOURS, :DAYS, :WEEKS,:MONTHS或之一:YEARS,指定时间单位,在该时间单位中,当前时间与date_time要表示的时间差。

代码

require 'date'

TIME_UNIT_TO_SECS = { SECONDS:1, MINUTES:60, HOURS:3600, DAYS:24*3600,
                      WEEKS: 7*24*3600 }
TIME_UNIT_LBLS    = { SECONDS:"seconds", MINUTES:"minutes", HOURS:"hours",
                      DAYS:"days", WEEKS: "weeks", MONTHS:"months",
                      YEARS: "years" }

def relative_time(date_time, time_unit)
  now = DateTime.now
    raise ArgumentError, "'date_time' cannot be in the future" if
      date_time > now

  v = case time_unit
      when :SECONDS, :MINUTES, :HOURS, :DAYS, :WEEKS
        (now.to_time.to_i-date_time.to_time.to_i)/
          TIME_UNIT_TO_SECS[time_unit]
      when :MONTHS
        0.step.find { |n| (date_time >> n) > now } -1
      when :YEARS
        0.step.find { |n| (date_time >> 12*n) > now } -1
      else
        raise ArgumentError, "Invalid value for 'time_unit'"
      end
  puts "#{v} #{TIME_UNIT_LBLS[time_unit]} ago"
end

例子

date_time = DateTime.parse("2020-5-20")

relative_time(date_time, :SECONDS)
5870901 seconds ago

relative_time(date_time, :MINUTES)
97848 minutes ago

relative_time(date_time, :HOURS)
1630 hours ago

relative_time(date_time, :DAYS)
67 days ago

relative_time(date_time, :WEEKS)
9 weeks ago

relative_time(date_time, :MONTHS)
2 months ago

relative_time(date_time, :YEARS)
0 years ago

解释

如果time_unit等于:SECONDS, :MINUTES, :HOURS,:DAYS:WEEKSI 只需计算与当前时间之间经过的秒数date_time,然后将其除以给定时间单位的秒数。例如,如果time_unit等于:DAYS以秒为单位的经过时间除以24*3600,因为每天有那么多秒。

如果time_unit等于:MONTHS,我使用Date#>>方法(由 继承DateTime)来确定从date_time达到当前时间之后的时间所经过的月数,然后减去1

time_unit如果等于,则计算类似:YEARS:确定从date_time达到当前时间之后的时间所经过的年数,然后减去1

可以要求用户输入Time实例(而不是DateTime实例)作为第一个参数。但是,这不会简化方法,因为Time实例必须在等于或DateTime时转换为实例才能使用方法。time_unit:MONTH:YEARDate#>>

于 2020-07-26T23:00:31.323 回答