遍历日期以查找日期之间的天数

Iterate over dates to find days between dates

我有一个日期数组,我需要让每个日期计算出下一个日期之间的差异,例如

a= {1=>03-01-2015, 2=>03-10-2015, 3=>03-15-2015, 4=>03-27-2015}

一个完整的对象看起来像这样...

#<Transaction:0xc413c58 @id="KYYEZEPedacrwBkpqDk8C5Yy04BqvJIXJdYXJ", @account="Ppp575Ke19T8PxrAdMrNUq4Ox4AQVrHMLa5D0", @date="2013-10-23", @amount=100, @name="COMED", @meta={"is_risky"=>false, "location"=>{"store_number"=>"10782"}}, @location=nil, @pending=false, @score={"location"=>{}, "name"=>0.2}, @type={"primary"=>"unresolved"}, @category=nil, @category_id=nil>] 

应该return

{9,5,12} #the days between each 2 dates

这是我目前所掌握的...

@collect_transactions = (@user.transactions.find_all { |t| t.name == 'due_at' })
@date_difference = @collect_transactions.map(&:date).each_cons(2) { |a,b| b-a }

我不确定 each_cons 是否是正确的方法,但我现在的回答是这样...

NoMethodError: undefined method `-' for "2015-01-15":String

不计算天数...任何建议将不胜感激

是的,Enumerable#each_cons 是正确的选择。您只需先将日期字符串转换为日期对象。您可以按如下方式进行。

代码

require 'date'

def date_diffs(h)    
  h.values.
    map { |s| Date.strptime(s,'%m-%d-%Y') }.
    each_cons(2).
    map { |d1,d2| (d2-d1).to_i }
end

例子

h = { 1=>"03-01-2015", 2=>"03-10-2015", 3=>"03-15-2015", 4=>"03-27-2015" }

date_diffs(h)
  #=> [9, 5, 12]

讨论

从对您问题的编辑看来,您可能需要使用日期格式字符串 '%Y-%m-%d'。另一种选择是使用 Date::parse rather than Date::strptime。 (我一般更喜欢strptime,因为它要求更高。)

您当然必须确保散列值的顺序正确。如果您使用的是 1.9 之前的 Ruby 版本(当时无法保证哈希键的顺序,甚至没有键顺序的概念),或者您使用的是更新版本(其中键插入顺序是维护),但不确定键的顺序,您可以将此作为第一步:

values = h.values_at(*(1..4))
  #=> ["03-01-2015", "03-10-2015", "03-15-2015", "03-27-2015"] 

说明

我们有:

b = h.values
  #=> ["03-01-2015", "03-10-2015", "03-15-2015", "03-27-2015"] 
c = b.map { |s| Date.strptime(s,'%m-%d-%Y') }
  #=> [#<Date: 2015-03-01 ((2457083j,0s,0n),+0s,2299161j)>,
  #    #<Date: 2015-03-10 ((2457092j,0s,0n),+0s,2299161j)>,
  #    #<Date: 2015-03-15 ((2457097j,0s,0n),+0s,2299161j)>,
  #    #<Date: 2015-03-27 ((2457109j,0s,0n),+0s,2299161j)>] 
d = c.each_cons(2)
  #=> #<Enumerator: [#<Date: 2015-03-01....

我们看到枚举器 d 包含 d.size #=> 3 个元素。它将传递到块中并分配给块变量的第一个元素是:

d1,d2 = d.next
   #=> [#<Date: 2015-03-01 ((2457083j,0s,0n),+0s,2299161j)>,
   #    #<Date: 2015-03-10 ((2457092j,0s,0n),+0s,2299161j)>] 
d1 #=> #<Date: 2015-03-01 ((2457083j,0s,0n),+0s,2299161j)> 
d2 #=> #<Date: 2015-03-10 ((2457092j,0s,0n),+0s,2299161j)> 

我们计算有理数:

d2-d1 #=> (9/1)

我们可以将其(因为它始终是整数)转换为整数:

(9/1).to_i #=> 9

所以我们得到:

d.map { |d1,d2| (d2-d1).to_i }   
  #=> [9, 5, 12] 

为什么 Date#- return a rational number rather than an integer? @Stefan gives a good explanation here.

根据您的日期格式是 mm-dd-YYYY 还是 YYYY-mm-dd(您的问题有点模棱两可),您可以使用 Rails 帮助器 String#to_date 进行转换到 Date 对象。 (这只适用于后者。)

a.map { |t| t.date.to_date }.each_cons(2).map { |a, b| (b - a).to_i }

无论哪种方式,您可能都希望重构代码以将日期存储为数据库中的正确日期数据类型。