The difference between the two dates, including only business days (i.e. excluding weekends and holidays)

How can I get the number of working days between two java.util.Date , i.e. excluding weekends and holidays? By holidays, I mean legally recognized holidays . This should be country dependent, due to holidays that differ from country to country.

For example, 2012-08-27 - 2012-08-24 should return 1 instead of 3 because of the weekend between them.

I already looked at Jollyday and ObjectLab-Kit , but I can not get them to satisfy my need. I mean, both have a lot of interesting methods, but they cannot find something like getBusinessDaysCount(Date d1, Date d2) ...

+6
source share
2 answers

Here is finally the Calendar API based solution:

 /** * Handles holidays by country. */ public enum Holidays { /** * See <a href="http://www.wikiwand.com/en/Public_holidays_in_France">http://www.wikiwand.com/en/Public_holidays_in_France</a>. */ FRANCE { @Override protected void addFixedHolidays(Set<Holiday> holidays) { holidays.add(new Holiday(Calendar.JANUARY, 1)); holidays.add(new Holiday(Calendar.MAY, 1)); holidays.add(new Holiday(Calendar.MAY, 8)); holidays.add(new Holiday(Calendar.JULY, 14)); holidays.add(new Holiday(Calendar.AUGUST, 15)); holidays.add(new Holiday(Calendar.NOVEMBER, 1)); holidays.add(new Holiday(Calendar.NOVEMBER, 11)); holidays.add(new Holiday(Calendar.DECEMBER, 25)); } @Override protected void addVariableHolidays(int year, Set<Holiday> holidays) { Date easterSunday = getEasterSunday(year); holidays.add(new Holiday(getEasterMonday(easterSunday))); holidays.add(new Holiday(getAscensionThursday(easterSunday))); holidays.add(new Holiday(getPentecostMonday(easterSunday))); } }, /** * See <a href="http://www.wikiwand.com/en/Public_holidays_in_the_United_Kingdom">http://www.wikiwand.com/en/Public_holidays_in_the_United_Kingdom</a>. */ ENGLAND { @Override protected void addFixedHolidays(Set<Holiday> holidays) { holidays.add(new Holiday(Calendar.JANUARY, 1)); holidays.add(new Holiday(Calendar.DECEMBER, 25)); holidays.add(new Holiday(Calendar.DECEMBER, 26)); } @Override protected void addVariableHolidays(int year, Set<Holiday> holidays) { Date easterSunday = getEasterSunday(year); holidays.add(new Holiday(getGoodFriday(easterSunday))); holidays.add(new Holiday(getEasterMonday(easterSunday))); holidays.add(new Holiday(get(WeekdayIndex.FIRST, Calendar.MONDAY, Calendar.MAY, year))); holidays.add(new Holiday(get(WeekdayIndex.LAST, Calendar.MONDAY, Calendar.MAY, year))); holidays.add(new Holiday(get(WeekdayIndex.LAST, Calendar.MONDAY, Calendar.AUGUST, year))); Holiday christmasDay = new Holiday(Calendar.DECEMBER, 25); if (christmasDay.isWeekend(year)) { holidays.add(new Holiday(Calendar.DECEMBER, 27)); } Holiday boxingDay = new Holiday(Calendar.DECEMBER, 26); if (boxingDay.isWeekend(year)) { holidays.add(new Holiday(Calendar.DECEMBER, 28)); } } }; public class HolidayException extends Exception { private static final long serialVersionUID = 1L; private HolidayException(String message) { super(message); } } /** * A holiday is defined by a {@link Calendar#MONTH} and a {@link Calendar#DAY_OF_MONTH}. */ private class Holiday { private final int day; private final int month; public Holiday(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); month = calendar.get(Calendar.MONTH); day = calendar.get(Calendar.DAY_OF_MONTH); } public Holiday(int month, int day) { this.month = month; this.day = day; } public Date toDate(int year) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month, day); return calendar.getTime(); } public boolean isWeekend(int year) { Calendar calendar = Calendar.getInstance(); calendar.setTime(toDate(year)); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); return dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (!(obj instanceof Holiday)) { return false; } else { Holiday holiday = (Holiday) obj; return holiday.month == month && holiday.day == day; } } @Override public int hashCode() { return Arrays.hashCode(new int[] { month, day }); } } /** * Use with {@link Holidays#get(WeekdayIndex, int, int, int)}.<br /> * <br /> * Example: <code>Holidays.get(WeekdayIndex.FIRST, Calendar.MONDAY, Calendar.MAY, 2000)</code>. */ public enum WeekdayIndex { FIRST(1), SECOND(2), THIRD(3), FOURTH(4), LAST(null); private final Integer index; private WeekdayIndex(Integer index) { this.index = index; } private boolean is(int count) { return index != null && index == count; } } private final Set<Holiday> fixedHolidays = new HashSet<Holiday>(); private final Map<Integer, Set<Holiday>> variableHolidays = new HashMap<Integer, Set<Holiday>>(); private Holidays() { addFixedHolidays(fixedHolidays); } protected abstract void addFixedHolidays(Set<Holiday> holidays); protected abstract void addVariableHolidays(int year, Set<Holiday> holidays); /** * Returns the number of business days between two dates. * * @param d1 * The first date. * @param d2 * The second date. * @return The number of business days between the two provided dates. * @throws HolidayException * If <code>d1</code> or <code>d2</code> is not a business day. */ public int getBusinessDayCount(Date d1, Date d2) throws HolidayException { Calendar calendar = Calendar.getInstance(); DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); try { d1 = formatter.parse(formatter.format(d1)); d2 = formatter.parse(formatter.format(d2)); } catch (ParseException ignore) { // cannot happen } if (!isBusinessDay(d1) || !isBusinessDay(d2)) { throw new HolidayException("Input dates must be business days"); } int businessDayCount = 0; Date min = d1.before(d2) ? d1 : d2; Date max = min.equals(d2) ? d1 : d2; calendar.setTime(min); while (calendar.getTime().before(max)) { calendar.add(Calendar.DAY_OF_MONTH, 1); if (isBusinessDay(calendar.getTime())) { businessDayCount++; } } return businessDayCount; } /** * Returns whether a date is a business day. * * @param date * The date. * @return <code>true</code> if the <code>date</code> is a business day, <code>false</code> otherwise. */ public boolean isBusinessDay(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); if (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY) { return false; } else if (isFixedHoliday(date)) { return false; } else if (isVariableHoliday(date)) { return false; } return true; } private boolean isFixedHoliday(Date date) { return fixedHolidays.contains(new Holiday(date)); } private boolean isVariableHoliday(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int year = calendar.get(Calendar.YEAR); Set<Holiday> yearHolidays; if (!variableHolidays.containsKey(year)) { // variable holidays have not been calculated for this year yet yearHolidays = new HashSet<Holiday>(); addVariableHolidays(year, yearHolidays); variableHolidays.put(year, yearHolidays); } else { yearHolidays = variableHolidays.get(year); } return yearHolidays.contains(new Holiday(date)); } public static Date getEasterSunday(int year) { // credits: https://www.wikiwand.com/en/Computus#/Anonymous_Gregorian_algorithm Calendar calendar = Calendar.getInstance(); int initialYear = year; if (year < 1900) { year += 1900; } int a = year % 19; int b = year / 100; int c = year % 100; int d = b / 4; int e = b % 4; int f = (b + 8) / 25; int g = (b - f + 1) / 3; int h = (19 * a + b - d - g + 15) % 30; int i = c / 4; int j = c % 4; int k = (32 + 2 * e + 2 * i - h - j) % 7; int l = (a + 11 * h + 22 * k) / 451; int m = (h + k - 7 * l + 114) % 31; int month = (h + k - 7 * l + 114) / 31 - 1; int day = m + 1; calendar.set(initialYear, month, day); return calendar.getTime(); } public static Date getGoodFriday(Date easterSunday) { Calendar calendar = Calendar.getInstance(); calendar.setTime(easterSunday); calendar.add(Calendar.DAY_OF_MONTH, -2); return calendar.getTime(); } public static Date getEasterMonday(Date easterSunday) { Calendar calendar = Calendar.getInstance(); calendar.setTime(easterSunday); calendar.add(Calendar.DAY_OF_MONTH, 1); return calendar.getTime(); } public static Date getAscensionThursday(Date easterSunday) { Calendar calendar = Calendar.getInstance(); calendar.setTime(easterSunday); calendar.add(Calendar.DAY_OF_MONTH, 39); return calendar.getTime(); } public static Date getPentecostMonday(Date easterSunday) { Calendar calendar = Calendar.getInstance(); calendar.setTime(easterSunday); calendar.add(Calendar.DAY_OF_MONTH, 50); return calendar.getTime(); } public static Date get(WeekdayIndex weekdayIndex, int dayOfWeek, int month, int year) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month, 1); int count = 0; Date last = null; do { if (calendar.get(Calendar.DAY_OF_WEEK) == dayOfWeek) { count++; last = calendar.getTime(); if (weekdayIndex.is(count)) { return last; } } calendar.add(Calendar.DAY_OF_MONTH, 1); } while (calendar.get(Calendar.MONTH) == month); if (weekdayIndex.equals(WeekdayIndex.LAST)) { return last; } return null; } } 

Call using:

 Holidays.FRANCE.getBusinessDayCount(d1, d2); 
+3
source

This is apparently a common issue with Date calculations. I often shudder when using JodaTime , but it seems to be the actual Date API for Java. If you want to be adventurous, it is trivial to implement a class that has dates based on a year. Thus, we know that in the city of America that 4th of July adheres to these rules

"Federal law (5 USC 6103) establishes the following holidays for federal employees. Please note that most federal employees work on a schedule from Monday to Friday. For these employees, when the holiday falls on a non-working day - Saturday or Sunday - the holiday is usually observed on Monday (if the holiday falls on Sunday) or Friday (if the holiday falls on Saturday). "

So, given this knowledge, you can calculate which day of the week a holiday will occur and subtract this value from the number of days between the two ranges.

+1
source

Source: https://habr.com/ru/post/923866/


All Articles