
// DayOfWeek.java
// https://stackoverflow.com/questions/40332445/find-day-of-week-without-using-calendar-function-in-java
// Andrew Davison, ad@coe.psu.ac.th, April 2023

/*  Read in the day (d), month (m), and year (y) and print out what
    day of the week it falls on according to the Gregorian calendar.
    For m use 1 for January, 2 for February, and so forth. 

    https://en.wikipedia.org/wiki/Gregorian_calendar

    The code here uses two different calendar functions, and the
    java.time package as a double check. 

    Usage:
     > compile DayOfWeek.java
     > run DayOfWeek <day> <month> <year>
         -- note the UK ordering!
    e.g.
     > run DayOfWeek 2 8 1953      // 2 August, 1953
     Sunday
    
     > run DayOfWeek 23 7 1962      // 23 July, 1962
     Monday
    
     > run DayOfWeek 29 2 2023
     > run DayOfWeek 31 4 2023
     error tests
*/

import java.time.*;


public class DayOfWeek 
{
  private static final String[] DAY_NAMES = {
                    "Sunday", "Monday", "Tuesday", "Wednesday", 
                    "Thursday", "Friday", "Saturday" };

  private static final int[] MONTH_LENGTHS = {
                        31, 29, 31, 30, 31, 30, 
                        31, 31, 30, 31, 30, 31 };


  public static void main(String[] args) 
  {
    if (args.length != 3) {
      System.out.println("Usage: run DayOfWeek <day> <month> <year>");
      System.out.println("   e.g.  23 7 1962");
      return;
    }

    int d = Math.abs(Nut.parseInt(args[0]));
    int m = Math.abs(Nut.parseInt(args[1]));
    int y = Math.abs(Nut.parseInt(args[2]));

    // error checking
    if ((m < 1) || (m > 12)) {
      System.out.println("Month is out of range");
      return;
    }

    if ((d < 1) || (d > MONTH_LENGTHS[m-1])) {
      System.out.println("Day is out of range");
      return;
    }

    if (y < 1582) {   // Pope Gregory introduced this system
      System.out.println("Year is out of range");
      return;
    }

    if (!isLeapYear(y) && (m == 2) && (d == 29)) {
      System.out.println("Not a leap year; day is out of range");
      return;
    }

    System.out.println("That day (Princeton) was a " + princeton(d, m, y));
    System.out.println("That day (Zeller) was a " + zeller(d, m, y));

    try {
      LocalDate date = LocalDate.of(y, m, d);
      System.out.println("That day (LocalDate) was a " + date.getDayOfWeek());
    }
    catch (DateTimeException e)
    {  System.out.println("LocalDate: invalid date");  }

  } // end of main()


  private static String princeton(int d, int m, int y)
  // https://introcs.cs.princeton.edu/java/12types/DayOfWeek.java.html
  {
    int y0 = y - (14 - m) / 12;
    int x = y0 + y0/4 - y0/100 + y0/400;
    int m0 = m + 12 * ((14 - m) / 12) - 2;
    int d0 = (d + x + (31*m0)/12) % 7;
    return DAY_NAMES[d0];
  }  // princeton()


  private static String zeller(int d, int m, int y)
  // https://stackoverflow.com/questions/40332445/find-day-of-week-without-using-calendar-function-in-java
  // https://en.wikipedia.org/wiki/Zeller%27s_congruence
  {
    if (m < 3) {
      m += 12;
      y--;
    }
    int d0 = (d + ((m+1)*13/5) + y + (y/4) + 6*(y/100) + 
             (y/400) + 6) % 7;
    return DAY_NAMES[d0];
  }  // end of zeller()


  public static boolean isLeapYear(int year) 
  {
    if (year % 4 != 0)
      return false;
    else if (year % 400 == 0)
      return true;
    else if (year % 100 == 0)
      return false;
    else
      return true;
  }  // end of isLeapYear()

}  // end of DayOfWeek class
