【问题标题】:Ruby - How to round Unix Timestamp (Epoch) to nearest monthRuby - 如何将 Unix 时间戳(纪元)舍入到最近的月份
【发布时间】:2016-05-31 11:33:56
【问题描述】:

我正在尝试将 Ruby 中的 UNIX 时间戳四舍五入到最接近的整月。我有以下 UNIX 时间戳,我想将其转换为所示 - 基本上,如果该月的日期是 15 日及以后,它应该舍入到下个月(例如,2 月 23 日舍入到 3 月 1 日;2 月 9 日向下舍入到 2 月 1 日)。

以下是我拥有的时间戳以及我需要帮助实现的结果:

1455846925(2016 年 2 月 19 日)=> 1456790400(2016 年 3 月 1 日)

1447476352(2015 年 11 月 14 日)=> 1446336000(2015 年 11 月 1 日)

1242487963(2009 年 5 月 16 日)=> 1243814400(2009 年 6 月 1 日)。

我可以仅依靠 1-14(向下舍入)/15+(向上舍入)的逻辑。我意识到这并不总是考虑一个月中的天数,如果需要,我可以接受这一点(尽管始终考虑给定月份中的天数的解决方案是一种奖励) .

Ruby 的 DateTime 模块可能能够结合以一个月的秒数为模来完成,但我不太确定如何将它们放在一起。如果我可以直接转换 UNIX 时间戳而无需先将其转换为 Ruby 日期,那也很好。

提前感谢您的帮助。

【问题讨论】:

  • Rails Datetime 有方法 begin_of_month 和 end_of_month 可用于计算。

标签: ruby rounding unix-timestamp epoch datetimeoffset


【解决方案1】:

如果你在 Rails 中使用 ActiveSupport,这样的事情会起作用:

require 'date'    

def round_to_nearest_month(timestamp)

  # Convert the unix timestamp into a Ruby DateTime object
  datetime = timestamp.to_datetime

  # Get the day of the month from the datetime object
  day_of_month = datetime.mday

  if day_of_month < 15
    datetime.at_beginning_of_month
  else 
    datetime.at_beginning_of_month.next_month
  end    

  return datetime

end

【讨论】:

  • starting_of_month 和 next_month 是否像显示的那样工作,还是在仅 Ruby 的设置中也需要 ActiveSupport?谢谢!
【解决方案2】:

很容易将其转换为Time 对象,然后将其转换回timestamp

如果你使用 Rails,这个方法应该可以,你想要什么:

def nearest_month(t)
  time = Time.at(t).utc
  time = time.next_month if time.day >= 15

  time.beginning_of_month.to_i
end

【讨论】:

  • 如果 Rails/Active 支持可用,非常简洁和一个很好的答案。感谢您的回答。
【解决方案3】:

四舍五入到最接近的秒数。

require 'time'

def round_to_month(secs)
  t1 = Time.at secs
  t2 = (t1.to_datetime >> 1).to_time
  s1 = Time.new(t1.year, month=t1.month)
  s2 = Time.new(t2.year, month=t2.month)
  (t1-s1) < (s2-t1) ? s1 : s2
end

round_to_month(1455846925) # round 2016-02-18 17:55:25 -0800 
  #=> 2016-03-01 00:00:00 -0800
round_to_month(1447476352) # round 2015-11-13 20:45:52 -0800
  #=> 2015-11-01 00:00:00 -0700 
round_to_month(1242487963) # round 2009-05-16 08:32:43 -0700 
  #=> 2009-05-01 00:00:00 -0700 

考虑

secs = 1455846925

计算如下:

 t1 = Time.at secs
   #=> 2016-02-18 17:55:25 -0800 
 dt = t1.to_datetime
   #=> #<DateTime: 2016-02-18T17:55:25-08:00 ((2457438j,6925s,0n),-28800s,2299161j)> 
 dt_next = dt >> 1
   #=> #<DateTime: 2016-03-18T17:55:25-08:00 ((2457467j,6925s,0n),-28800s,2299161j)> 
 t2 = dt_next.to_time 
   #=> 2016-03-18 18:55:25 -0700 
 s1 = Time.new(t1.year, month=t1.month)
   #=> Time.new(2016, month=2) 
   #=> 2016-02-01 00:00:00 -0800 
 s2 = Time.new(t2.year, month=t2.month)
   # Time.new(2016, month=3)
   #=> 2016-03-01 00:00:00 -0800 
 (t1-s1) < (s2-t1) ? s1 : s2
   #=> 1533325.0 < 972275.0 ? 2016-02-18 17:55:25 -0800 : 2016-03-01 00:00:00 -0800
   #=> 2016-03-01 00:00:00 -0800

【讨论】:

  • 为什么秒数与四舍五入到最近的月份有关? (回复:secs 在你的方法中)
  • 问题不是将 Unix 时间戳四舍五入到最近月份的(开始)吗? Unix 时间戳是自纪元以来的秒数。 Kurt 说近似值可以,但为什么不正确地做呢?
  • 嗨,Cary,很高兴再次见到您,感谢您的快速回答。不应该 round_to_month(1447476352) 向下舍入到 11 月 1 日吗?根据您的代码 sn-p,它四舍五入到 12 月 1 日。期待这次选择你的答案而不是乔丹的答案 ;-)
  • 接受为最佳答案。谢谢卡里!在我看来,这是最简洁的,a) 不需要 ActiveSupport/Rails,b) 是最精确的。尽管我要求进行近似四舍五入,但最好使用精确的结果(精确到秒),并且可能会帮助其他 SO 用户。谢谢!
  • 库尔特。你说的对。我最初写了(s2-t1) &lt; (s2-t1) ? s1 : s2(剪切和粘贴s2-t1),但后来忘记将第一个s2-t1更改为t1-s1。我在进行详细计算时注意到了这一点,我现在已经发布了。我很高兴它有帮助。
【解决方案4】:

我不知道这是否和@CarySwoveland 的解决方案一样准确,但我喜欢它:

require 'time'

FIFTEEN_DAYS = 15 * 24 * 60 * 60

def round_to_month(secs)
  t1 = Time.at(secs + FIFTEEN_DAYS)
  Time.new(t1.year, t1.month)
end

p round_to_month(1455846925) # round 2016-02-18 17:55:25 -0800 
# => 2016-03-01 00:00:00 -0800

p round_to_month(1447476352) # round 2015-11-13 20:45:52 -0800
# => 2015-11-01 00:00:00 -0700

p round_to_month(1242487963) # round 2009-05-16 08:32:43 -0700 
# => 2009-05-01 00:00:00 -0700

如果您希望它返回 UNIX 时间戳,只需在方法的最后一行添加 .to_i

【讨论】:

  • 天啊,你几天前才 49.8K ......你花多少时间在 SO 上而不是在你的工作上? :)
  • 如果精度不是最重要的因素,这个答案很有意义。如果我在日常工作中有更多的周期来进行开发,那么我更可能会想出这个。不幸的是,现在的周期非常有限。我希望我有时间坐下来思考逻辑——编码挑战可以用我的方式清楚地解决。再次感谢乔丹。
  • 对于它的价值,在某一点上,您的 SO 分数具有自己的生命。一目了然,上周我在几个月或几年前发布的答案中获得了 120 分。
  • 如果@Kurt 是对的,即您在短短几天内从 49.8K 变为 45.2K,那么您一定发布了一些真正的恶作剧。
  • 乔丹和/或@Cary Swoveland,如果你有时间,你能看一下stackoverflow.com/questions/35713260/…吗?我昨晚发布了它,但它只被查看了几次,几乎就像它从未循环到问题列表中一样。不管怎样,谢谢!
猜你喜欢
  • 1970-01-01
  • 2012-03-27
  • 2022-10-13
  • 2017-12-02
  • 1970-01-01
  • 2018-10-25
  • 2012-03-10
  • 2015-02-04
  • 1970-01-01
相关资源
最近更新 更多