Reputation: 1347
I need to create logfiles per month for a range of months. Therefor I need all [year,month] tuples in a given range
How do you do iterating over dates?
How can this be done if I'd need to iterate every day?
Upvotes: 14
Views: 13549
Reputation: 1960
A potential one-line approach.
(start_date..end_date).to_a.map { |date| date.strftime("%B %Y") }.uniq
Upvotes: 0
Reputation: 1
date1 = Date.new(2024,4,25)
date2 = Date.new(2025,2,8)
month_distance = 12 * (date2.year - date1.year) + date2.month - date1.month + 1
month_distance.times.map { |m| d = date1 + m.months; [d.year, d.month] }
# => [[2024, 4], [2024, 5], [2024, 6], [2024, 7], [2024, 8], [2024, 9], [2024, 10], [2024, 11], [2024, 12], [2025, 1], [2025, 2]]
Upvotes: 0
Reputation: 1872
I had a slighly different problem, but it is related: I needed the months between a given start & end year.
def month_start_dates_between_years(start_year, end_year)
years = (start_year..end_year).to_a
month_tuples = years.product((1..12).to_a)
return month_tuples.map { |tuple| Date.new(*tuple) }
end
Upvotes: 0
Reputation: 2518
Most of the answers here require iterating over every day in the range. Meaning if you are doing this for a range of a few years, you could be having a loop with thousands of iterations.
This snippet creates a loop with as many steps as there are months in the range, which is more efficient:
require 'date'
start_date = Date.new(2010, 10)
end_date = Date.new(2011, 4)
current_month = start_date
date_tuples = []
while current_month <= end_date
date_tuples << [current_month.year, current_month.month]
current_month = current_month.next_month
end
pp date_tuples
# => [[2010, 10], [2010, 11], [2010, 12], [2011, 1], [2011, 2], [2011, 3], [2011, 4]]
One quirk of this method is that it will only work with dates that are on the first day of the month. So if you have a date like Date.new(2020, 10, 12)
you need to convert it to the first day of the month
Upvotes: 0
Reputation: 2030
start_date = 1.year.ago.to_date
end_date = Date.current.yesterday
monthly = [Date.new(start_date.year, start_date.beginning_of_month.month, 1)]
(start_date..end_date).each do |d|
month_date = Date.new(d.year, d.next_month.beginning_of_month.month, 1)
monthly << month_date if monthly.exclude?(month_date) && month_date < end_date - 1.month
end
monthly
=> [Fri, 01 Sep 2017, Sun, 01 Oct 2017, Wed, 01 Nov 2017, Fri, 01 Dec 2017, Sun, 01 Jan 2017, Thu, 01 Feb 2018, Thu, 01 Mar 2018, Sun, 01 Apr 2018, Tue, 01 May 2018, Fri, 01 Jun 2018, Sun, 01 Jul 2018, Wed, 01 Aug 2018]
Upvotes: 1
Reputation: 1
Here is a way i wrote to solve this issue. This was designed for working with hash data like: {Sun, 01 Jan 2012=>58, Wed, 01 Feb 2012=>0, Thu, 01 Mar 2012=>0} but could be easily modified for array data.
See: https://github.com/StephenOTT/add_missing_dates_ruby where i have provided a working code sample
But the key piece of code is:
def addMissingMonths (datesHash)
count = 0
result = {}
datesHash.keys.each do |x|
if x != datesHash.keys.last
(x+1.month).upto(datesHash.keys[count+1]-1.month) do |a|
result[a.at_beginning_of_month] = 0
end
end
count += 1
end
return result.merge!(datesHash)
end
The key content to look at is: (x+1.month).upto(datesHash.keys[count+1]-1.month)
Upvotes: 0
Reputation: 55002
For example:
((Date.today - 90)..Date.today).map{|d| [d.year, d.month]}.uniq
#=> [[2012, 12], [2013, 1], [2013, 2], [2013, 3]]
Upvotes: 31
Reputation: 817
require 'date'
Time.new(2011).to_date.upto(Time.now.to_date) do |a|
puts ""+a.day.to_s+","+a.month.to_s+","+a.year.to_s
end
Or getting your month/year tuples:
require 'date'
result = []
Time.new(2002).to_date.upto(Time.now.to_date) do |a|
result << [a.month,a.year]
end
result.uniq!
Use the upto method from date: http://ruby-doc.org/stdlib-2.0/libdoc/date/rdoc/Date.html#method-i-upto
Upvotes: 0
Reputation: 20398
Ruby Date
supports producing successive days and offers a next_month
method which could be used to efficiently iterate over months.
Here's a generic method that adapts to the precision of your inputs:
require 'date'
def date_tuples(from,to)
prec = from.size
start = Date.new(*from)
finish = Date.new(*to)
filter_on = [:day,:mon].first(3-prec)
filter = ->(d) { filter_on.all? {|attr| d.send(attr) == 1 } }
(start..finish)
.select(&filter)
.map { |d| [d.year,d.mon,d.day].first(prec) }
end
[7] pry(main)> date_tuples([2012],[2015])
=> [[2012], [2013], [2014], [2015]]
[8] pry(main)> date_tuples([2012,10],[2013,3])
=> [[2012, 10], [2012, 11], [2012, 12], [2013, 1], [2013, 2], [2013, 3]]
[9] pry(main)> date_tuples([2012,10,25],[2012,11,6])
=> [[2012, 10, 25],
[2012, 10, 26],
[2012, 10, 27],
[2012, 10, 28],
[2012, 10, 29],
[2012, 10, 30],
[2012, 10, 31],
[2012, 11, 1],
[2012, 11, 2],
[2012, 11, 3],
[2012, 11, 4],
[2012, 11, 5],
[2012, 11, 6]]
Upvotes: 3
Reputation: 1347
I came up with this solution to generate a list of all [year,month] tuples in the range:
first=[2012,10]
last=[2013,03]
(first[0]..last[0]).to_a.product((1..12).to_a).select{|ym|(first..last).cover?(ym)}
=> [[2012, 10], [2012, 11], [2012, 12], [2013, 1], [2013, 2], [2013, 3]]
Upvotes: 1