Итак, у меня есть два объекта Ruby Date, и я хочу их повторять каждый месяц. Например, если у меня есть Date.new (2008, 12) и Date.new (2009, 3), это даст мне 2008-12, 2009-1, 2009-2, 2009-3 (как, например, объекты Date). Я пробовал использовать диапазон, но он дает каждый день. Я видел метод шагов для Date, но он позволяет мне пропускать только количество дней (и каждый месяц имеет другое число). У кого-нибудь есть идеи?Итерации каждый месяц с объектами даты
ответ
Я добавил следующий метод Date класса:
class Date
def all_months_until to
from = self
from, to = to, from if from > to
m = Date.new from.year, from.month
result = []
while m <= to
result << m
m >>= 1
end
result
end
end
Вы можете использовать это нравится:
>> t = Date.today
=> #<Date: 2009-11-12 (4910295/2,0,2299161)>
>> t.all_months_until(t+100)
=> [#<Date: 2009-11-01 (4910273/2,0,2299161)>, #<Date: 2009-12-01 (4910333/2,0,2299161)>, #<Date: 2010-01-01 (4910395/2,0,2299161)>, #<Date: 2010-02-01 (4910457/2,0,2299161)>]
Итак, более rubyish подход ИМХО было бы что-то по:
class Month<Date
def succ
self >> 1
end
end
и
>> t = Month.today
=> #<Month: 2009-11-13 (4910297/2,0,2299161)>
>> (t..t+100).to_a
=> [#<Month: 2009-11-13 (4910297/2,0,2299161)>, #<Month: 2009-12-13 (4910357/2,0,2299161)>, #<Month: 2010-01-13 (4910419/2,0,2299161)>, #<Month: 2010-02-13 (4910481/2,0,2299161)>]
Но вы должны быть осторожны, чтобы использовать первые дни месяца (или реализовать такую логику в месяц) ...
Я считаю, что мне нужно сделать это иногда при создании списков выберите месяцев. Ключом является оператор >>
по дате, который продвигает дату вперед на один месяц.
def months_between(start_month, end_month)
months = []
ptr = start_month
while ptr <= end_month do
months << ptr
ptr = ptr >> 1
end
months
end
results = months_between(Date.new(2008,12), Date.new(2009,3))
Конечно, вы можете отформатировать результаты, как вам нравится в цикле.
months << "#{Date::MONTHNAMES[ptr.month]} #{ptr.year}"
Вернет название месяца и год («Март 2009») вместо объекта «Дата». Обратите внимание, что возвращенные объекты Date будут установлены в 1-ом месяце.
Это единственный действительный ответ здесь, поскольку он объясняет 'Date # >>' – mikezter
Я придумал следующее решение. Это смесь для диапазонов дат, которая добавляет итератор для обоих лет и месяцев. Он дает поддиапазоны полного диапазона.
require 'date'
module EnumDateRange
def each_year
years = []
if block_given?
grouped_dates = self.group_by {|date| date.year}
grouped_dates.each_value do |dates|
years << (yield (dates[0]..dates[-1]))
end
else
return self.enum_for(:each_year)
end
years
end
def each_month
months = []
if block_given?
self.each_year do |range|
grouped_dates = range.group_by {|date| date.month}
grouped_dates.each_value do |dates|
months << (yield (dates[0]..dates[-1]))
end
end
else
return self.enum_for(:each_month)
end
months
end
end
first = Date.parse('2009-01-01')
last = Date.parse('2011-01-01')
complete_range = first...last
complete_range.extend EnumDateRange
complete_range.each_year {|year_range| puts "Year: #{year_range}"}
complete_range.each_month {|month_range| puts "Month: #{month_range}"}
Даст вам:
Year: 2009-01-01..2009-12-31
Year: 2010-01-01..2010-12-31
Month: 2009-01-01..2009-01-31
Month: 2009-02-01..2009-02-28
Month: 2009-03-01..2009-03-31
Month: 2009-04-01..2009-04-30
Month: 2009-05-01..2009-05-31
Month: 2009-06-01..2009-06-30
Month: 2009-07-01..2009-07-31
Month: 2009-08-01..2009-08-31
Month: 2009-09-01..2009-09-30
Month: 2009-10-01..2009-10-31
Month: 2009-11-01..2009-11-30
Month: 2009-12-01..2009-12-31
Month: 2010-01-01..2010-01-31
Month: 2010-02-01..2010-02-28
Month: 2010-03-01..2010-03-31
Month: 2010-04-01..2010-04-30
Month: 2010-05-01..2010-05-31
Month: 2010-06-01..2010-06-30
Month: 2010-07-01..2010-07-31
Month: 2010-08-01..2010-08-31
Month: 2010-09-01..2010-09-30
Month: 2010-10-01..2010-10-31
Month: 2010-11-01..2010-11-30
Month: 2010-12-01..2010-12-31
Как вспомогательный метод:
def iterate(d1, d2)
date = d1
while date <= d2
yield date
date = date >> 1
end
end
Использование:
start_date = Date.new(2008, 12)
end_date = Date.new(2009, 3)
iterate(start_date, end_date){|date| puts date}
Или, если вы предпочитаете обезьяны патч Дата:
class Date
def upto(end_date)
date = self
while date <= end_date
yield date
date = date >> 1
end
end
end
Использование:
start_date = Date.new(2008, 12)
end_date = Date.new(2009, 3)
start_date.upto(end_date){|date| puts date}
Вот что-то очень Ruby:
первый день каждого месяца
(Date.new(2008, 12)..Date.new(2011, 12)).select {|d| d.day == 1}
Это даст вам массив в первый день для каждый месяц в пределах диапазона.
последний день каждого месяца
(Date.new(2008, 12)..Date.new(2012, 01)).select {|d| d.day == 1}.map {|d| d - 1}.drop(1)
Сразу отметим, что дата окончания должна быть месяц после конца диапазона.
Это самый элегантный! –
И неэффективен для больших диапазонов дат –
действительно? 4000 лет достаточно большой диапазон дат? Benchmark.measure {(Date.new (1, 1) .. Date.new (4000, 12)). Select {| d | d.day == 1}} => 1.170000 0.000000 1.170000 (1.181518) –
MonthRange.new(date1..date2).each { |month| ... }
MonthRange.new(date1..date2).map { |month| ... }
Вы можете использовать все методы Enumerable, если вы используете этот класс итератора. Я также обрабатываю строки так, чтобы они могли принимать входные данные формы.
# Iterate over months in a range
class MonthRange
include Enumerable
def initialize(range)
@start_date = range.first
@end_date = range.last
@start_date = Date.parse(@start_date) unless @start_date.respond_to? :month
@end_date = Date.parse(@end_date) unless @end_date.respond_to? :month
end
def each
current_month = @start_date.beginning_of_month
while current_month <= @end_date do
yield current_month
current_month = (current_month + 1.month).beginning_of_month
end
end
end
Для begin_of_month вам нужен ActiveSupport (от рельсов) –
def each_month(date, end_date)
ret = []
(ret << date; date += 1.month) while date <= end_date
ret
end
Date.new(2014,1,1).upto(Date.today).map {|date| "#{date.to_s[0..-4]}"}.uniq
Даст вам строковое представление каждого месяца, включая его год.
- 1. Извлечь месяц с даты
- 2. Получение данных за каждый месяц
- 3. Как каждый объект с объектами?
- 4. Показать данные за каждый месяц
- 5. Как производить подсчеты за каждый месяц вокруг ссылочной даты
- 6. JQuery каждый цикл итерации
- 7. Как увеличить целое число за каждый месяц после фиксированной даты?
- 8. SQL: Получение строки за каждый месяц до даты
- 9. Php, рассылка каждый месяц
- 10. Group запрос каждый месяц
- 11. Рубин - каждый энный итерации
- 12. Получить месяц с строки даты?
- 13. Как extraxt месяц с даты
- 14. Как добавить «loanPaid» каждый месяц
- 15. Изменить даты с 1 по 1 месяц
- 16. автоматизировать SQL-запрос для запуска каждый месяц
- 17. Произвести 1 месяц текущей даты и +1 месяц текущей даты
- 18. Auto Insert In Database Каждый месяц
- 19. Проверки даты и месяц
- 20. DAX Месяц до даты
- 21. Get месяц заданной даты
- 22. получить часы за каждый месяц
- 23. Автоматически добавлять проценты каждый месяц
- 24. Петля в диапазоне дат за каждый месяц и каждый день
- 25. Как печатать каждый месяц с его номером?
- 26. Не каждый месяц начинается с понедельника
- 27. Уменьшите стоимость автомобиля каждый месяц
- 28. Проверьте со строки месяц даты?
- 29. Итерации через каждый тег html
- 30. set повторяющееся напоминание, которое повторяется каждый месяц
Надеюсь на что-то более рубиново ... – Andrius
Ну, единственный «рубиновый» подход, который приходит мне на ум, будет определять класс «месяц» (путем наследования даты), определяющий метод succ и использование Range на нем. –
Спасибо Младен! Этот метод all_month_until был именно тем, что я искал. – jspooner