/** A Day object represents a day in the Julian/Gregorian calendar. */ import java.util.GregorianCalendar; public class Day { private int year; private int month; private int date; private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; private static final int GREGORIAN_START_YEAR = 1582; private static final int GREGORIAN_START_MONTH = 10; private static final int GREGORIAN_START_DAY = 15; private static final int JULIAN_END_DAY = 4; private static final int JANUARY = 1; private static final int FEBRUARY = 2; private static final int DECEMBER = 12; /** Constructs a day object representing today's date. */ public Day() { GregorianCalendar today = new GregorianCalendar(); year = today.get(GregorianCalendar.YEAR); month = today.get(GregorianCalendar.MONTH) + 1; date = today.get(GregorianCalendar.DAY_OF_MONTH); } /** Constructs a day with a given year, month, and day of the Julian/Gregorian calendar. The Julian calendar is used for all days before October 15, 1582 @param aYear a year != 0 @param aMonth a month between 1 and 12 @param aDate a date between 1 and 31 */ public Day(int aYear, int aMonth, int aDate) { year = aYear; month = aMonth; date = aDate; } /** Returns the year of this day. @return the year */ public int getYear() { return year; } /** Returns the month of this day. @return the month */ public int getMonth() { return month; } /** Returns the day of the month of this day. @return the day of the month */ public int getDate() { return date; } /** Returns a day that is a certain number of days away from this day. @param n the number of days, can be negative @return a day that is n days away from this one */ public Day addDays(int n) { Day result = this; while (n > 0) { result = result.nextDay(); n--; } while (n < 0) { result = result.previousDay(); n++; } return result; } /** Returns the number of days between this day and another day. @param other the other day @return the number of days that this day is away from the other (>0 if this day comes later than other) */ public int daysFrom(Day other) { int n = 0; Day d = this; while (d.compareTo(other) > 0) { d = d.previousDay(); n++; } while (d.compareTo(other) < 0) { d = d.nextDay(); n--; } return n; } public String toString() { return String.format("%04d-%02d-%02d", year, month, date); } /** Compares this day with another day. @param other the other day @return a positive number if this day comes after the other day, a negative number if this day comes before the other day, and zero if the days are the same */ private int compareTo(Day other) { if (year > other.year) return 1; if (year < other.year) return -1; if (month > other.month) return 1; if (month < other.month) return -1; return date - other.date; } /** Computes the next day. @return the day following this day */ private Day nextDay() { int y = year; int m = month; int d = date; if (y == GREGORIAN_START_YEAR && m == GREGORIAN_START_MONTH && d == JULIAN_END_DAY) d = GREGORIAN_START_DAY; else if (d < daysPerMonth(y, m)) d++; else { d = 1; m++; if (m > DECEMBER) { m = JANUARY; y++; if (y == 0) y++; } } return new Day(y, m, d); } /** Computes the previous day. @return the day preceding this day */ private Day previousDay() { int y = year; int m = month; int d = date; if (y == GREGORIAN_START_YEAR && m == GREGORIAN_START_MONTH && d == GREGORIAN_START_DAY) d = JULIAN_END_DAY; else if (d > 1) d--; else { m--; if (m < JANUARY) { m = DECEMBER; y--; if (y == 0) y--; } d = daysPerMonth(y, m); } return new Day(y, m, d); } /** Gets the days in a given month. @param y the year @param m the month @return the last day in the given month */ private static int daysPerMonth(int y, int m) { int days = DAYS_PER_MONTH[m - 1]; if (m == FEBRUARY && isLeapYear(y)) days++; return days; } /** Tests if a year is a leap year. @param y the year @return true if y is a leap year */ private static boolean isLeapYear(int y) { if (y % 4 != 0) return false; if (y < GREGORIAN_START_YEAR) return true; return (y % 100 != 0) || (y % 400 == 0); } }