// This file contains a program that calculates the day of the week on
// which a particular date fell or will fall. The program consists of a
// collection of static methods -- objects provide relatively little
// benefit in a program this narrowly focused on doing one thing. The
// main program reads a year, month, and day from the user, then calls
// a series of private methods that do parts of the actual day-of-week
// calculation.

// History:
//
//   December 2003 -- Created by Doug Baldwin, as a translation into
//     Java of a Pascal program.




import java.io.*;




class WeekDay {




    private static final int INVALID = -1;		// Value used to indicate an invalid year/month/day


    

    // The main program loops, reading a date from the user, printing the
    // corresponding day of the week, and asking the user if they want to keep
    // going until the user indicates that they want to stop.
    
    public static void main( String args[] ) {

        BufferedReader keyboard = new BufferedReader( new InputStreamReader( System.in ) );

        try {
            
            String more = "y";

            while ( more.equalsIgnoreCase("y") ) {


                // Read a year, month, and day from the user. Just read via standard input,
                // for (relative) simplicity:

                int year = readNumber( keyboard, "Enter a year (e.g., 2003)", 1900, INVALID );
                int month = readNumber( keyboard, "Enter a month (e.g., 12)", 1, 12 );
                int day = readNumber( keyboard, "Enter a day of the month (e.g., 3)", 1, monthLength(month,year) );


                // Display the corresponding weekday:

                System.out.println( month + "/" + day + "/" + year + " is a " + dayOfWeek( day, month, year ) );


                // See if the user wants to keep going:

                System.out.print( "Another date (y/n)? " );
                more = keyboard.readLine();
            }
        }
        catch ( IOException error ) { }
    }




    // dayOfWeek, a method that determines what day of the week a given day in a
    // given month of a given year is or was. This works by computing the number of
    // days between January 1, 1900 and the given date, not counting either Jan. 1,
    // 1900 or the given date -- e.g., Jan. 1, 1900 would yield a value of 0. The
    // remainder when this number is divided by 7 is how many days in excess of a
    // whole number of weeks the given date is after Jan. 1, 1900. Using this number
    // to look up a weekday name, in a list that starts with whatever day of the
    // week Jan. 1, 1900 was, yields the desired result.

    private static String dayOfWeek( int day, int month, int year ) {

        // Names of weekdays, starting with Jan. 1, 1900 (a Monday):        
        final String days[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

        return days[ daysSince1900( day, month, year ) % 7 ];
    }




    // daysSince1900, a method that computes the number of days between Jan. 1, 1900, and
    // a given date, not counting either Jan. 1, 1900 or the given date (e.g., this will
    // return 0 if the given date is Jan. 1, 1900). This function calculates the number of
    // days since the base date in three parts:
    //   First, it calculates the days in the completed years between 1900 and "year". Each year
    //     contributes 365 days, every fourth year adds one day for leap year (conveniently, the
    //     first leap year was 1904, 4 years after 1900), every hundredth year removes a day since
    //     years divisible by 100 generally aren't leap years, and the year 2000 and every 400th
    //     year thereafter adds a day back because years divisible by 400 *are* leap years.
    //   Second, this function adds all the days in the months up to but not including "month" to the
    //     result of the first step.
    //   Finally, this function adds "day"-1 to the total from the first two steps. The "-1" reflects
    //     the fact that days since the base date start at 0 (i.e., January 1, 1900 is 0 days
    //     since the base date), but day-of-month values start at 1.

    private static int daysSince1900( int day, int month, int year ) {
        
        int fullYears = year - 1900;
        int leapYears = ( year - 1901 ) / 4;		// To calculate *full* leap years, centuries,
        int centuries = ( year - 1901 ) / 100;		// etc. since 1900, I count from 1901 (or 1601,
        int fourCenturies = ( year - 1601 ) / 400;	// for quadruple centuries) to avoid counting the
                                                        // current year, which isn't full yet.
        return   fullYears * 365 + leapYears - centuries + fourCenturies
               + daysIntoYear( month, year )
               + day - 1;
    }




    // daysIntoYear, a method that returns the number of days between the beginning of a
    // year and the first day of a given month (not counting the first of the month).
    // This method just adds up the lengths of all the months from January to, but
    // not including, "month". Note that the year is used in "monthLength" to determine
    // whether February has 28 or 29 days.

    private static int daysIntoYear( int month, int year ) {

        int days = 0;

        for ( int m = 1; m < month; m++ ) {
            days = days + monthLength( m, year );
        }

        return days;
    }




    // monthLength, a method that returns the number of days in a specified
    // month of a specified year. This method looks up the month in an array
    // of lengths, except that if the month is February and the year is a
    // leap year, this function returns 29.

    private static int monthLength( int month, int year ) {

        final int lengths[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

        if (  month != 3  || ! isLeapYear(year)  ) {
            return lengths[ month-1 ];
        }
        else {
            return 29;
        }
    }




    // isLeapYear, a method that returns true if a year is a leap year, and
    // false if it isn't. A year is a leap year if it is divisible by 4, but
    // not by 100, or if it *is* divisible by 400.

    private static boolean isLeapYear( int year ) {
        return ( year % 4 == 0  &&  year % 100 != 0 )  ||  ( year % 400 == 0 );
    }




    // readNumber, a method that reads a natural number from the user and
    // returns it. Parameters to this method are the reader to read from,
    // a prompt to display, the lowest acceptable number, and the highest
    // acceptable number. Either the minimum or maximum numbers may be the
    // constant INVALID, in which case the corresponding bound isn't checked.
    // This method will keep prompting and reading until it gets a number in
    // the right range. If this function is completely unable to read from the
    // keyboard, it returns the minimum acceptable value as a default.

    private static int readNumber( BufferedReader keyboard, String prompt, int min, int max ) {

        int result = INVALID;

        while ( result == INVALID ) {

            System.out.print( prompt + ": " );

            try {
                String input = keyboard.readLine();
                result = Integer.parseInt( input );
            }
            catch ( NumberFormatException error ) {
                System.out.println( "Input must be a number. Try again..." );
            }
            catch ( IOException error ) {
                System.out.println( "Error reading keyboard. Using default value." );
                result = min;
            }

            if (  min != INVALID  &&  result != INVALID  &&  result < min  ) {
                System.out.println( "Input must be at least " + min + ". Try again..." );
                result = INVALID;
            }
            if (  max != INVALID  &&  result != INVALID  &&  result > max  ) {
                System.out.println( "Input must be no more than " + max + ". Try again..." );
                result = INVALID;
            }
        }

        return result;
    }
    
}