package org.ripple.power.timer; import java.io.Serializable; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; public abstract class SerialDate implements Serializable, MonthConstants { /** * */ private static final long serialVersionUID = 1L; public static final DateFormatSymbols DATE_FORMAT_SYMBOLS = new SimpleDateFormat() .getDateFormatSymbols(); public static final int SERIAL_LOWER_BOUND = 2; public static final int SERIAL_UPPER_BOUND = 2958465; public static final int MINIMUM_YEAR_SUPPORTED = 1900; public static final int MAXIMUM_YEAR_SUPPORTED = 9999; public static final int MONDAY = Calendar.MONDAY; public static final int TUESDAY = Calendar.TUESDAY; public static final int WEDNESDAY = Calendar.WEDNESDAY; public static final int THURSDAY = Calendar.THURSDAY; public static final int FRIDAY = Calendar.FRIDAY; public static final int SATURDAY = Calendar.SATURDAY; public static final int SUNDAY = Calendar.SUNDAY; static final int[] LAST_DAY_OF_MONTH = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; public static final int FIRST_WEEK_IN_MONTH = 1; public static final int SECOND_WEEK_IN_MONTH = 2; public static final int THIRD_WEEK_IN_MONTH = 3; public static final int FOURTH_WEEK_IN_MONTH = 4; public static final int LAST_WEEK_IN_MONTH = 0; public static final int INCLUDE_NONE = 0; public static final int INCLUDE_FIRST = 1; public static final int INCLUDE_SECOND = 2; public static final int INCLUDE_BOTH = 3; public static final int PRECEDING = -1; public static final int NEAREST = 0; public static final int FOLLOWING = 1; private String description; protected SerialDate() { } public static boolean isValidWeekdayCode(final int code) { switch (code) { case SUNDAY: case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: case SATURDAY: return true; default: return false; } } public static int stringToWeekdayCode(String s) { final String[] shortWeekdayNames = DATE_FORMAT_SYMBOLS .getShortWeekdays(); final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays(); int result = -1; s = s.trim(); for (int i = 0; i < weekDayNames.length; i++) { if (s.equals(shortWeekdayNames[i])) { result = i; break; } if (s.equals(weekDayNames[i])) { result = i; break; } } return result; } public static String weekdayCodeToString(final int weekday) { final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays(); return weekdays[weekday]; } public static String[] getMonths() { return getMonths(false); } public static String[] getMonths(final boolean shortened) { if (shortened) { return DATE_FORMAT_SYMBOLS.getShortMonths(); } else { return DATE_FORMAT_SYMBOLS.getMonths(); } } public static boolean isValidMonthCode(final int code) { switch (code) { case JANUARY: case FEBRUARY: case MARCH: case APRIL: case MAY: case JUNE: case JULY: case AUGUST: case SEPTEMBER: case OCTOBER: case NOVEMBER: case DECEMBER: return true; default: return false; } } public static int monthCodeToQuarter(final int code) { switch (code) { case JANUARY: case FEBRUARY: case MARCH: return 1; case APRIL: case MAY: case JUNE: return 2; case JULY: case AUGUST: case SEPTEMBER: return 3; case OCTOBER: case NOVEMBER: case DECEMBER: return 4; default: throw new IllegalArgumentException( "SerialDate.monthCodeToQuarter: invalid month code."); } } public static String monthCodeToString(final int month) { return monthCodeToString(month, false); } public static String monthCodeToString(final int month, final boolean shortened) { if (!isValidMonthCode(month)) { throw new IllegalArgumentException( "SerialDate.monthCodeToString: month outside valid range."); } final String[] months; if (shortened) { months = DATE_FORMAT_SYMBOLS.getShortMonths(); } else { months = DATE_FORMAT_SYMBOLS.getMonths(); } return months[month - 1]; } public static int stringToMonthCode(String s) { final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths(); final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths(); int result = -1; s = s.trim(); try { result = Integer.parseInt(s); } catch (NumberFormatException e) { } if ((result < 1) || (result > 12)) { for (int i = 0; i < monthNames.length; i++) { if (s.equals(shortMonthNames[i])) { result = i + 1; break; } if (s.equals(monthNames[i])) { result = i + 1; break; } } } return result; } public static boolean isValidWeekInMonthCode(final int code) { switch (code) { case FIRST_WEEK_IN_MONTH: case SECOND_WEEK_IN_MONTH: case THIRD_WEEK_IN_MONTH: case FOURTH_WEEK_IN_MONTH: case LAST_WEEK_IN_MONTH: return true; default: return false; } } public static boolean isLeapYear(final int yyyy) { if ((yyyy % 4) != 0) { return false; } else if ((yyyy % 400) == 0) { return true; } else if ((yyyy % 100) == 0) { return false; } else { return true; } } public static int leapYearCount(final int yyyy) { final int leap4 = (yyyy - 1896) / 4; final int leap100 = (yyyy - 1800) / 100; final int leap400 = (yyyy - 1600) / 400; return leap4 - leap100 + leap400; } public static int lastDayOfMonth(final int month, final int yyyy) { final int result = LAST_DAY_OF_MONTH[month]; if (month != FEBRUARY) { return result; } else if (isLeapYear(yyyy)) { return result + 1; } else { return result; } } public static SerialDate addDays(final int days, final SerialDate base) { final int serialDayNumber = base.toSerial() + days; return SerialDate.createInstance(serialDayNumber); } public static SerialDate addMonths(final int months, final SerialDate base) { final int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12; final int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1; final int dd = Math.min(base.getDayOfMonth(), SerialDate.lastDayOfMonth(mm, yy)); return SerialDate.createInstance(dd, mm, yy); } public static SerialDate addYears(final int years, final SerialDate base) { final int baseY = base.getYYYY(); final int baseM = base.getMonth(); final int baseD = base.getDayOfMonth(); final int targetY = baseY + years; final int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY)); return SerialDate.createInstance(targetD, baseM, targetY); } public static SerialDate getPreviousDayOfWeek(final int targetWeekday, final SerialDate base) { if (!SerialDate.isValidWeekdayCode(targetWeekday)) { throw new IllegalArgumentException("Invalid day-of-the-week code."); } final int adjust; final int baseDOW = base.getDayOfWeek(); if (baseDOW > targetWeekday) { adjust = Math.min(0, targetWeekday - baseDOW); } else { adjust = -7 + Math.max(0, targetWeekday - baseDOW); } return SerialDate.addDays(adjust, base); } public static SerialDate getFollowingDayOfWeek(final int targetWeekday, final SerialDate base) { if (!SerialDate.isValidWeekdayCode(targetWeekday)) { throw new IllegalArgumentException("Invalid day-of-the-week code."); } final int adjust; final int baseDOW = base.getDayOfWeek(); if (baseDOW > targetWeekday) { adjust = 7 + Math.min(0, targetWeekday - baseDOW); } else { adjust = Math.max(0, targetWeekday - baseDOW); } return SerialDate.addDays(adjust, base); } public static SerialDate getNearestDayOfWeek(final int targetDOW, final SerialDate base) { if (!SerialDate.isValidWeekdayCode(targetDOW)) { throw new IllegalArgumentException("Invalid day-of-the-week code."); } final int baseDOW = base.getDayOfWeek(); int adjust = -Math.abs(targetDOW - baseDOW); if (adjust >= 4) { adjust = 7 - adjust; } if (adjust <= -4) { adjust = 7 + adjust; } return SerialDate.addDays(adjust, base); } public SerialDate getEndOfCurrentMonth(final SerialDate base) { final int last = SerialDate.lastDayOfMonth(base.getMonth(), base.getYYYY()); return SerialDate.createInstance(last, base.getMonth(), base.getYYYY()); } public static String weekInMonthToString(final int count) { switch (count) { case SerialDate.FIRST_WEEK_IN_MONTH: return "First"; case SerialDate.SECOND_WEEK_IN_MONTH: return "Second"; case SerialDate.THIRD_WEEK_IN_MONTH: return "Third"; case SerialDate.FOURTH_WEEK_IN_MONTH: return "Fourth"; case SerialDate.LAST_WEEK_IN_MONTH: return "Last"; default: return "SerialDate.weekInMonthToString(): invalid code."; } } public static String relativeToString(final int relative) { switch (relative) { case SerialDate.PRECEDING: return "Preceding"; case SerialDate.NEAREST: return "Nearest"; case SerialDate.FOLLOWING: return "Following"; default: return "ERROR : Relative To String"; } } public static SerialDate createInstance(final int day, final int month, final int yyyy) { return new SpreadsheetDate(day, month, yyyy); } public static SerialDate createInstance(final int serial) { return new SpreadsheetDate(serial); } public static SerialDate createInstance(final java.util.Date date) { final GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(date); return new SpreadsheetDate(calendar.get(Calendar.DATE), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.YEAR)); } public abstract int toSerial(); public abstract java.util.Date toDate(); public String getDescription() { return this.description; } public void setDescription(final String description) { this.description = description; } public String toString() { return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth()) + "-" + getYYYY(); } public abstract int getYYYY(); public abstract int getMonth(); public abstract int getDayOfMonth(); public abstract int getDayOfWeek(); public abstract int compare(SerialDate other); public abstract boolean isOn(SerialDate other); public abstract boolean isBefore(SerialDate other); public abstract boolean isOnOrBefore(SerialDate other); public abstract boolean isAfter(SerialDate other); public abstract boolean isOnOrAfter(SerialDate other); public abstract boolean isInRange(SerialDate d1, SerialDate d2); public abstract boolean isInRange(SerialDate d1, SerialDate d2, int include); public SerialDate getPreviousDayOfWeek(final int targetDOW) { return getPreviousDayOfWeek(targetDOW, this); } public SerialDate getFollowingDayOfWeek(final int targetDOW) { return getFollowingDayOfWeek(targetDOW, this); } public SerialDate getNearestDayOfWeek(final int targetDOW) { return getNearestDayOfWeek(targetDOW, this); } }