Skip to content

Instantly share code, notes, and snippets.

@jmdeldin
Created November 13, 2014 19:45
Show Gist options
  • Save jmdeldin/31e94d6e381fbdf64a2d to your computer and use it in GitHub Desktop.
Save jmdeldin/31e94d6e381fbdf64a2d to your computer and use it in GitHub Desktop.
# 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