Charts with empty months - ruby-on-rails

I have a hash
{"Apr 2016"=>6.0, "Aug 2016"=>7.5, "Jan 2017"=>8.666666666666666, "Apr 2017"=>7.333333333333333, "May 2017"=>7.571428571428571, "Jun 2017"=>6.75, "Jul 2017"=>6.7272727272727275}
I want display a chart line but the empty months in my hash create ugly chart,
I would know how get empty months and give previous value has value to get something like
{"Apr 2016"=>6.0, "May 2016"=>6, "June 2016"=>6, "July 2016"=>6 "Aug 2016"=>7.5, "Jan 2017"=>8.666666666666666...}
UPDATE: I get all values but i dont know how atribute the previous value when the value is empty, i tried many things but nothing work

Code
require 'date'
def fill_in(h)
month, date_last = h.keys.map { |s| Date.strptime(s, '%b %Y') }.minmax
h_out = {}
last = nil
loop do
str = month.strftime('%b %Y')
h_out[str] = h.fetch(str, last)
last = h_out[str]
return h_out if month == date_last
month = month >> 1
end
end
Example
h = { "May 2016"=>6.0, "Aug 2016"=>7.5, "Jan 2017"=>8.6, "Nov 2016"=>7.3 }
fill_in(h)
#=> {"May 2016"=>6.0, "Jun 2016"=>6.0, "Jul 2016"=>6.0,
# "Aug 2016"=>7.5, "Sep 2016"=>7.5, "Oct 2016"=>7.5,
# "Nov 2016"=>7.3, "Dec 2016"=>7.3, "Jan 2017"=>8.6}
Explanation
See Date::strptime, Date#strftime, Date#>>, Enumerable#minmax and Hash#fetch.
Let's go though the steps for h given in the example.
month, date_last = h.keys.map { |s| Date.strptime(s, '%b %Y') }.minmax
#=> [#<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)>,
# #<Date: 2017-01-01 ((2457755j,0s,0n),+0s,2299161j)>]
month
#=> #<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)>
date_last
#=> #<Date: 2017-01-01 ((2457755j,0s,0n),+0s,2299161j)>
h_out = {}
last = nil
Perform the loop calculation once
str = month.strftime('%b %Y')
#=> #<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)>.strftime('%b %Y')
#=> "May 2016"
h_out[str] = h.fetch("May 2016", nil)
#=> h.fetch(str, last)
#=> 6.0
last = h_out[str]
#=> 6.0
return h_out if month == date_last
# <do not return>
month = month >> 1
#=> #<Date: 2016-06-01 ((2457541j,0s,0n),+0s,2299161j)>
Now proceed through the loop once more.
str = month.strftime('%b %Y')
#=> "Jun 2016"
h_out[str] = h.fetch(str, last)
#=> 6.0
This time fetch uses its default (last #=> 6.0) because h has no key "Jun 2016".
last = h_out[str]
#=> 6.0
return h_out if month == date_last
# <do not return>
month = month >> 1
#=> #<Date: 2016-07-01 ((2457571j,0s,0n),+0s,2299161j)>
The remaining calculations are similar.

Please read the Update as well.
You will need to fill the gaps:
require 'date'
data = {'Apr 2016' => 6.0, 'Aug 2016' => 7.5, 'Jan 2017' => 8.666666666666666, 'Apr 2017' => 7.333333333333333, 'May 2017' => 7.571428571428571, 'Jun 2017' => 6.75, 'Jul 2017' => 6.7272727272727275}
range = Date.parse('1.4.2016')..Date.parse('1.7.2017')
range.each do |date|
key = date.strftime('%b %Y')
data[key] ||= 0
end
This creates a date range with start and end date and then iterates it. It creates a String from the date (strftime) and checks if it is already in the hash (||=) and if not, assigns it with value set to 0
Depending on how you render the chart and what you actually want to display, 0 might be the wrong value, perhaps you'll need to set it to nil.
UPDATE:
I just realized that this is not the most efficient way to do this because the range will contain a entry per day, not per month. I'll leave the code in the answer but you should use something like:
require 'date'
data = {'Apr 2016' => 6.0, 'Aug 2016' => 7.5, 'Jan 2017' => 8.666666666666666, 'Apr 2017' => 7.333333333333333, 'May 2017' => 7.571428571428571, 'Jun 2017' => 6.75, 'Jul 2017' => 6.7272727272727275}
current = Date.parse('1.4.2016')
stop = Date.parse('1.7.2017')
while current <= stop do
key = current.strftime('%b %Y')
data[key] ||= 0
current = current >> 1
end

Related

Rails - Convert string to time

I'm trying to parse these string into time: "3 on Jun 20", "Jun 20 at 3", "Jun 20 at 300".
Using DateTime.parse didnt parse "3", "300" into "3:00 AM", it just returns Wed, 20 Jun 2018 00:00:00 +0000.
Anyone has any idea to parse these integer into time?
There's Chronic, a "natural language date/time parser":
require 'chronic'
Chronic.parse('3 on Jun 20') #=> 2018-06-20 15:00:00 +0200
Chronic.parse('Jun 20 at 3') #=> 2018-06-20 15:00:00 +0200
Chronic.parse('Jun 20 at 300') #=> 2018-06-20 15:00:00 +0200
Just out of curiosity, trying to reinvent chronic in 4 LOCs :)
["3 on Jun 20", "Jun 20 at 3", "Jun 20 at 300"].map do |dt|
d, t = dt.split(/\s+at\s+/i)
t, d = dt.split(/\s+on\s+/i) unless t
return [dt] unless t && d
t = t[0..-3] + (t[-2..-1] ? ":" << t[-2..-1] : t[/.{,2}\z/] + ":00")
[d, t] # [["Jun 20", "3:00"], ["Jun 20", "3:00"], ["Jun 20", "3:00"]]
end.map { |dt| DateTime.parse dt.join ' ' }
Use strptime to parse a custom format:
DateTime.strptime("3 on Jun 20", "%H on %b %d")
https://ruby-doc.org/stdlib-2.5.0/libdoc/date/rdoc/DateTime.html#method-c-strptime

Get empty months in hash

I have a hash of Month->Value
{"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
I would know what is the best way to get all empty months between the first and the last value
I would like something like
{"Apr 2016"=>6, May 2016=>0, Jun 2016=>0 .... "Aug 2016"=>9, "Sep 2016" => 0 "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
Here's another way, using each_with_object:
def add_months(dates)
min, max = dates.keys.map { |date| Date.parse(date) }.minmax
range = (min..max).map { |date| date.strftime("%b %Y") }.uniq
range.each_with_object({}) { |date, result| result[date] = dates[date] || 0 }
end
Output:
dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
add_months(dates)
#=> {
# "Apr 2016"=>6,
# "May 2016"=>0,
# "Jun 2016"=>0,
# "Jul 2016"=>0,
# "Aug 2016"=>9,
# "Sep 2016"=>0,
# "Oct 2016"=>0,
# "Nov 2016"=>0,
# "Dec 2016"=>0,
# "Jan 2017"=>11,
# "Feb 2017"=>0,
# "Mar 2017"=>0,
# "Apr 2017"=>6,
# "May 2017"=>9,
# "Jun 2017"=>1,
# "Jul 2017"=>9
# }
Code
require 'date'
def fill_in_missing_months(dates)
date_fmt = '%b %Y'
fm, lm = dates.keys.
minmax_by { |date| Date.strptime(date, date_fmt) }.
map { |date| Date.strptime(date, date_fmt) }
(0..12*(lm.year-fm.year) + lm.month-fm.month).each_with_object({}) do |_,h|
str = fm.strftime(date_fmt)
h[str] = dates.fetch(str, 0)
fm >>= 1
end
end
Example
dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6,
"May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9}
fill_in_missing_months(dates)
#=> {"Apr 2016"=>6, "May 2016"=>0, "Jun 2016"=>0, "Jul 2016"=>0, "Aug 2016"=>9,
# "Sep 2016"=>0, "Oct 2016"=>0, "Nov 2016"=>0, "Dec 2016"=>0, "Jan 2017"=>11,
# "Feb 2017"=>0, "Mar 2017"=>0, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1,
# "Jul 2017"=>9}
Explanation
Experienced Rubiests: A gory-detail-follows advisory has been issued, so you may wish to skip the rest of my answer.
Ruby expands fm >>= 1 to fm = fm >> 1 when parsing the code. Date#>> advances the date by the number of months given by its argument, which here is 1.
In addition to :>>, see the docs for the methods Integer#times, Hash#fetch, Date::strptime, Date#strftime, Date#year, Date#month, Enumerator#with_object and Enumerable#minimax_by (not including the more common methods such as Enumerable#map). Recall # in Date#year denotes an instance method whereas :: in [Date::strptime] indicates a class method.
For dates given in the example, the steps are as follows.
date_fmt = '%b %Y'
b = dates.keys
#=> ["Apr 2016", "Aug 2016", "Jan 2017", "Apr 2017", "May 2017",
# "Jun 2017", "Jul 2017"]
c = b.minmax_by { |date| Date.strptime(date, date_fmt) }
#=> ["Apr 2016", "Jul 2017"]
fm, lm = c.map { |date| Date.strptime(date, date_fmt) }
#=> [#<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)>,
# #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>]
fm #=> #<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)>
lm #=> #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>]
d = 0..12*(lm.year-fm.year) + lm.month-fm.month
#=> 0..15
e = d.each_with_object({})
#=> #<Enumerator: 0..15:each_with_object({})>
We can see the values that will be generated by e and passed to the block by converting it to an array, using Enumerable#entries (or Enumerable#to_a).
e.entries
#=> [[0, {}], [1, {}],..., [15, {}]]
The hashes in these tuples is initially empty, but will be filled in as block calculations are performed.
The first element is generated by e, which is passed to the block and the block variables are set equal to its value, using a process called disambiguation or decomposition to stab out the part associated with each block variable.
_,h = e.next
#=> [0, {}]
h #=> {}
I've used _ as the first block variable to signify that it (the index) is not used in the block calculation. Continuing,
str = fm.strftime(date_fmt)
#=> "Apr 2016"
h[str] = dates.fetch(str, 0)
#=> 6
h #=> {"Apr 2016"=>6}
In this case dates has a key "Apr 2016", so h["Apr 2016"] is set equal to dates["Apr 2016"]. In other cases dates will not have key equal to str ("May 2016", for example), so the value will be set equal to fetch's default value of 0.
fm >>= 1
#=> #<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)>
fm is now May, 2016. The remaining calculations are similar.
You can use the following method to do the above :
def normalize_months(month_values)
ordered_months = month_values.keys.map do |m| Date.parse(m) end.sort.map do |s| s.strftime('%b %Y') end
normalized = []
current = ordered_months.first
while current != ordered_months.last do
normalized << current
current = Date.parse(current).next_month.strftime('%b %Y')
end
result = {}
normalized.each do |g| result[g] = month_values[g].nil? ? 0 : month_values[g] end
result
end
Do a require 'date' above this if you are doing this using pure ruby.
I would suggest a method like the following:
require 'date'
def add_missing_months(dates)
# get all months
months = dates.keys.map{|m| Date.parse(m)}
# get min and max
min = months.min
max = months.max
# collect all missing months
missing_months = {}
while min < max
min = min.next_month
missing_months[min.strftime('%b %Y')] = 0 unless months.include?(min)
end
# merge hashes
dates.merge(missing_months)
end

Get unique array of month and years between two dates

I have two Date objects, for example:
first = Fri, 02 Dec 2016
last = Wed, 01 Mar 2017
What is the most efficient way to get a unique array of months and years between them? In this case I'm after:
Dec 2016
Jan 2017
Feb 2017
Mar 2017
require 'date'
def doit(first, last)
first = first << 1
(12*last.year + last.month - 12*first.year - first.month + 1).
times.map { |i| (first = first >> 1).strftime("%b %Y") }
end
first = Date.parse('Fri, 02 Dec 2016')
last = Date.parse('Wed, 01 Mar 2017')
doit(first, last)
#=> ["Dec 2016", "Jan 2017", "Feb 2017", "Mar 2017"]
Note that
(12*last.year + last.month - 12*first.year - first.month + 1)
equals the number of months covered by the range.
You could create an array of dates, then use strftime to set the correct format, and uniq to avoid repeated values, like this:
(first..last).map{ |date| date.strftime("%b %Y") }.uniq
#=> ["Dec 2016", "Jan 2017", "Feb 2017", "Mar 2017"]
Since user asks for most efficient way (and just for fun) here's a simple benchmark of the proposed solutions:
require 'benchmark'
Benchmark.bmbm(10) do |bm|
bm.report('Cary') do
first = Date.new(1000, 1, 1)
last = Date.new(2100, 1, 1)
def doit(first, last)
(12*last.year + last.month - 12*first.year - first.month).times.map do
first.strftime("%b %Y")
first = first >> 1
end
end
doit(first, last)
end
bm.report('Simple Lime') do
first = Date.new(1000, 1, 1)
last = Date.new(2100, 1, 1)
dates = []
while first.beginning_of_month < last.end_of_month
dates << first.strftime("%b %Y")
first = first.next_month
end
end
bm.report('Máté') do
first = Date.new(1000, 1, 1)
last = Date.new(2100, 1, 1)
(first.beginning_of_month..last).map { |d| d.strftime("%b %Y") if d.day == 1 }.compact
end
bm.report('Gerry/Dan') do
first = Date.new(1000, 1, 1)
last = Date.new(2100, 1, 1)
(first..last).map{ |date| date.strftime("%b %Y") }.uniq
end
end
Results:
Rehearsal -----------------------------------------------
Cary 0.020000 0.000000 0.020000 ( 0.025968)
Simple Lime 0.190000 0.000000 0.190000 ( 0.192860)
Máté 0.460000 0.020000 0.480000 ( 0.481839)
Gerry/Dan 0.810000 0.020000 0.830000 ( 0.835931)
-------------------------------------- total: 1.520000sec
user system total real
Cary 0.020000 0.000000 0.020000 ( 0.024871)
Simple Lime 0.150000 0.000000 0.150000 ( 0.150696)
Máté 0.390000 0.010000 0.400000 ( 0.398637)
Gerry/Dan 0.710000 0.010000 0.720000 ( 0.711155)
Not a pretty one-liner, but also doesn't walk through each day individually so should be a fair bit faster for large ranges.
first = Date.new(2016, 12, 2)
last = Date.new(2017, 3, 1)
dates = []
while first.beginning_of_month < last.end_of_month
dates << first.strftime("%b %Y")
first = first.next_month
end
puts dates.inspect
# => ["Dec 2016", "Jan 2017", "Feb 2017", "Mar 2017"]
Assuming these variables:
first = Date.new(2016, 12, 2)
last = Date.new(2017, 3, 1)
A one-liner solution:
(first.beginning_of_month..last).map { |d| d.strftime("%b %Y") if d.day == 1 }.compact
#=> ["Dec 2016", "Jan 2017", "Feb 2017", "Mar 2017"]
start_date=Date.new(2016,12,2)
end_date=Date.new(2017,3,1)
(start_date..end_date).map{ |d| d.strftime("%b %Y") }.uniq
=> ["Dec 2016", "Jan 2017", "Feb 2017", "Mar 2017"]

Calculate time from an array and convert to time zone

I have an array bed_times with Time instances in UTC format:
bed_times = [
Time.utc(2015, 12, 10, 5, 58, 24),
Time.utc(2015, 12, 9, 3, 35, 28),
Time.utc(2015, 12, 8, 6, 32, 26),
Time.utc(2015, 12, 7, 1, 43, 28),
Time.utc(2015, 12, 5, 7, 49, 30),
Time.utc(2015, 12, 04, 7, 2, 30)
]
#=> [2015-12-10 05:58:24 UTC,
# 2015-12-09 03:35:28 UTC,
# 2015-12-08 06:32:26 UTC,
# 2015-12-07 01:43:28 UTC,
# 2015-12-05 07:49:30 UTC,
# 2015-12-04 07:02:30 UTC]
I am trying to get the average bedtime, but I'm not getting the correct result
ave = Time.at(bed_times.map(&:to_f).inject(:+) / bed_times.size)
result is
2015-12-07 01:26:57 -0800
which is not correct. Also, I want to then convert the average time to a different time zone
I tried
Time.zone = 'Pacific Time (US & Canada)'
Time.zone.parse(ave.to_s)
2015-12-07 01:26:57 -0800
This is not correct either.
You have to calculate the average on the gap from midnight.
A not elegant (but fast) solution could be:
# Keep only time
bed_times.map! { |bt| Time.parse(bt.split(" ")[1]) }
# calculate the gap from 00:00:00
gap_from_midnight = bed_times.map do |bt|
if bt > Time.parse("12:00:00")
gap = (bt.to_f - Time.parse("24:00:00").to_f)
else
gap = (bt.to_f - Time.parse("00:00:00").to_f)
end
gap.to_i
end
# average in sec
avg_in_sec = gap_from_midnight.inject(:+) / bed_times.size
# average in UTC time zone
avg = Time.at(avg_in_sec).utc # => 1970-01-01 05:26:57 UTC (result for bed_times array)
# average in PST time zone (see note)
avg_pst = Time.parse(avg.to_s).in_time_zone("Pacific Time (US & Canada)") # => Wed, 31 Dec 1969 21:26:57 PST -08:00 (result for bed_times array)
# Keep only time
avg_pst.strftime("%H:%M:%S") # => "21:26:57" (result for bed_times array)
With your bed_times array (with the values as a string)
bed_times = [
"2015-12-10 05:58:24 UTC",
"2015-12-09 03:35:28 UTC",
"2015-12-08 06:32:26 UTC",
"2015-12-07 01:43:28 UTC",
"2015-12-05 07:49:30 UTC",
"2015-12-04 07:02:30 UTC"
]
the average is :
05:26:57 in UTC zone
21:26:57 in PST zone
With another array like this
bed_times = [
"2015-12-10 01:00:00 UTC",
"2015-12-09 23:00:00 UTC",
"2015-10-19 18:00:00 UTC",
]
the average is:
22:00:00 in UTC zone
14:00:00 in PST zone
note: .in_time_zone is a helper from ActiveSupport::TimeWithZone
http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
As I understand, you are given an array arr that contains the UTC times of bedtimes in areas that are on PST time. You wish to compute the average bedtime.
Code
def avg_bedtime(arr)
avg = Time.at(arr.reduce(0) do |t,s|
lt = Time.parse(s).localtime("-08:00")
t + Time.new(2000, 1, (lt.hour < 12) ? 2 : 1, lt.hour, lt.min, lt.sec, "-08:00").to_f
end/arr.size)
"%d:%d:%d" % [avg.hour, avg.min, avg.sec]
end
Example
arr = ["2015-12-10 08:58:24 UTC", "2015-12-09 03:35:28 UTC",
"2015-12-08 06:32:26 UTC", "2015-12-07 01:43:28 UTC",
"2015-12-05 07:49:30 UTC", "2015-12-04 07:02:30 UTC"]
I've changed the first time in this array to make it more interesting.
avg_bedtime(arr)
#=> "21:56:57"
Explanation
Let's begin by converting these strings to time objects:
utc = arr.map { |s| Time.parse(s) }
#=> [2015-12-10 08:58:24 UTC, 2015-12-09 03:35:28 UTC, 2015-12-08 06:32:26 UTC,
# 2015-12-07 01:43:28 UTC, 2015-12-05 07:49:30 UTC, 2015-12-04 07:02:30 UTC]
Recalling that PST is 8 hours later than UTC, we can use Time.localtime to convert to PST:
pst = utc.map { |t| t.localtime("-08:00") }
#=> [2015-12-10 00:58:24 -0800, 2015-12-08 19:35:28 -0800,
# 2015-12-07 22:32:26 -0800, 2015-12-06 17:43:28 -0800,
# 2015-12-04 23:49:30 -0800, 2015-12-03 23:02:30 -0800]
I will refer to bedtimes before noon to be "late" bedtimes and those later in the day to be "early" bedtimes. (This is of course arbitrary. If, for example, some of the individuals are shift workers, this could be a problem.) As you see, the first element of pst is a late bedtime and all others are early bedtimes.
I will now convert these time objects to time objects having the same time of day but a different date. Early bedtime objects will be assigned an arbitrary date (say, January 1, 2000) and late bedtime objects will be one day later (January 2, 2000):
adj = pst.map { |t| Time.new(2000, 1, (t.hour < 12) ? 2 : 1, t.hour, t.min, t.sec, "-08:00") }
#=> [2000-01-02 00:58:24 -0800, 2000-01-01 19:35:28 -0800, 2000-01-01 22:32:26 -0800,
# 2000-01-01 17:43:28 -0800, 2000-01-01 23:49:30 -0800, 2000-01-01 23:02:30 -0800]
We can now convert the time objects to seconds since the epoch:
secs = adj.map { |t| t.to_f }
#=> [946803504.0, 946784128.0, 946794746.0, 946777408.0, 946799370.0, 946796550.0]
compute the average:
avg = secs.reduce(:+)/arr.size
#=> 946792617.6666666
convert back to a time object for the PST zone:
tavg = Time.at(avg)
#=> 2000-01-01 21:56:57 -0800
and, lastly, extract the time of day:
"%d:%d:%d" % [tavg.hour, tavg.min, tavg.sec]
# "21:56:57

Generating a list of weeks and months given a date-range

Given a date-range, how can I generate a sequential list of
weeks
months
that are included in the date range? For example, if the date-range is Jan 15 - Apr 29, then for the weeks it should be
15 Jan - 17, 18 - 24, 25 - 31 ... 19 Apr - 25, 26 - 29 Apr
where Jan 18th is Sunday, so in this case the week starts on Sunday, but it can also be Monday, doesn't matter. And for the months:
15 Jan - 31, 1 Feb - 28, 1 - 31 March, 1 - 29 Apr
What's the easiest way to do it?
Not sure if this is the easiest way, but it is a way. (Lazy way?)
Months: You start with a range like:
range = (Date.today..6.months.from_now)
Then you can get each month like:
months = range.to_a.map(&:beginning_of_month).uniq
#lookup docs on strftime to get exactly what you want here
months.map { |date| date.strftime('%Y %b') }
For weeks, you can start with months:
weeks = months.flat_map { |date|
m_weeks = [date]
until (week = date + 1.week) && week.month > date.month
m_weeks << week
end
m_weeks
}
#enter some string in strftime (im too lazy to look it up now)
weeks.map { |date| date.strftime('') }
I didn't test this, but I think it should work. Anyway, there are many ways to do this.
Ok so to get all weeks in the month by weeks Monday - Sunday, you can do something like this:
m_days = (Date.today.beginning_of_month..Date.today.end_of_month).to_a
day_offset = m_days.first.wday - 1
day_offset.times { m_days.unshift(nil) }
Then if you want to get an array of strings that have the first day of the week to the last day of the week you could do this.
weeks = []
m.days.each_slice(7) do |w_days|
w_days.compact!
weeks << "#{w_days.first} - #{w_days.last}"
end
You have two problems:
determining the beginning and end of each week between the start date and end date.
formatting the dates for display.
I have addressed the first problem only, but have provided all the information required to format the result in any way desired.
We are given:
start_date = "Jan 15, 2015"
end_date = "Apr 29, 2015"
We then compute the array weeks as follows:
require 'date'
days = (Date.parse(start_date)..Date.parse(end_date)).to_a
weeks = ([days.shift(7-days.first.wday)].concat(
days.each_slice(7).to_a)).map { |w|
[w.first, w.last].map { |d| [d.year, d.month, d.day, d.wday] } }
# => [[[2015, 1, 15, 4], [2015, 1, 17, 6]],
# [[2015, 1, 18, 0], [2015, 1, 24, 6]],
# [[2015, 1, 25, 0], [2015, 1, 31, 6]],
# [[2015, 2, 1, 0], [2015, 2, 7, 6]],
# ...
# [[2015, 4, 19, 0], [2015, 4, 25, 6]],
# [[2015, 4, 26, 0], [2015, 4, 29, 3]]]
Each element of weeks corresponds to a week and contains two arrays, one for the first day of the week (a Sunday, for all weeks after the first); the second for the last day of the week (a Saturday for all weeks other than the last). The arrays for individual days contain the year, month (1-12), day of month and day of week (Sunday: 0, Monday: 1,...Saturday: 6).
You can then use the Date class constants (search for "constants" at Date) to format the results.

Resources