【问题标题】:Difference between two dates, including only business days (i.e. excluding weekends and holidays)两个日期之间的差异,仅包括工作日(即不包括周末和节假日)
【发布时间】:2012-08-22 00:18:17
【问题描述】:

如何获取两个java.util.Date 之间的工作日数,即不包括周末和节假日?假期,我的意思是legally recognized holidays。这必须取决于国家/地区,因为每个国家/地区的假期不同。

例如,2012-08-27 - 2012-08-24 应该返回 1 而不是 3,因为中间是周末。

我已经看过JollydayObjectLab-Kit,但我无法让它们满足我的需要。我的意思是,他们两个都有很多有趣的方法,但是找不到像getBusinessDaysCount(Date d1, Date d2)这样的东西......

【问题讨论】:

标签: java date datediff


【解决方案1】:

这似乎是日期计算的常见问题。我经常对建议使用JodaTime 感到不寒而栗,但它似乎是事实上的 Java 日期 API。如果您想冒险,那么实现一个具有基于年份的日期的类是微不足道的。所以我们知道在美国小镇4th of July遵守这些rules

“联邦法律 (5 USC 6103) 为联邦雇员规定了以下公共假期。请注意,大多数联邦雇员的工作时间为周一至周五。对于这些员工,当假期是非工作日时 - 周六或周日——节假日通常在星期一(如果节假日在星期日)或星期五(如果节假日在星期六)举行。”

因此,有了这些知识,您就可以计算一周中的哪一天放假,然后从两个范围之间的天数中减去该值。

【讨论】:

    【解决方案2】:

    这里终于有了一个基于Calendar API 的解决方案:

    /**
     * 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;
      }
    
    }
    

    调用方式:

    Holidays.FRANCE.getBusinessDayCount(d1, d2);
    

    【讨论】:

      猜你喜欢
      • 2019-10-03
      • 1970-01-01
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多