C++ Intro

Assignment 3: serial Julian date


Description

In this assignment you will write a program that uses dates of Gregorian calendar, which is the calendar commonly used today. In doing so, you will have a chance to create a number of useful C++ functions to convert external date to/from serial Julian date, which is simply an integer that stores the number of days since November 24 4714 BC, otherwise known as the Julian Period, or January 1st 4713BC in the Julian Calendar.

You will also explore the functionality of C++ streams while prompting, validating, and processing user input of external dates. One week is the time frame to complete your work. Start early, since you may discover things looking trivial at the beginning appear to take longer time to implement than you originally expected. Solutions to the given problems will be available at the next lecture.

Part 1. Getting started

  1. Input three integer variables from the user, corresponding to the month, day, and year. Don't worry about any input validation at this stage of your project.

  2. Create a new function, such as
    int serial_julian_date( int Month, int Day, int Year );
    to calculate and return the serial Julian date nDate as follows:










  3. All divisions in these formulas are integer divisions. The solution perfectly accounts for all leap years and varying month lengths to produce a unique serial number for each and every day. The limitation is only the numerical capacity of the integer data type on your machine.

  4. You must pay some special attention to the operator precedence when translating the above equations to the C++ expressions. Use parentheses were appropriate, and if in doubt about the order of calculation.

  5. Test your program with a few dates, you should be able to obtain the following serial dates:
         7/4/1776   2369916
        12/31/2000  2451910
         2/8/2007   2454140
         2/9/2007   2454141
                            
    Suggestion: your program should implement an endless loop, asking user for input and printing the calculated serial date on the screen, then repeat.

Part 2. Back to Gregorian calendar

  1. Add three more functions to your program,
        int serial_2_month( int nDate );
        int serial_2_day( int nDate );
        int serial_2_year( int nDate );
    


    where nDate is the value calculated by the serial_julian_date( ) function from above. The new functions should return calendar month, day, and year, respectively.

  2. Performing the inverse calculations for the Gregorian calendar can be achieved using the following formulas:











    Here, a, b, c, d, e, and m are temporary variables. Month, Day, and Year are the results that you have to return from your functions.

  3. Again, operator precedence and order of evaluation of the expressions are paramount in those calculations. Since we have to deal with integer division,
        int Month = m + 3 - 12 * ( m / 10 ); // CORRECT
    and
        int Month = m + 3 - ( 12 * m ) / 10; // *** WRONG!
    give different results, of which the last version is incorrect due to misplaced parentheses.

  4. Add new function calls to your main loop and display the results on the screen. Verify that inverse calculations return expected results.

Part 3. Input validation and other improvements

Consider validating the user input as follows:

  1. Verify that the month range is correct. If not, tell the user.

  2. Verify that the day range of the month is correct. If not, tell the user. Again, you can use a switch statement based on the integer month's value to do your checks.

  3. February is a bit tougher case to verify, because in leap year it will have 29 days. Here is a simple function to determine if the year entered is a leap year or not:
    bool is_leap_year( int Year ) {
        if ( ( Year % 100 ) == 0 ) {
            return ( ( Year % 400 == 0 ) ? true : false );
        } else {
            return ( ( Year % 4 == 0 ) ? true : false );
        }
    }
    
  4. To turn in your homework, please submit your program before the next lecture.

  5. To learn more about calendars, visit http://www.tondering.dk/claus/cal/node3.html.

Part 4. Coping with user input (optional)

Take a look at another sample program, hw03unformatted.cpp (download), which you can use to support the unformatted input functionality for a variety of date formats.

  1. Often part of the program that has to deal with the raw input quickly becomes one of the most problematic components of the entire application. The code can be tough to understand and maintain. Your next effort will be to enhance the input functionality of your program to diversify acceptable date styles. To do that, you should switch to reading unformatted character sequence, similar to the input loop in lecture sample program  strstream.cpp (download source code here.)

  2. Consider
          7/4/1776
        07/04/1776
        Jul 4 1776
       JUL, 4 1776
       jul 4, 1776
                            
    Each date looks fine to the user, but presents a new challenge to the programmer. How can this mix be successfully translated to the proper int Month, int Day, and int Year ?

  3. In strstream.cpp , each individual character is obtained via call to  std::cin.get( ). The program invokes ANSI C character classification functions, such as isalpha( ) and isdigit( ) to make decisions about the input. Once the decision is made, the program generates its own stream of data accumulated by the string stream object named str_stream .

  4. Think about the possibilities of input formats: the first character could be either a digit or a letter corresponding to the name of the month; the second character could either be a digit or a letter, and so on. Commas, spaces and slashes are legal input delimiters.

  5. Keep record of the input position of each character in a separate variable.

  6. The month is the nastiest part: it is either a three-letter abbreviation, or a one- or two-digit decimal number. In either case, under no circumstance the length of the month can be longer than 3 characters. Because of this, you should store first three characters of the raw input in a separate set of variables, e.g.
        char month_abbrev_1st;
        char month_abbrev_2nd;
        char month_abbrev_3rd;
                            
    Later you can decide how to interpret month by looking at the value of month_abbrev_1st variable.

  7. Your program could have three different std::stringstream objects to accumulate data about what you think should belong to the month part, the day part, and the year, respectively.

  8. Once input is broken into pieces corresponding to month, date, and year, you can convert them to integers. Recall function string2type( ) from another lecture sample, manipulators.cpp (download). You can use string2type( ) to convert all "numeric" inputs.

  9. If first input character is not a digit, you will have to inspect month's name and choose the month manually. I've noticed that in the ASCII world the sum of second and third lowercase characters of each month name is unique. Therefore, one could try
    
        switch( tolower( second_ ) + tolower( third_ ) )
        {
            case 'a' + 'n': return 1;    // Jan
            case 'e' + 'b': return 2;    // Feb
            case 'a' + 'r': return 3;    // Mar
            case 'p' + 'r': return 4;    // Apr
            case 'a' + 'y': return 5;    // May
            case 'u' + 'n': return 6;    // Jun
            case 'u' + 'l': return 7;    // Jul
            case 'u' + 'g': return 8;    // Aug
            case 'e' + 'p': return 9;    // Sep
            case 'c' + 't': return 10;   // Oct
            case 'o' + 'v': return 11;   // Nov
            case 'e' + 'c': return 12;   // Dec
            default:        return 0;    // bad month...
        }
    
  10. You don't have to make everything perfect, but try your best and see how much progress you can make with the raw input.