jjzjj

java - 两个日期之间的差异,仅包括工作日(即不包括周末和节假日)

coder 2024-04-01 原文

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

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

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

这样的方法

最佳答案

最后是一个基于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);

关于java - 两个日期之间的差异,仅包括工作日(即不包括周末和节假日),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12143194/

有关java - 两个日期之间的差异,仅包括工作日(即不包括周末和节假日)的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  3. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  4. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  5. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  6. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  7. ruby - 检查日期是否在过去 7 天内 - 2

    我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

  8. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  9. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  10. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

随机推荐