Created
November 13, 2014 19:45
-
-
Save jmdeldin/31e94d6e381fbdf64a2d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Pointless implementation of cal(1). | |
# | |
# Author :: Jon-Michael Deldin <[email protected]> | |
# Date :: 2011-06-27 | |
# | |
class Calendar | |
# width of calendar in spaces | |
WIDTH = 20 | |
# month lookup | |
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', | |
} | |
# day lookup, 0-based for Sakamoto's algorithm | |
DAYS = { | |
0 => 'Su', | |
1 => 'Mo', | |
2 => 'Tu', | |
3 => 'We', | |
4 => 'Th', | |
5 => 'Fr', | |
6 => 'Sa', | |
} | |
# @param [Fixnum] year | |
# @param [Fixnum] month | |
# | |
def initialize(year, month) | |
@year = year | |
@month = month | |
end | |
# Returns the month's calendar as a string. | |
# | |
# @return [String] | |
# | |
def to_s | |
s = header(@month) + "\n" | |
day_tbl = {} | |
DAYS.values.each { |d| day_tbl[d] = [] } | |
# associate days to names | |
(1 .. days_in_month(@month)).each do |i| | |
day_tbl[DAYS[day_of_week(@year, @month, i)]] << i | |
end | |
# pad as many days necessary till the start | |
day_tbl.each do |name, days| | |
break if days.first == 1 | |
day_tbl[name].unshift(nil) | |
end | |
i = 0 | |
last = 0 | |
while day_tbl.any? | |
# check sunday | |
if day_tbl[DAYS[i]].first == last + 1 | |
s << "%2d" % day_tbl[DAYS[i]].first | |
if (i+1) % 7 == 0 | |
s << "\n" | |
else | |
s << " " | |
end | |
day_tbl[DAYS[i]].shift | |
last += 1 | |
elsif day_tbl[DAYS[i]].empty? | |
day_tbl.delete(DAYS[i]) | |
else | |
s << " " | |
day_tbl[DAYS[i]].shift | |
end | |
i = (i + 1) % 7 | |
end | |
s.rstrip + "\n\n" | |
end | |
# Uses Sakamoto's algorithm <http://c-faq.com/misc/zeller.html> | |
def day_of_week(y, m, d) | |
t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] | |
y -= m < 3 ? 1 : 0 | |
(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7 | |
end | |
# Returns the number of days in a month. | |
# | |
# L = 30 + { [ M + floor(M/8) ] MOD 2 } | |
# | |
# @param [Fixnum] month | |
# @return [Array] | |
# | |
def days_in_month(month) | |
days = 30 + ( month + (month / 8.0).floor ) % 2 | |
if month == 2 | |
days - 2 | |
else | |
days | |
end | |
end | |
# Leap years occur either every four years when not divisible by 100 | |
# or every year that is divisible by 400. | |
# | |
# @return [Boolean] | |
def leap_year? | |
(@year % 4 == 0 && @year % 100 != 0) || @year % 400 == 0 | |
end | |
# Prints a header compatible with cal(1). | |
# | |
# cal prints more space on the right side if the header cannot be | |
# evenly divided into two parts. | |
# | |
# @param [Fixnum] month | |
# | |
# @return [String] | |
def header(month) | |
lbl = MONTHS[month] + ' ' + @year.to_s | |
padl = (WIDTH - lbl.length) / 2 | |
padr = WIDTH - lbl.length - padl | |
hdr = ' ' * padl + lbl | |
hdr += "\n" | |
hdr += "Su Mo Tu We Th Fr Sa" | |
end | |
end | |
if $0 == __FILE__ | |
cal = Calendar.new(2011, 06) | |
puts cal | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment