Comp 272 Sept 10, 2004 Program1: MiniDate Due: Fri, Sept 17 You are to implement a simplified Date class, which we will call MiniDate, in which the year is constant (we will assume it is 2004, ie a leap year). That is, MiniDate will keep track of the month and day, and allow some manipulations. The MiniDate class should also take care to maintain only valid days (though see the section below on constructors); that is, you should never have a Jan 32 or a Feb 30 or an illegal month. We will assume that January is month 1 (not 0). Your program will be graded as much on READABILITY as on correctness. A few guidelines are below. * Your indentation should be consistent * Your indentation should reflect program logic * Use appropriate whitespace (eg blank lines between definitions) * Use sensible variable names * program logic should be clean * a brief comment summarizing each function is in order IF you wish to implement something different from the way it is described here, I will consider your innovation to be an error UNLESS you clearly document it in a comment at an appropriate point! I like to encourage such innovation, generally, but you have to fess up when you depart from the spec. CONSTRUCTORS The constructor MiniDate(int month, int day) should be straightforward, although you will need to check for validity. It is recommended that if the date is invalid you call assert() to signal this; eg assert(isvalid(month,day)); To call assert(), you will need #include at the beginning of your program. You will also need a "default" constructor, ie a zero-parameter ctor. You can take one of two approaches. Perhaps the simplest is to set the default date to, say, Jan 27 (my birthday; you may want to pick your own significant date; Jan 1 will do if you wish). Another option altogether, though, is to have an internal flag _isvalid, and have the default ctor set _isvalid=false. All other operations that would access or update the current date would then be written so as to fail (eg by calling assert(_isvalid) if the date weren't valid; whenever _isvalid was true, all other operations would maintain date validity. Note that it makes *sense* here not to provide a default ctor, in that you can argue that there is no clear natural default value (there is no analogue of "zero"). However, that means all sorts of minor but annoying problems for programmers using your class (eg they can't create a nonempty vector). Give 'em a break. Finally, although it is not required, another option is a ctor from a string, supporting eg MiniDate d("Jan 27"); BASIC ACCESSORS Your class should provide day() and month() functions to return the current day and month. BASIC MUTATORS Note that you can *not* provide mutators setday() and setmonth(); there is no practical way to do that while maintaining the invariant that valid MiniDates stay valid. That is, if d is Jan 31, then d.setmonth(2) is invalid, even if the programmer intends to follow it with d.setday(29). Similarly, if d is Feb 29, then d.setday(31) is invalid even if the intent is to follow it with d.setmonth(1). Instead, you should provide setdate(int month, int day). advance(ndays) This advances the day by ndays; that is, if the MiniDate d is Jan 26 then d.advance(1) sets the date to Jan 27 and d.advance(31) sets the date to Feb 26. Note that if d is Dec 25, then d.advance(10) should set the date to Jan 4; that is, we wrap around. (An alternative is to have the date become "invalid"). When pushed to extremes this gets into awkward problems about leap years etc (if this is a leap year, what is d.advance(366)?); you can either disallow wrapping past Feb 28 or you can just ignore this. daysbetween(MiniDate d1) This returns the number of days between the current MiniDate and d1; that is, if d is May 5 and d1 is Jun 5 then d.daysbetween(d1) should return 31 and d1.daysbetween(d) should return -31. dayofweek(MiniDate d1, int weekday), OR setdayofweek(MiniDate d1, int weekday); int weekday(); Without keeping track of the year, we obviously can't report what day of week Jan 27 is. However, what we *can* do is report what day of the week a given MiniDate d is, assuming that the MiniDate d1 occurs on the weekday given (sun=1, mon=2, ..., sat=7). That is, if d is Mar 10, and d1 is Jan 27, known to fall on Tuesday, day 3, then dayofweek(d1, 3) would return 4: if Jan 27 falls on a Tuesday (in a leap year) then Mar 10 falls on a Wednesday. Alternatively, you can implement the second arrangement: d.setdayofweek(MiniDate d1, int weekday) sets an internal field (eg _weekday) to the correct day for d (not necessarily the value of weekday; that represents the correct day for d1!). The call d.weekday() then returns that day. The _weekday field should be updated correctly if we call d.advance(n) to change the date. _weekday should be, say, -1 if unset; unset values should not be subject to update. IMPLEMENTATION Your MiniDate class will have, of course, private variables to represent the date. But you have a choice: you can use _month and _day (the "obvious" choice), or you can use the cumulative number of days since January 1 (so that Feb 5 might be implemented internally as _cumulativeday=36). Both alternatives have their advantages; note that to implement advance(numdays) you will at least need to convert back and forth between the month/day format and the cumulative-day format. bool isvalid() I recommend a *private* member function is_valid to check the validity of your date representation, at least if you implement using _day/_month. NOte that if you implement using the cumulative-day format, you don't need this (except possibly for setdate()). WHAT YOU SHOULD TURN IN Your MiniDate class, with nontrivial member function implementations *outside* the class declaration. You should also, in the same file, have a main() that illustrates use of each of the member functions. All the basic accessors and mutators should be tested, as well as: * advance(n), with various n between 0 and 366, and n<0, and n>366, and wraparound at yearend * daysbetween(), for various near, far, and negative combinations * dayofweek, or equiv HELPER FUNCTIONS The following are provided to illustrate certain parts of C++ and to help simplify life a little. Note that they are written to handle nonleapyears as well as leapyears, but that the *default value* of the second parameter is "true". That is, if the second parameter is omitted the call is still legal, and a call to daysinmonth(2) returns 29. int daysinmonth1(int month, bool leapyear=true) { int days; if (month<1 || month>12) return -1; switch (month) { case 1: days = 31; break; case 2: if (leapyear) days = 29; else days=28; break; case 3: days = 31; break; case 4: days = 30; break; case 5: days = 31; break; case 6: days = 30; break; case 7: days = 31; break; case 8: days = 31; break; case 9: days = 30; break; case 10: days = 31; break; case 11: days = 30; break; case 12: days = 31; break; } /* end switch */ return days; } int daysinmonth2(int month, int leapyear=true) { // the following is a "native" array, days, initialized // statically (ie once, at compile time, not run time) // so that days[month-1] = # of days in given month. // Native arrays, like vectors, always begin at 0, // so days[] goes from 0 to 11, hence the need for month-1. static int days[] = {31,28,31,30,31,30,31,31,30,31,30,31}; if (month<1 || month>12) return -1; int d = days[month-1]; if (month==2 && leapyear) d++; return d; }