/* cal.cpp - Calendar program. Matt Mahoney, mmahoney@cs.fit.edu This program prints a calendar for any given month and year, for instance: cal Feb 2002 Prints a calendar for February 2002 cal Mar Prints a calendar for March of the current year cal Prints a calendar for the current month and year cal +10 Prints a calendar for 10 months from now cal -20 Prints a calendar for 20 months ago The name of the month can be any unambiguous prefix, and is not case sensitive, for example "F" or "febr" for February, or "Ja" (but not "J") for January. The year must include all digits. Assumes Georgian calendar back to Jan. 1 AD; prior years are not valid. */ #include #include #include #include #include #include using namespace std; // true if s1 is a prefix of s2, not case sensitive bool prefix(const string& s1, const char* s2) { for (int i=0;; ++i) { if (i==int(s1.size())) return true; if (s2[i]==0 || tolower(s1[i])!=tolower(s2[i])) return false; } } // A Month represents a month and year, which prints as a calendar class Month { public: Month(const string& m="", int y=0); // Defaults: this month and year // m is the month name or prefix, not case sensitive class Ambiguous {}; // Thrown if m matches >1 month name, e.g. "JU" class Invalid {}; // Thrown if m matches no month name, e.g. "X" void print(ostream& out) const; // Print a one month calendar to out void add(int n) {now+=n;} // Add n months (may be negative) private: int now; // year * 12 + month (0=Jan, 11=Dec), e.g. 24001 means Feb. 2000 static const char* const monthNames[12]; // Table of month names }; const char* const Month::monthNames[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; Month::Month(const string& m, int y) { // m = prefix of month name, y = year int mon=12; // Get month as 0-11 (0=Jan, 11=Dec), 12 = unknown if (y<1) { // Default year: use current year time_t t=time(0); tm* tp=localtime(&t); y=1900+tp->tm_year; if (m=="") // Default month: use current month mon=tp->tm_mon; } // Look up the month name if not "" if (mon==12) { for (int i=0; i<12; ++i) { // Find all matches of m to monthNames if (prefix(m, monthNames[i])) { if (mon==12) // First matching prefix mon=i; else // Second matching prefix throw Ambiguous(); } } if (mon==12) throw Invalid(); // No match found } now=y*12+mon; } // Print the calendar void Month::print(ostream& out) const { if (now<12) throw Invalid(); // Must be at least Jan 1 AD. const int year=now/12; const int mon=now%12; // Month, 0 = Jan, ... 11 = Dec // Find weekday of first day of month: 0 = Sun, 1 = Mon,... 6 = Sat. const int len[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // Days in month const bool leapyear = year%400==0 || (year%4==0 && year%100!=0); int weekday=year*365+year/4-year/100+year/400; // Days from year 0 to Jan 1 for (int i=0; i1 && (argv[1][0]=='+' || argv[1][0]=='-')) // cal +n or -n m.add(atoi(argv[1])); else if (argc > 2) m = Month(argv[1], atoi(argv[2])); // cal month year else if (argc == 2) m = Month(argv[1]); // cal month m.print(cout); } catch (Month::Ambiguous x) { cerr << "Month name is ambiguous\n"; } catch (Month::Invalid x) { cerr << "Month name is invalid\n"; } return 0; }