Cal
January 1, 2010
Happy New Year! And best wishes for a healthy and prosperous year for all my readers and their families.
On the first day of a new year it seems appropriate to write an exercise based on calendars. Unix provides a cal command to print calendars. There are several forms:
cal— prints a calendar for the current monthcalyear — prints a twelve-month calendar for the specified year; note that year 10 occurred during the time of Christ, so you must specify 2010 for the current yearcalmonth year — prints a calendar for the specified month and year; the month is given as a number from 1 to 12cal -3— prints a three-month calendar for the prior month, current month and next month
The current date is highlighted wherever it appears.
Your task is to implement cal. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
[…] today’s Programming Praxis exercise we have to implement the Unix utility cal, which prints calendars. […]
My Haskell solution (see http://bonsaicode.wordpress.com/2010/01/01/programming-praxis-cal/ for a version with comments):
import Data.List import Data.List.Split import qualified Data.Text as T import Data.Time import System.Environment import System.Locale days :: Integer -> Int -> [Day] days y m = map (fromGregorian y m) [1..gregorianMonthLength y m] fmt :: FormatTime t => String -> t -> String fmt = formatTime defaultTimeLocale monthCal :: Integer -> Int -> String monthCal y m = unlines $ (T.unpack . (T.center 20 ' ') . T.pack . fmt "%B %Y" $ fromGregorian y m 1) : "Su Mo Tu We Th Fr Sa" : (map unwords . take 6 . chunk 7 $ replicate (read . fmt "%w" . head $ days y m) " " ++ map (fmt "%e") (days y m) ++ repeat " ") showCal :: [String] -> IO () showCal = putStrLn . unlines . map (unlines . map (intercalate " ") . transpose . map lines) . chunk 3 surround :: UTCTime -> [String] surround d = map ((\(y, m, _) -> monthCal y m) . toGregorian . (`addGregorianMonthsRollOver` utctDay d)) [-1..1] main :: IO () main = do args <- getArgs now <- getCurrentTime let (curYear, curMonth) = read $ fmt "(%Y,%m)" now case args of [y,m] -> showCal [monthCal (read y) (read m)] ["-3"] -> showCal $ surround now [y] -> showCal $ map (monthCal $ read y) [1..12] [] -> showCal [monthCal curYear curMonth] _ -> error "Invlaid parameters"There’s a bug here: compare the output of your cal with the standard one (either BSD or GNU) on September 1752 or any earlier date.
“Give us back our eleven days!”
C++ style
#include
#include
#include
using namespace std;
const int MONTHS_IN_YEAR = 12;
const int DAYS_IN_WEEK = 7;
const int MAX_DAYS_IN_MONTH = 31;
const int MIN_DAYS_IN_MONTH = 28;
const int LEAP_YEAR_FACTOR = 4;
const int DAY_WIDTH = 3;
const int JAN = 1;
const int FEB = 2;
const int MAR = 3;
const int APR = 4;
const int MAY = 5;
const int JUN = 6;
const int JUL = 7;
const int AUG = 8;
const int SEP = 9;
const int OCT = 10;
const int NOV = 11;
const int DEC = 12;
void PrintMonthHeader(int month);
string MonthString(int month);
void PrintMonthBody(int year, int month, int& day);
void PrintMonthDates(int& day, int lastday);
void PrintSelectedMonths(int year, int start_month,
int day, int total_months);
int main()
{
int month, totalMonths, startDay, year;
cout <> year;
while (year != 0)
{
cout <> month;
cout <> totalMonths;
cout << "What day of the week does the starting month fall on?\n";
cout <> startDay;
PrintSelectedMonths(year, month, startDay, totalMonths);
cout <> year;
}
return 0;
}
//———————————————————————–
// This function uses the integer month to print out that months name.
// It also prints out the one letter abbreviations for Sunday
// through Saturday and the underscores under them.
// Parameters: (in)
//———————————————————————–
void PrintMonthHeader(int month)
{
cout << "\n" << " " << MonthString(month) << '\n';
cout << " S M T W T F S\n";
cout < 0)
{
if (day == DAYS_IN_WEEK)
daySpaces = 0;
else
{
cout << " ";
daySpaces–;
}
}
while (daysInMonth <= lastday)
{
if (day % DAYS_IN_WEEK == 0 && daysInMonth != 1)
{
cout << "\n";
day = 0;
}
cout << setw(DAY_WIDTH) << daysInMonth;
daysInMonth++;
day++;
}
cout << endl;
return;
}
//———————————————————————–
// This function prints out the calendar for the given number
// of months (total_months), the given year, using the
// start_month and starting day (day). The function repeatedly
// calls PrintMonthHeader and PrintMonthBody.
// Parameters: (in, in, in, in)
//———————————————————————–
void PrintSelectedMonths(int year, int start_month,
int day, int total_months)
{
int monthCount = 0;
cout << "\n\n" << " " << year << "\n" ;
while(monthCount < total_months)
{
PrintMonthHeader(start_month);
PrintMonthBody(year, start_month, day);
if (start_month % 12 == 0 && total_months != 12)
{
year++;
cout << "\n\n\n" << " " << year << "\n" ;
monthCount = 1;
start_month = 0;
}
monthCount++;
start_month++;
}
return;
}
import calendar import datetime as dt import itertools as it import re calendar.setfirstweekday(calendar.SUNDAY) def onemonth( month=None, year=None ): today = dt.date.today() month = month or today.month year = year or today.year mstring = re.sub( r"\n", r" \n ", calendar.month( year, month ) ) if year == today.year and month == today.month: mstring = re.sub( r" (%2s) "%today.day, r"[\1]", mstring ) return mstring.splitlines() def threemonth( month=None, year=None ): today = dt.date.today() month = month or today.month year = year or today.year m1 = onemonth( month-1, year ) if month > 1 else onemonth( 12, year-1 ) m2 = onemonth( month, year ) m3 = onemonth( month+1, year ) if month < 12 else onemonth( 1, year+1 ) fmt = "{0:22}{1:22}{2:22}".format return it.starmap( fmt, it.izip_longest( m1, m2, m3, fillvalue='' ) ) def twelvemonths(year=None): return it.chain( *( threemonth( m, year ) for m in (2, 5, 8, 11) ) ) def cal( *args ): if len( args ) == 1: year = int( args[0] ) if year == -3: rows = threemonth() else: rows = twelvemonths( year ) else: rows = onemonth( *map( int, args ) ) return '\n'.join( rows ) if __name__ == '__main__': from sys import argv print cal( *argv[1:] )