calculate business days including holidays

2020-02-05 09:27发布

i need to calculate the business days between two dates. ex : we have holiday(in USA) on july4th. so if my dates are date1 = 07/03/2012 date2 = 07/06/2012

no of business days b/w these dates should be 1 since july4th is holiday.

i have a below method to calclulate the business days which will only counts week ends but not holidays. is there any way to calculate holidays also....please help me on this.

  public static int getWorkingDaysBetweenTwoDates(Date startDate, Date endDate) {  
    Calendar startCal;  
    Calendar endCal;  
    startCal = Calendar.getInstance();  
    startCal.setTime(startDate);  
    endCal = Calendar.getInstance();  
    endCal.setTime(endDate);  
    int workDays = 0;  

    //Return 0 if start and end are the same  
    if (startCal.getTimeInMillis() == endCal.getTimeInMillis()) {  
        return 0;  
    }  

    if (startCal.getTimeInMillis() > endCal.getTimeInMillis()) {  
        startCal.setTime(endDate);  
        endCal.setTime(startDate);  
    }  

    do {  
        startCal.add(Calendar.DAY_OF_MONTH, 1);  
        if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY   
       && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {  
            ++workDays;  
        }  
    } while (startCal.getTimeInMillis() < endCal.getTimeInMillis());  

    return workDays;  
}

5条回答
时光不老,我们不散
2楼-- · 2020-02-05 09:52

Let's pretend you have a list containing all the holidays, as you mentioned.

ArrayList<Integer> holidays = ...

Just add a condition to your if condition in your do-while:

do {
          startCal.add(Calendar.DAY_OF_MONTH, 1);
          if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY
          && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY
          && !holidays.contains((Integer) startCal.get(Calendar.DAY_OF_YEAR))) {
              ++workDays;
          }
} while (startCal.getTimeInMillis() < endCal.getTimeInMillis());

For simplicity's sake, I've assumed holiday contains dates in the format identical to Calendar.DAY_OF_YEAR.

查看更多
\"骚年 ilove
3楼-- · 2020-02-05 09:57

The fastest way I've found is using some math (borrowing from the SO post):

public static int totalBusinessDaysBetween(LocalDate start, LocalDate end) {
    Objects.requireNonNull(start, "Start date must not be null");
    Objects.requireNonNull(end, "End date must not be null");
    long daysBetweenWithoutWeekends = calculateNumberOfDaysBetweenMinusWeekends(start, end);
    final Set<LocalDate> holidayForYearRange = getUSFederalHolidayForYearRange(start.getYear(), end.getYear());
    for (LocalDate localDate : holidayForYearRange) {
        if (localDate.isAfter(start) && localDate.isBefore(end)) {
            daysBetweenWithoutWeekends--;
        }
    }
    return (int) daysBetweenWithoutWeekends;
}

And:

private static long calculateNumberOfDaysBetweenMinusWeekends(LocalDate start, LocalDate end) {

    final DayOfWeek startW = start.getDayOfWeek();
    final DayOfWeek endW = end.getDayOfWeek();

    final long days = ChronoUnit.DAYS.between(start, end);
    final long daysWithoutWeekends = days - 2 * ((days + startW.getValue()) / 7);

    //adjust for starting and ending on a Sunday:
    return daysWithoutWeekends + (startW == DayOfWeek.SUNDAY ? 1 : 0) + (endW == DayOfWeek.SUNDAY ? 1 : 0);
}

I put something more complete together here.

查看更多
放荡不羁爱自由
4楼-- · 2020-02-05 09:59

I don't have any code samples or anything like that, but I did some searching for you and came across this Stack Overflow thread that has some links to web services that can return holiday dates for you, which may help you get to where you need to be: National holiday web service

The top answer in that thread links to this web service: http://www.holidaywebservice.com/

I'm not sure if using a web service for this type of thing is overkill or not, but surely there is a better way. I apologize, I am not the most experienced programmer so I can't help you as much as I'd like to.

查看更多
淡お忘
5楼-- · 2020-02-05 10:08

Nager.Date

You can use the JSON API of Nager.Date project. It supports USA, Canada and Europe. The data available for every year you can save the information in your own database.

Example

//https://github.com/FasterXML/jackson-databind/
ObjectMapper mapper = new ObjectMapper();
MyValue value = mapper.readValue(new URL("http://date.nager.at/api/v1/get/US/2017"), PublicHoliday[].class);

PublicHoliday.class

public class PublicHoliday
{
    public String date;
    public String localName;
    public String name;
    public String countryCode;
    public Boolean fixed;
    public Boolean countyOfficialHoliday;
    public Boolean countyAdministrationHoliday;
    public Boolean global;
    public String[] counties;
    public int launchYear;
}

Example JSON data retrieved.

[
  {
    "date": "2017-01-01",
    "localName": "New Year's Day",
    "name": "New Year's Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-01-16",
    "localName": "Martin Luther King, Jr. Day",
    "name": "Martin Luther King, Jr. Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-01-20",
    "localName": "Inauguration Day",
    "name": "Inauguration Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": false,
    "counties": [
      "US-DC",
      "US-LA",
      "US-MD",
      "US-VA"
    ],
    "launchYear": null
  },
  {
    "date": "2017-02-20",
    "localName": "Washington's Birthday",
    "name": "Presidents' Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-05-29",
    "localName": "Memorial Day",
    "name": "Memorial Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-07-04",
    "localName": "Independence Day",
    "name": "Independence Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-09-04",
    "localName": "Labor Day",
    "name": "Labor Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-09-09",
    "localName": "Columbus Day",
    "name": "Columbus Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": false,
    "counties": [
      "US-AL",
      "US-AZ",
      "US-CO",
      "US-CT",
      "US-DC",
      "US-GA",
      "US-ID",
      "US-IL",
      "US-IN",
      "US-IA",
      "US-KS",
      "US-KY",
      "US-LA",
      "US-ME",
      "US-MD",
      "US-MA",
      "US-MS",
      "US-MO",
      "US-MT",
      "US-NE",
      "US-NH",
      "US-NJ",
      "US-NM",
      "US-NY",
      "US-NC",
      "US-OH",
      "US-OK",
      "US-PA",
      "US-RI",
      "US-SC",
      "US-TN",
      "US-UT",
      "US-VA",
      "US-WV"
    ],
    "launchYear": null
  },
  {
    "date": "2017-11-10",
    "localName": "Veterans Day",
    "name": "Veterans Day",
    "countryCode": "US",
    "fixed": false,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  },
  {
    "date": "2017-12-23",
    "localName": "Thanksgiving Day",
    "name": "Thanksgiving Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": 1863
  },
  {
    "date": "2017-12-25",
    "localName": "Christmas Day",
    "name": "Christmas Day",
    "countryCode": "US",
    "fixed": true,
    "countyOfficialHoliday": true,
    "countyAdministrationHoliday": true,
    "global": true,
    "counties": null,
    "launchYear": null
  }
]
查看更多
Root(大扎)
6楼-- · 2020-02-05 10:08

Since the accepted answer still uses the obsolete Calendar class – here's my two cents using the new Java Date and Time API.

You have to get the dates of the holidays from somewhere, there is no standard Java library for it. That would be too localized anyway, since holidays heavily depends on your country and region. (except for widely known holidays, such as Christmas or Easter).
You could get them from a holiday API, for instance. In the code below I've hardcoded them as a List of LocalDates.

Java 9

LocalDate startDate = LocalDate.of(2012, 3, 7);
LocalDate endDate = LocalDate.of(2012, 6, 7);

// I've hardcoded the holidays as LocalDates and put them in a List
final List<LocalDate> holidays = Arrays.asList(
    LocalDate.of(2018, 7, 4)
);

List<LocalDate> allDates =

    // Java 9 provides a method to return a stream with dates from the
    // startdate to the given end date. Note that the end date itself is
    // NOT included.
    startDate.datesUntil(endDate)

        // Retain all business days. Use static imports from
        // java.time.DayOfWeek.*
        .filter(t -> Stream.of(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY)
            .anyMatch(t.getDayOfWeek()::equals))

        // Retain only dates not present in our holidays list
        .filter(t -> !holidays.contains(t))

         // Collect them into a List. If you only need to know the number of
         // dates, you can also use .count()
        .collect(Collectors.toList());

Java 8

The method LocalDate.datesUntil is not available in Java 8, so you have to obtain a stream of all dates between those two dates in a different manner. We must first count the total number of days in between using the ChronoUnit.DAYS.between method.

long numOfDaysBetween = ChronoUnit.DAYS.between(startDate, endDate);

Then we must generate a sequence of integers exactly as long as the number of days between the start and end date, and then create LocalDates from it, starting at the start date.

IntStream.iterate(0, i -> i + 1)
    .limit(numOfDaysBetween)
    .mapToObj(startDate::plusDays)

Now we have a Stream<LocalDate>, and you can then use the remaining part of the Java 9 code.

查看更多
登录 后发表回答