问题
How can i get the day/number go under each day? This is my code below:
class Month
attr_reader :month, :year
def initialize( month, year)
@month = month
@year = year
end
def month_names
names_of_months = {1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December'}
return names_of_months[@month]
end
def length
days_of_months = {1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31}
return days_of_months[@month]
end
def to_s
weekdays = "Su Mo Tu We Th Fr Sa"
month = "#{month_names} #{year}"
output = [
month.center(weekdays.size),
weekdays
].join("\n")
(1..length).each do |day|
output << day.to_s
end
output
end
end
Below is my result.
January 2017
Su Mo Tu We Th Fr Sa12345678910111213141516171819202122232425262728293031
回答1:
As your problem has been diagnosed, I will limit my answer to showing how you could make use of the class Date.
Code
require 'date'
DAY_WIDTH = 4
def calendar(year, month)
title = "#{Date::MONTHNAMES[month]}, #{year}".center(7*DAY_WIDTH)
puts "\n#{title}"
Date::ABBR_DAYNAMES.each { |s| print s.rjust(DAY_WIDTH) }
puts
arr = [*[' ']*Date.new(year,month).wday, *1..days_in_month(year,month)]
arr.each_slice(7) { |week|
week.each { |d| print d.to_s.rjust(DAY_WIDTH) }; puts }
end
def days_in_month(year,month)
(((month==12) ? Date.new(year+1,1) : Date.new(year,month+1)) -
Date.new(year,month))
end
[Edit: In his answer here, @spickerman expressed the number of days in the month thus: Date.new(year,month,-1).day. Better,eh?]
Examples
calendar(2015, 2)
February, 2015
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
calendar(2015, 4)
April, 2015
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
calendar(2016, 2)
February, 2016
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29
Explanation
Consider the second example:
year = 2015
month = 4
First print the title:
title = "#{Date::MONTHNAMES[month]}, #{year}".center(7*DAY_WIDTH)
#=> "April, 2015".center(28)
#=> " April, 2015 "
puts "\n#{title}"
Next print the day of week header:
Date::ABBR_DAYNAMES.each { |s| print s.rjust(DAY_WIDTH) }
#=> ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].each { |s|
#=> print s.rjust(4) }
# Sun Mon Tue Wed Thu Fri Sat
For example, "Sun".rjust(4)" #=> " Sun".
Now we need to print the days in each week. I've done that in two steps: first create an array days to be printed, including 0-6 spaces in the first week, then print each group of seven elements:
arr = [*[' ']*Date.new(year,month).wday, *1..days_in_month(year,month)]
#=> arr = [*[' ']*3, *1..30]
#=> arr = [*[' ', ' ', ' '], *1..30]
#=> [" ", " ", " ", 1, 2,..., 30]
We now divide arr into groups of seven and print each:
arr.each_slice(7) { |week|
week.each { |d| print d.to_s.rjust(DAY_WIDTH) }; puts }
For example:
' '.to_s.d.to_s.rjust(4)
#=> ' '
10.to_s.rjust(4)
#=> ' 10'
The number of days in April, 2015 is computed as follows:
(((month==12) ? Date.new(year+1,1) : Date.new(year,month+1))-
Date.new(year,month))
#=> (((4==12) ? Date.new(2016,1) : Date.new(2015,5))-Date.new(2015,4)
#=> Date.new(2015,5) - Date.new(2015,4)
#=> #<Date: 2015-05-01 ((2457144j,0s,0n),+0s,2299161j)> -
#<Date: 2015-04-01 ((2457114j,0s,0n),+0s,2299161j)>
#=> (30/1), a rational number equal to 30
回答2:
You just forgot to add your spaces and newlines to the days. Try using something like this:
def to_s
weekdays = "Su Mo Tu We Th Fr Sa"
month = "#{month_names} #{year}"
output = [
month.center(weekdays.size),
weekdays
].join("\n")
output << "\n"
i = 0
(1..length).each do |day|
i += 1
output << day.to_s
if day.to_s.length == 1
output << ' '
else
output << ' '
end
if i == 7
i = 0
output << "\n"
end
end
output
end
I added the first if statement to handle single length (1-9) dates' spacing, and the second to handle newlines (\n) after each week.
Running with:
Month.new(1, 2017).to_s
Your output will be:
January 2017
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Also, on a side note, don't forget about leap years (where February has 29 days).
回答3:
If you do not care about months that do not start on a Sunday (like you mentioned in this comment), you can just do this:
def to_s
wdays = "Su Mo Tu We Th Fr Sa"
month = "#{month_names} #{year}"
output = [ month.center(wdays.size), wdays]
(1..length).each_slice(7) do |week|
output << week.map { |day| day.to_s.rjust(2) }.join(' ')
end
output.join("\n")
end
Use each_slice(7) to slice the array with all 31 days into sub arrays of 7 days (a week). Call map to transform each day in the sub array first into a string (to_s) and second ensure to handling alignment of single digits (rjust(2)). Join all days in a week with a single whitespace.
来源:https://stackoverflow.com/questions/29908876/how-can-i-get-the-dates-to-fall-on-the-perspective-day