2016-03-22 5 views
3

Я пытаюсь пройти через годы и месяцы. Если @user.created_at является November 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. Как я могу выяснить эту проблему?

+0

Сколько лет и месяцев вы хотите пройти? Вы можете добавить числа, такие как 1.month, чтобы медленно увеличиваться в течение месяцев – Vincent

ответ

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 
+0

Спасибо @tadman за ответ. Почему существует '0..6.collect'? Я хочу сделать это до 'now', поэтому я сделал это:' Hash [@ admin.created_at.to_date.beginning_of_month..Date.today.to_date.beginning_of_month.collect {| n | [start.advance (months: n) .month, 0]}] ', однако получил это сообщение об ошибке:' NoMethodError (undefined method 'collect 'for 3: Fixnum):' - я пропустил часть конвертирования дат? – user984621

+0

О, конечно, это может сработать. Просто убедитесь, что в 'n..m', что' n <= m'. Я забыл сгруппировать диапазон. Сделано редактирование. Должно быть '(0..6)' как в «месяцах плюс от нуля до плюс шесть», но вы также можете заменить свой код. Вы не можете использовать диапазон, если вы не хотите выполнять итерацию каждый день. – tadman

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 и сейчас.

1

Одна вещь, которую вы можете сделать, это превратить каждую дату в «номер месяца» это год, умноженная на 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 - март, а 10 - ноябрь. Как и ожидалось, nov2015 и mar2016 имеют разницу в четыре. Теперь мы можем перебирать один из другого:

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 

Отлично! Но это лучше, если мы можем поставить это в метод, который возвращает нумератор, поэтому мы можем использовать любой из перечислимых методов (each, map, each_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" ] 
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 
+0

Hi @steenslag, спасибо за ваш ответ. Я пытаюсь заставить его работать, но выход будет всегда останавливаться в феврале, он никогда не дойдет до марта. – user984621

+0

@ user984621 Я переключил 2 строки кода (строки 'puts..' и' from_date = from_date ... '). Возможно, вы тестируете «старую» версию. – steenslag

+0

Я тестирую эту «последнюю»/новую версию ... – user984621

0

Это чисто-рубиновое решение.

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 
0

Добавление месяцев объекта даты в Rails с использованием метода Integer#month() будет заботиться о всех обертке лет для вас. Это также достаточно разумно, чтобы не пропускать месяц с менее чем 31 дней, если дата присоединения пользователя выпадает на 31-е число месяца. Если вы добавите месяц до 31 января, возвращаемое значение будет 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 
Смежные вопросы