|
| 1 | +package com.thealgorithms.maths; |
| 2 | + |
| 3 | +import java.time.DateTimeException; |
| 4 | +import java.time.LocalDate; |
| 5 | +import java.util.Objects; |
| 6 | + |
| 7 | +/** |
| 8 | + * A utility class for calculating the day of the week for a given date using Zeller's Congruence. |
| 9 | + * |
| 10 | + * <p>Zeller's Congruence is an algorithm devised by Christian Zeller in the 19th century to calculate |
| 11 | + * the day of the week for any Julian or Gregorian calendar date. The input date must be in the format |
| 12 | + * "MM-DD-YYYY" or "MM/DD/YYYY". |
| 13 | + * |
| 14 | + * <p>This class is final and cannot be instantiated. |
| 15 | + * |
| 16 | + * @see <a href="https://en.wikipedia.org/wiki/Zeller%27s_congruence">Wikipedia: Zeller's Congruence</a> |
| 17 | + */ |
| 18 | +public final class ZellersCongruence { |
| 19 | + |
| 20 | + private static final String[] DAYS = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; |
| 21 | + |
| 22 | + // Private constructor to prevent instantiation |
| 23 | + private ZellersCongruence() { |
| 24 | + } |
| 25 | + |
| 26 | + /** |
| 27 | + * Calculates the day of the week for a given date using Zeller's Congruence. |
| 28 | + * |
| 29 | + * <p>The algorithm works for both Julian and Gregorian calendar dates. The input date must be |
| 30 | + * in the format "MM-DD-YYYY" or "MM/DD/YYYY". |
| 31 | + * |
| 32 | + * @param input the date in the format "MM-DD-YYYY" or "MM/DD/YYYY" |
| 33 | + * @return a string indicating the day of the week for the given date |
| 34 | + * @throws IllegalArgumentException if the input format is invalid, the date is invalid, |
| 35 | + * or the year is out of range |
| 36 | + */ |
| 37 | + public static String calculateDay(String input) { |
| 38 | + if (input == null || input.length() != 10) { |
| 39 | + throw new IllegalArgumentException("Input date must be 10 characters long in the format MM-DD-YYYY or MM/DD/YYYY."); |
| 40 | + } |
| 41 | + |
| 42 | + int month = parsePart(input.substring(0, 2), 1, 12, "Month must be between 1 and 12."); |
| 43 | + char sep1 = input.charAt(2); |
| 44 | + validateSeparator(sep1); |
| 45 | + |
| 46 | + int day = parsePart(input.substring(3, 5), 1, 31, "Day must be between 1 and 31."); |
| 47 | + char sep2 = input.charAt(5); |
| 48 | + validateSeparator(sep2); |
| 49 | + |
| 50 | + int year = parsePart(input.substring(6, 10), 46, 8499, "Year must be between 46 and 8499."); |
| 51 | + |
| 52 | + try { |
| 53 | + Objects.requireNonNull(LocalDate.of(year, month, day)); |
| 54 | + } catch (DateTimeException e) { |
| 55 | + throw new IllegalArgumentException("Invalid date.", e); |
| 56 | + } |
| 57 | + if (month <= 2) { |
| 58 | + year -= 1; |
| 59 | + month += 12; |
| 60 | + } |
| 61 | + |
| 62 | + int century = year / 100; |
| 63 | + int yearOfCentury = year % 100; |
| 64 | + int t = (int) (2.6 * month - 5.39); |
| 65 | + int u = century / 4; |
| 66 | + int v = yearOfCentury / 4; |
| 67 | + int f = (int) Math.round((day + yearOfCentury + t + u + v - 2 * century) % 7.0); |
| 68 | + |
| 69 | + int correctedDay = (f + 7) % 7; |
| 70 | + |
| 71 | + return "The date " + input + " falls on a " + DAYS[correctedDay] + "."; |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Parses a part of the date string and validates its range. |
| 76 | + * |
| 77 | + * @param part the substring to parse |
| 78 | + * @param min the minimum valid value |
| 79 | + * @param max the maximum valid value |
| 80 | + * @param error the error message to throw if validation fails |
| 81 | + * @return the parsed integer value |
| 82 | + * @throws IllegalArgumentException if the part is not a valid number or is out of range |
| 83 | + */ |
| 84 | + private static int parsePart(String part, int min, int max, String error) { |
| 85 | + try { |
| 86 | + int value = Integer.parseInt(part); |
| 87 | + if (value < min || value > max) { |
| 88 | + throw new IllegalArgumentException(error); |
| 89 | + } |
| 90 | + return value; |
| 91 | + } catch (NumberFormatException e) { |
| 92 | + throw new IllegalArgumentException("Invalid numeric part: " + part, e); |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * Validates the separator character in the date string. |
| 98 | + * |
| 99 | + * @param sep the separator character |
| 100 | + * @throws IllegalArgumentException if the separator is not '-' or '/' |
| 101 | + */ |
| 102 | + private static void validateSeparator(char sep) { |
| 103 | + if (sep != '-' && sep != '/') { |
| 104 | + throw new IllegalArgumentException("Date separator must be '-' or '/'."); |
| 105 | + } |
| 106 | + } |
| 107 | +} |
0 commit comments