循环数年+数月

时间:2016-03-22 19:27:34

标签: ruby-on-rails ruby date datetime

我试图循环数年和数月。如果@user.created_atNovember 2015,那么我想获得此输出:

November 2015
December 2015
January 2016
February 2016
March 2016

我尝试了以下内容:

Hash[(@user.created_at.month..Date.today.month).map { |month| [ month, 0 ] }].merge(...)

但上面的代码返回

  堆栈级别太深

因为它循环11..3。 我怎么能弄清楚这个问题?

6 个答案:

答案 0 :(得分:4)

如果您想计算即将到来的月份/年份,可以随时使用advance

start = @user.created_at.to_date.beginning_of_month

Hash[(0..6).collect { |n| [ start.advance(months: n).month, 0 ] }]

这应该适当地逐步完成几天/几个月。你可能只想坚持日期而不仅仅是月份数。

如果你想做“到今天”,那就试试这个:

date = @user.created_at.to_date.beginning_of_month
stop = Date.today.beginning_of_month
hash = { }

while (date <= stop)
  hash[date] = 0

  date = date.advance(months: 1)
end

答案 1 :(得分:2)

您可以做的一件事就是将每个日期转换为&#34;月份数&#34;这一年乘以12加上零基月,然后迭代它们,例如:

from_date = Date.new(2015, 11) # Nov. 1, 2015
to_date = Date.today # Mar. 22, 2016

nov2015 = from_date.year * 12 + (from_date.month - 1)
# => 24190
nov2015.divmod(12)
# => [2015, 10]

mar2016 = to_date.year * 12 + (to_date.month - 1)
# => 24194
mar2016.divmod(12)
# => [2016, 2]

请注意,这些月份从零开始,因此2为3月,10为11月。正如预期的那样,nov2015mar2016相差四个。现在我们可以从一个迭代到另一个:

nov2015.upto(mar2016) do |month_num|
  year, month = month_num.divmod(12)
  puts Date.new(year, month + 1).strftime("%B %Y")
end
# => November 2015
#    December 2015
#    January 2016
#    February 2016
#    March 2016

完美!但是如果我们可以将它放入一个返回Enumerator的方法中,那么我们可以使用任何Enumerable方法(eachmapeach_cons,你可以命名它。 )上:

def months_enum(from_date, to_date)
  from = from_date.year * 12 + from_date.month - 1
  to = to_date.year * 12 + to_date.month - 1

  Enumerator.new do |y|
    from.upto(to) do |n|
      year, month = n.divmod(12)
      y << Date.new(year, month + 1)
    end
  end
end

然后:

from = Date.new(2015, 11, 1)
to = Date.today

months_enum(from, to).each do |date|
  puts date.strftime("%Y-%m")
end
# -> 2015-11
#    2015-12
#    2016-01
#    2016-02
#    2016-03

p months_enum(from, to).map {|date| date.strftime("%B %Y") }
# => [ "November 2015",
#      "December 2015",
#      "January 2016",
#      "February 2016",
#      "March 2016" ]

答案 2 :(得分:1)

require 'date'
from_date = Date.new(2015, 11)
to_date   = Date.today

until from_date > to_date do
  puts from_date.strftime("%B %Y")
  from_date = from_date.next_month
end

#November 2015
#December 2015
#January 2016
#February 2016
#March 2016

答案 3 :(得分:0)

我将从用户的created_at日期开始并循环直到它已经赶上今天:

start = @user.created_at
months = []
while start <= Time.zone.now
  months << [start.strftime('%B'), start.year]
  start += 1.month
end
months

结果:

=> [["November", 2015], ["December", 2015], ["January", 2016], ["February", 2016], ["March", 2016]]

这样您就不必计算created_at和now之间的月差异。

答案 4 :(得分:0)

这是一个纯Ruby解决方案。

require 'date'

如果给出

prev_date = "November 2015"
m = Date.strptime(prev_date,"%B %Y")
  #=> #<Date: 2015-11-01 ((2457328j,0s,0n),+0s,2299161j)> 

然后

n = Date.today
n -= n.day - 1
  #=> #<Date: 2016-03-01 ((2457449j,0s,0n),+0s,2299161j)>

while m <= n
  puts "#{Date::MONTHNAMES[m.month].ljust(8)} #{m.year}"
  m = m >> 1
end

November 2015
December 2015
January  2016
February 2016
March    2016

答案 5 :(得分:0)

使用Integer#month()方法在Rails中向日期对象添加月份将为您完成所有多年的包装。如果用户的加入日期在某个月的31日,那么它也足够聪明,不会跳过少于31天的月份。如果您在1月31日添加一个月,则返回值将是2月28日(如果是闰年,则为29)。

date_joined = @user.created_at
now = Time.now

until date_joined.month > now.month && date_joined.year == now.year
  puts date_joined.strftime("%B %Y")
  date_joined += 1.month
end