// 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; } }