Counting number of days of a particular month when given a duration - java

A duration is given.
Ex: Jan 15-March 15
I want to count the number of days which belongs to each month, in that given duration.
In this example,
number of days of January in that duration; 15
number of days of February in that duration; 28
number of days of March in that duration; 15
I'm looking for a solution other that traversing through each date of the duration and checking if Date.getMonth() = "Month I want to check against"
Is there an easier way of doing this using methods in Java Date or Java SQL Date or using any other Date type?

Map < YearMonth , Long > with lambda syntax
Here is a solution using a bit of terse code using streams and lambdas. While this solution does traverse each date of the time range, the simplicity and clarity of the code may outweigh that inefficiency.
Use LocalDate for the starting and stopping date. Use YearMonth to track each month.
LocalDate start = LocalDate.of( 2019 , 1 , 15 );
LocalDate stop = LocalDate.of( 2019 , 3 , 16 );
Make a Map to keep a number of days for each month.
Map < YearMonth, Long > map =
start
.datesUntil( stop )
.collect(
Collectors.groupingBy(
( LocalDate localDate ) -> YearMonth.from( localDate ) ,
TreeMap::new ,
Collectors.counting()
)
);
Dump to console.
{2019-01=17, 2019-02=28, 2019-03=15}
System.out.println( map );
Given a starting date, LocalDate::datesUntil provides a Stream of LocalDate objects, incremented by days.
Then just do a grouping into a SortedMap (a TreeMap) to keep months in chronological order, classified by the YearMonth and counting the days for that month in the range.
If you want the total days you can just do
long totalDays = d.datesUntil(LocalDate.of(2019, 3, 16)).count();

This is just a simple example I threw together with some basic research.
LocalDate from = LocalDate.of(2019, Month.JANUARY, 15);
LocalDate to = LocalDate.of(2019, Month.MARCH, 15);
DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MMM");
LocalDate date = from;
while (date.isBefore(to)) {
LocalDate endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
if (endOfMonth.isAfter(to)) {
endOfMonth = to;
}
// Inclusive to exclusive comparison
long days = ChronoUnit.DAYS.between(date, endOfMonth.plusDays(1));
System.out.println(days + " days in " + date.format(monthFormatter));
date = date.plusMonths(1).withDayOfMonth(1);
}
This will output
17 days in Jan.
28 days in Feb.
15 days in Mar.
There are probably better ways to achieve the same result, but as I said, I just threw it together with a little bit of Googling and trial and error.
As has already been stated, you should avoid using the older, out-of-date and effectively deprecated Date, Calendar and associated classes.

Try this. May be something like this you want. So it set a startdate and enddate, then loop for each moth till the end date and calculate the day count. I have not tested it thoroughly, but should be close to your concept.
public static void main(String[] args) throws ParseException {
String startDateS = "01/15/2019";
String endDateS = "03/15/2019";
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
Date startDate = dateFormat.parse(startDateS);
Date endDate = dateFormat.parse(endDateS);
while (endDate.compareTo(startDate) > 0) {
Calendar c = Calendar.getInstance();
c.setTime(startDate);
c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
Date endOfMonth = c.getTime();
if( endDate.compareTo(endOfMonth) > 0 )
System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endOfMonth));
else
System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endDate));
c.add(Calendar.DAY_OF_MONTH, 1);
startDate = c.getTime();
}
}
static String getMonthForInt(int num) {
String month = "wrong";
DateFormatSymbols dfs = new DateFormatSymbols();
String[] months = dfs.getMonths();
if (num >= 0 && num <= 11) {
month = months[num];
}
return month;
}
public static int getDifferenceDays(Date d1, Date d2) {
int daysdiff = 0;
long diff = d2.getTime() - d1.getTime();
long diffDays = diff / (24 * 60 * 60 * 1000) + 1;
daysdiff = (int) diffDays;
return daysdiff;
}
You can do the same using Java.time in Java 8.
public static void main(String[] args) throws ParseException {
String startDateS = "01/15/2019";
String endDateS = "03/15/2019";
DateTimeFormatter format1 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
LocalDate startDate = LocalDate.parse(startDateS, format1);
LocalDate endDate = LocalDate.parse(endDateS, format1);
while (endDate.compareTo(startDate) > 0) {
LocalDate endOfMonth = startDate.minusDays(startDate.getDayOfMonth()).plusMonths(1);
if( endDate.compareTo(endOfMonth) > 0 )
System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endOfMonth));
else
System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endDate));
startDate = endOfMonth.plusDays(1);
}
}
static String getMonthForInt(LocalDate startDate) {
return startDate.getMonth().getDisplayName(
TextStyle.FULL ,
Locale.US
);
}
public static long getDifferenceDays(LocalDate d1, LocalDate d2) {
// return Duration.between(d2.atStartOfDay(), d1.atStartOfDay()).toDays();
return ChronoUnit.DAYS.between(d1, d2) + 1;
}

Related

How to set a recurring holiday on the same date every year (disregarding the year component) with java using java.util.Calendar?

I am making a calendar that allows you to add a specific holiday which recurs each year automatically.
My WorkdayCalendar.class needs 2 methods:
-setHoliday(Calendar date) which sets a holiday only within that year
-setRecurringHoliday(Calendar date) which should (preferably) use setHoliday() and set it recurring each year.
How do I implement the logic that checks if it is a new year? I am adding holidays to a HashSet named holidaysList. I need a method that checks if it is a new year and then adds a specified holiday.
The setHoliday works fine and has been tested wih unitTests.
public void setHoliday(Calendar date) {
this.date = date.getTime();
if (!isHoliday(date)) {
holidaysList.add(this.date);
}
}
public void setRecurringHoliday(Calendar date) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm");
GregorianCalendar todaysDate = new GregorianCalendar();
System.out.println(
sdf.format("Todays date: " + todaysDate.getTime()) + "\n");
int thisYear = todaysDate.get(Calendar.YEAR);
int chosenYear = date.get(Calendar.YEAR);
System.out.println("Chosen year: " + chosenYear + "\nThis year: " + thisYear);
date.add(Calendar.YEAR, 1);
int nextYear = date.get(Calendar.YEAR);
System.out.println("Next year: " + nextYear);
/*What to do here???*/
if (thisYear == nextYear){
setHoliday(date);
System.out.println("recurring holiday added");
}
}
private boolean isHoliday(Calendar date) {
this.date = date.getTime();
return isWeekend(date) || holidaysList.contains(this.date);
}
private boolean isWeekend(Calendar date) {
int chosenDay = date.get(Calendar.DAY_OF_WEEK);
return chosenDay == Calendar.SATURDAY || chosenDay == Calendar.SUNDAY;
}
You are using terrible date-time classes that were years ago supplanted by the java.time classes defined in JSR 310.
MonthDay
For a month and day-of-month without year, use MonthDay.
MonthDay xmas = MonthDay.of( Month.DECEMBER , 25 ) ;
Your set of holidays should be a set MonthDay objects, from what I can discern of your Question. I find your overall Question confusing as its logic falls short of what you need for the usual workplace holiday tracking.
Set< MonthDay > holidays = new TreeSet<>() ;
holidays.add( xmas ) ;
For a date, use LocalDate.
Apply a year to get a date.
LocalDate xmas2020 = xmas.atYear( 2020 ) ;
To get current year, use Year, and specify a time zone. For any given moment, the date, and therefore possibly the year, varies around the globe by zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Year currentYear = Year.now( z ) ;
LocalDate xmasThisYear = currentYear.atMonthDay( xmas ) ;
Add to Year for next year.
Year nextYear = currentYear.plusYears( 1 ) ;
LocalDate xmasNextYear = nextYear.atMonthDay( xmas ) ;
Ask if a date is this year or next.
boolean isThisYear = Year.from( localDate ).equals( currentYear ) ;
boolean isNextYear = Year.from( localDate ).equals( nextYear ) ;
boolean isFutureYear = Year.from( localDate ).isAfter( currentYear ) ;
For your check of the weekend, define an EnumSet of the desired day of week values as defined in the DayOfWeek enum.
Set< DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
boolean isWeekend = weekend.contains( localDate.getDayOfWeek() ) ;

Determine how many time a Day occurs between two days in Java

I need to calculate how many times a Day occurs between two dates, which can be weekly or monthly.
Eg Monthly:
StartDate : 2019/01/29
EndDate: 2019/03/28
So considering the above example, the output should be 1, as this is not a leap year, Feb(28) days, also it ends on March 28.
int recordCountMonths = (int) ChronoUnit.MONTHS.between(scheduleStartDate, scheduleEndDate);
int recordCountWeeks = (int) ChronoUnit.WEEKS.between(scheduleStartDate, scheduleEndDate);
Exmaple 1
The Months implementation gives me 2 (it should return 3, considering leap year) if my date is between:
LocalDate startDate = LocalDate.of(2020, Month.JANUARY, 29);
LocalDate endDate = LocalDate.of(2020, Month.APRIL, 28);
Example2:
The Months implementation gives me 3 (it should return 2, considering 31 occurs in Jan and March only) if my date is between:
LocalDate startDate = LocalDate.of(2019, Month.JANUARY, 31);
LocalDate endDate = LocalDate.of(2019, Month.MAY, 28);
Also, the above count can be asked to calculate weekly too. In that case the count will be more. However, lets not focus on weekly for now.
I can't think of a simple way to do this.
I have tried different ways, but that gives me inaccurate results.
As promised, here's a method to solve your problem. It could be better but this should work well enough. You can make similar counting methods for other specific cases, like whatever you wanted to do with weeks.
/**
* Returns the count of the day of the start day of the month between start and end inclusively.
*/
public static int getCountOfDayOfMonthBetween(LocalDate start, LocalDate end) {
// the target day of the month to count
final int dom = start.getDayOfMonth();
// 'start' is counted as a valid day
int count = 1;
LocalDate currDate = start.plusMonths(1);
while (!currDate.isAfter(end)) {
try {
currDate = currDate.withDayOfMonth(dom);
// day of month changed, so check that the date is still valid
if (currDate.isAfter(end)) {
return count;
} else {
count++;
}
} catch (DateTimeException e) {
}
currDate = currDate.plusMonths(1);
}
return count;
}
public static void main(String[] args) {
LocalDate start1 = LocalDate.of(2020, Month.JANUARY, 29);
LocalDate end1 = LocalDate.of(2020, Month.APRIL, 28);
System.out.println(getCountOfDayOfMonthBetween(start1, end1)); // 3
LocalDate start2 = LocalDate.of(2019, Month.JANUARY, 31);
LocalDate end2 = LocalDate.of(2019, Month.MAY, 28);
System.out.println(getCountOfDayOfMonthBetween(start2, end2)); // 2
}
private int getCountOfDayOfMonthBetween(int dayOfMonth, LocalDate scheduleStartDate, LocalDate scheduleEndDate) {
if(scheduleEndDate == null) {
return -1;
}
LocalDate currDate = scheduleStartDate.withDayOfMonth(dayOfMonth);
LocalDate convertedDate = scheduleStartDate.withDayOfMonth(
scheduleStartDate.getMonth().length(scheduleStartDate.isLeapYear()));
int recordCount = 0;
while (currDate.isBefore(scheduleEndDate)) {
if(convertedDate.getDayOfMonth() >= scheduleStartDate.getDayOfMonth()) {
recordCount++;
}
currDate = currDate.plusMonths(1);
convertedDate = convertedDate.plusMonths(1);
convertedDate = convertedDate.withDayOfMonth(
convertedDate.getMonth().length(convertedDate.isLeapYear()));
}
return recordCount;
}

local time parser doesnt work [java]

Beneath the question I have put my code.For some reason it can't parse 6:00 and 17:30 to localtime and then put it as boolean in the ifstatetement, if it is before 6:00 put the values in the nachtMap, if it is after 17:30 , put it as well in the NachtMap, otherwise in Daymap (its basically all in the for loop).
Any one who knows whats wron with my code?
// for each license plate
for (int i = 1, n = licenseplateList.size(); i < n; i++) {
//first look at whether it is considered as day or night time
try {
String time1Str = begintimeList.get(i);
String time2Str = endingtimeList.get(i);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
LocalTime time1 = LocalTime.parse(time1Str, formatter);
LocalTime time2 = LocalTime.parse(time2Str, formatter);
// add date value: start with today
LocalDate today = LocalDate.now();
// lower value will be assigned to today
LocalDateTime dateTime1 = LocalDateTime.of(today, time1);
// upper value will be assigned same day if it is after lower value
// otherwise (we crossed date boundary) assign it to tomorrow
LocalDateTime dateTime2 = time2.isAfter(time1) ?
LocalDateTime.of(today, time2) : LocalDateTime.of(today.plusDays(1), time2);
Duration duration = Duration.between(dateTime1, dateTime2);
Duration halfDuration = duration.dividedBy(2);
LocalTime result = LocalTime.from(halfDuration.addTo(time1));
System.out.println(result.format(formatter));
LocalTime calc = LocalTime.parse("6:00");
LocalTime calc2 = LocalTime.parse("17:30");
boolean before = result.format(formatter).before(calc);
boolean after = result.format(formatter).after(calc2);
if (before == true) {
nachtMap.put(licenseplateList.get(i), kmperlineList.get(i));
}
else {
if(after == true) {
nachtMap.put(licenseplateList.get(i), kmperlineList.get(i));
}
else {
dagMap.put(licenseplateList.get(i), kmperlineList.get(i));
}
}
}catch (Exception e) {
}
Perhaps you should use H:mm instead of HH:mm, it can parse both 06:00 and 6:00
#Test
public void test() {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm");
Assert.assertEquals(
LocalTime.parse("6:00", formatter),
LocalTime.parse("06:00", formatter)
);
}

Iterate on all weeks between start, finish date

I have two Date objects, start and finish. I want to iterate them on a weekly basis, i.e. if there are exactly 4 weeks in between (calendar weeks, not just 7 days after each other), I want 4 iterations and in each iteration I want to get the actual start and end Dates.
I'm currently hacking up an Iterable for the purpose, but I'm thinking if it can be achieved easily with for example Joda Time or a smart custom method.
Thanks in advance!
EDIT: I must repeat that I need weeks as in calendar, not seven days after each other. If my start date is on a random day in the week (for example friday), my first iteration should contain [friday,sunday] not [friday,friday+7 days]. Solution posted as answer.
In Java 9 there is a nice LocalDate.datesUntil method. There is also an easy way to adjust time within a week boundaries using TemporalAdjusters.
int numOfWeeks = 4;
// First iteration with alignment
LocalDate start = LocalDate.now();
LocalDate endOfTheFirstWeek = start.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
out.println(String.format("[%s,%s]", start, endOfTheFirstWeek));
// Remaining iterations
endOfTheFirstWeek
.datesUntil(endOfTheFirstWeek.plusWeeks(numOfWeeks - 1), Period.ofWeeks(1))
.forEach(it -> {
LocalDate from = it.plusDays(1);
LocalDate to = from.plusWeeks(1);
out.println(String.format("[%s,%s]", from, to));
});
To calculate using Calendar and WEEK_OF_YEAR:
int startWeek;
int finishWeek;
int diff;
SimpleDateFormat sdf;
Calendar cal;
Calendar startCountingCal;
Date startDate;
Date finishDate;
String startDateS = "01/01/2013";
String finishDateS = "01/05/2013";
sdf = new SimpleDateFormat("dd/MM/yyyy");
startDate = sdf.parse(startDateS);
finishDate = sdf.parse(finishDateS);
cal = Calendar.getInstance();
cal.setTime(startDate);
startWeek = cal.get(Calendar.WEEK_OF_YEAR);
cal.setTime(finishDate);
finishWeek = cal.get(Calendar.WEEK_OF_YEAR);
diff = finishWeek - startWeek;
startCountingCal = Calendar.getInstance();
startCountingCal.setTime(startDate);
for (int i = 0; i < diff; i++) {
if (i==0) {
System.out.println("WEEK " + i + " start: " + sdf.format(startCountingCal.getTime()));
startCountingCal.add(Calendar.DATE, 7);
System.out.println("WEEK " + i + " start: " + sdf.format(startCountingCal.getTime()));
} else {
System.out.println("WEEK " + i + " start: " + sdf.format(startCountingCal.getTime()));
startCountingCal.add(Calendar.DATE, 7);
System.out.println("WEEK " + i + " start: " + sdf.format(startCountingCal.getTime()));
}
}
Output:
WEEK 0 start: 01/01/2013
WEEK 0 start: 08/01/2013
WEEK 1 start: 08/01/2013
WEEK 1 start: 15/01/2013
WEEK 2 start: 15/01/2013
WEEK 2 start: 22/01/2013
WEEK 3 start: 22/01/2013
WEEK 3 start: 29/01/2013
....
etc.
Hope it helps!
Thanks everyone, here is a working solution that fulfills my requirements (note that adding seven days to some day won't produce a calendar week in most cases, only if it's a monday!)
This code uses Joda Time API.
private static List<Interval> getWeeks(final DateTime start, final DateTime finish)
{
final int weekLeaps = Weeks.weeksBetween(start, finish).getWeeks();
if (weekLeaps == 0) {
return ImmutableList.of(new Interval(start, finish));
} else {
// We hop sundays at least once
final ImmutableList.Builder<Interval> resBuild = ImmutableList.<Interval> builder();
// First week
DateTime wStart = start;
DateTime wEnd = rollToWeekEnd(start);
resBuild.add(new Interval(wStart, wEnd));
// Other weeks
for (int i = 0; i < weekLeaps; i++) {
wStart = wEnd.plusSeconds(1);
DateTime actWeekEnd = rollToWeekEnd(wStart);
wEnd = actWeekEnd.isBefore(finish) ? actWeekEnd : finish;
resBuild.add(new Interval(wStart, wEnd));
}
return resBuild.build();
}
}
private static DateTime rollToWeekEnd(final DateTime from)
{
return from.withDayOfWeek(DateTimeConstants.SUNDAY)
.withHourOfDay(23)
.withMinuteOfHour(59)
.withSecondOfMinute(59)
.withMillisOfSecond(0);
}

Calculate number of weekdays between two dates in Java

Can anyone point me to some Java snippet wherein i can get business (except Sat and Sun) days between two dates.
public static int getWorkingDaysBetweenTwoDates(Date startDate, Date endDate) {
Calendar startCal = Calendar.getInstance();
startCal.setTime(startDate);
Calendar 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 {
//excluding start date
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()); //excluding end date
return workDays;
}
Start date and end date are exclusive, Only the days between given
dates will be counted. Start date and end date will not be included.
Solution without loop:
static long days(Date start, Date end){
//Ignore argument check
Calendar c1 = Calendar.getInstance();
c1.setTime(start);
int w1 = c1.get(Calendar.DAY_OF_WEEK);
c1.add(Calendar.DAY_OF_WEEK, -w1);
Calendar c2 = Calendar.getInstance();
c2.setTime(end);
int w2 = c2.get(Calendar.DAY_OF_WEEK);
c2.add(Calendar.DAY_OF_WEEK, -w2);
//end Saturday to start Saturday
long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
long daysWithoutWeekendDays = days-(days*2/7);
// Adjust days to add on (w2) and days to subtract (w1) so that Saturday
// and Sunday are not included
if (w1 == Calendar.SUNDAY && w2 != Calendar.SATURDAY) {
w1 = Calendar.MONDAY;
} else if (w1 == Calendar.SATURDAY && w2 != Calendar.SUNDAY) {
w1 = Calendar.FRIDAY;
}
if (w2 == Calendar.SUNDAY) {
w2 = Calendar.MONDAY;
} else if (w2 == Calendar.SATURDAY) {
w2 = Calendar.FRIDAY;
}
return daysWithoutWeekendDays-w1+w2;
}
Solution without loop in 5 lines of code
Days between are defined in the same way as ChronoUnit.DAYS.between(start, end) which means there are 4 days between Monday and Friday. Since we are only interested in weekdays we have to subtract weekends, therefore from Friday until Tuesday there will be 2 weekdays(just compute endDay - startDay and subtract 2 for the weekend). Add 1 to the result if you want an inclusive result, i.e. not days between.
I present two solutions.
First solution (5-liner, short and cryptic):
import java.time.*;
import java.time.temporal.*;
public static long calcWeekDays1(final LocalDate start, final 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);
}
Second solution:
public static long calcWeekDays2(final LocalDate start, final LocalDate end) {
final int startW = start.getDayOfWeek().getValue();
final int endW = end.getDayOfWeek().getValue();
final long days = ChronoUnit.DAYS.between(start, end);
long result = days - 2*(days/7); //remove weekends
if (days % 7 != 0) { //deal with the rest days
if (startW == 7) {
result -= 1;
} else if (endW == 7) { //they can't both be Sunday, otherwise rest would be zero
result -= 1;
} else if (endW < startW) { //another weekend is included
result -= 2;
}
}
return result;
}
java.time
The modern way is with the java.time classes.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );
DayOfWeek enum
The DayOfWeek enum provides a singleton instance for each of the sever days of the week.
DayOfWeek dow = start.getDayOfWeek();
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) …
We can collect the desired dates in a List.
int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );
…
if( dow.equals( DayOfWeek.SATURDAY ) || dow.equals( DayOfWeek.SUNDAY ) ) {
dates.add( date );
…
An EnumSet is an extremely efficient, fast and low-memory, implementation of Set. We can use an EnumSet instead of the if statement seen above.
Set<DayOfWeek> weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
if( weekend.contains( dayOfWeek ) ) …
Put that all together.
LocalDate date = start ;
while( date.isBefore( stop ) ) {
if( ! weekend.contains( date.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
dates.add( date ) ;
}
// Prepare for next loop.
date = date.plusDays( 1 ); // Increment to next day.
}
nextWorkingDay TemporalAdjuster
Another approach uses the ThreeTen-Extra project to add classes that work with java.time.
The Temporals class adds additional implementations of TemporalAdjuster for manipulating date-time values. We want the nextWorkingDay adjuster to increment the date while skipping over Saturday & Sunday.
LocalDate start = LocalDate.of( 2016 , 1 , 23 );
LocalDate stop = start.plusMonths( 1 );
int initialCapacity = Duration.between( start , stop ).toDays() ;
List<LocalDate> dates = new ArrayList<>( initialCapacity );
LocalDate date = start.minusDays( 1 ); // Start a day ahead.
while( date.isBefore( stop ) ) {
date = date.with( org.threeten.extra.Temporals.nextWorkingDay() );
// Double-check ending date as the `nextWorkingDay` adjuster could move us past the stop date.
if( date.isBefore( stop ) ) {
dates.add( date ) ;
}
}
Performance
I am curious about the performance of the various approach in various Answers on this page. I am considering only the modern java.time code, not the code using troublesome legacy Date/Calendar classes.
Here are four methods that each return the number of days elapsed.
One uses the clever math-based approach seen in the Answer by Roland.
private long countWeekDaysMath ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Roland.
// https://stackoverflow.com/a/44942039/642706
long count = 0;
final DayOfWeek startW = start.getDayOfWeek();
final DayOfWeek stopW = stop.getDayOfWeek();
final long days = ChronoUnit.DAYS.between( start , stop );
final long daysWithoutWeekends = days - 2 * ( ( days + startW.getValue() ) / 7 );
//adjust for starting and ending on a Sunday:
count = daysWithoutWeekends + ( startW == DayOfWeek.SUNDAY ? 1 : 0 ) + ( stopW == DayOfWeek.SUNDAY ? 1 : 0 );
return count;
}
Two use approaches seen in this Answer of mine: (a) Visit each date, incrementing one-by-one in a conventional loop.
private long countWeekDaysVisit ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Basil Bourque.
// https://stackoverflow.com/a/40369140/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
LocalDate ld = start;
while ( ld.isBefore( stop ) ) {
if ( ! weekend.contains( ld.getDayOfWeek() ) ) { // If not weekend, collect this LocalDate.
count++;
}
// Prepare for next loop.
ld = ld.plusDays( 1 ); // Increment to next day.
}
return count;
}
…and, (b) Using the TemporalAdjuster implementation org.threeten.extra.Temporals.nextWorkingDay().
private long countWeekDaysAdjuster ( LocalDate start , LocalDate stop ) {
// Code taken from Answer by Basil Bourque.
// https://stackoverflow.com/a/40369140/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
TemporalAdjuster nextWorkingDayTA = org.threeten.extra.Temporals.nextWorkingDay();
LocalDate ld = start;
if ( weekend.contains( ld.getDayOfWeek() ) ) {
ld = ld.with( nextWorkingDayTA );
}
while ( ld.isBefore( stop ) ) {
count++;
// Prepare for next loop.
ld = ld.with( nextWorkingDayTA ); // Increment to next working day (non-weekend day).
}
return count;
}
The last uses Java Streams approach seen in the Answer by Ravindra Ranwala.
private long countWeekDaysStream ( LocalDate start , LocalDate stop ) {
// Code taken from the Answer by Ravindra Ranwala.
// https://stackoverflow.com/a/51010738/642706
long count = 0;
Set < DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY );
final long weekDaysBetween = start.datesUntil( stop )
.filter( d -> ! weekend.contains( d.getDayOfWeek() ) )
.count();
return count;
}
And the test harness.
Caveats:
Well, the usual caveats about micro-benchmarking being untrustworthy, prone to unjustified or unrealistic conclusions.
I wish I'd learned to use the JMH micro-benchmarking framework.
I have not bothered to try optimizing any of this code. For example, in real work, the TemporalAdjuster could be cached outside our method.
Test harness.
LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 1 );
LocalDate stop = start.plusYears( 1 );
int runs = 100_000;
long go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysMath( start , stop );
}
long elapsedMath = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysVisit( start , stop );
}
long elapsedVisit = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysStream( start , stop );
}
long elapsedAdjuster = ( System.nanoTime() - go );
go = System.nanoTime();
for ( int i = 1 ; i <= runs ; i++ ) {
long count = this.countWeekDaysStream( start , stop );
}
long elapsedStream = ( System.nanoTime() - go );
System.out.println( "math: " + elapsedMath + " each: " + ( elapsedMath / runs ) );
System.out.println( "visit: " + elapsedVisit + " each: " + ( elapsedVisit / runs ) );
System.out.println( "adjuster: " + elapsedAdjuster + " each: " + ( elapsedAdjuster / runs ) );
System.out.println( "stream: " + elapsedStream + " each: " + ( elapsedStream / runs ) );
When run on my MacBook Pro (Sierra) with Oracle JDK 10.0.1 and ThreeTen-Extra version 1.3.2, I get results consistently close to the following. The math solution is a tiny fraction of the others at a couple hundred nanos versus several thousand, as we would expect obviously. Of the other three, the TemporalAdjuster is the longest, always over 10,000 nanos each. The visit and stream both come in well under that 10,000 nanos each, with visit being noticeably faster than streams. As seen in other examples around the internets, Java Streams usually make for nifty short code while often running significantly longer, about 20% longer in this case.
math: 18313309 each: 183
visit: 708420626 each: 7084
adjuster: 1002157240 each: 10021
stream: 924724750 each: 9247
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I used Shengyuan Lu's solution, but I needed to make a fix for the case where the method is called when one of the dates is on a Saturday and the other a Sunday - otherwise the answer is off by a day:
static long days(Date start, Date end){
//Ignore argument check
Calendar c1 = GregorianCalendar.getInstance();
c1.setTime(start);
int w1 = c1.get(Calendar.DAY_OF_WEEK);
c1.add(Calendar.DAY_OF_WEEK, -w1 + 1);
Calendar c2 = GregorianCalendar.getInstance();
c2.setTime(end);
int w2 = c2.get(Calendar.DAY_OF_WEEK);
c2.add(Calendar.DAY_OF_WEEK, -w2 + 1);
//end Saturday to start Saturday
long days = (c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60*24);
long daysWithoutSunday = days-(days*2/7);
if (w1 == Calendar.SUNDAY) {
w1 = Calendar.MONDAY;
}
if (w2 == Calendar.SUNDAY) {
w2 = Calendar.MONDAY;
}
return daysWithoutSunday-w1+w2;
}
Almost all the solutions are pretty much obsoleted and narrative. However here's a much condensed and readable solution.
This approach uses a Java Stream provided by the LocalDate::datesUntil method built into in Java 9 and later.
LocalDate startDate = LocalDate.of(2018, 5, 2);
LocalDate endDate = LocalDate.now();
Set<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
final long weekDaysBetween = startDate.datesUntil(endDate)
.filter(d -> !weekend.contains(d.getDayOfWeek()))
.count();
.datesUntil returns a sequential ordered stream of dates. The
returned stream starts from this date (inclusive) and goes to
endExclusive (exclusive) by an incremental step of 1 day.
Then all the Saturdays and Sundays are filtered out. Final step is to get the count of the remaining week days.
Java-9 has been released one year ago, since using it now seems reasonable to me.
This thread is filled with failing solutions... I started by writing a little test file which met my needs, and saw that Roland's both solutions fails, Amir's too. I wanted a solution that uses java 8 and that does not uses loops because, do I have to say why ?
So here's the test file :
#Test
public void test() {
LocalDate d1 = LocalDate.of(2018, 8, 1);
LocalDate d2 = LocalDate.of(2018, 8, 2);
LocalDate d3 = LocalDate.of(2018, 8, 3);
LocalDate d4 = LocalDate.of(2018, 8, 4);
LocalDate d5 = LocalDate.of(2018, 8, 5);
LocalDate d6 = LocalDate.of(2018, 8, 6);
LocalDate d7 = LocalDate.of(2018, 8, 7);
LocalDate d8 = LocalDate.of(2018, 8, 8);
LocalDate d9 = LocalDate.of(2018, 8, 9);
LocalDate d10 = LocalDate.of(2018, 8, 10);
LocalDate d15 = LocalDate.of(2018, 8, 15);
LocalDate dsep = LocalDate.of(2018, 9, 5);
// same day : 0 days between
Assert.assertEquals(0, DateUtils.calcWeekDays1(d1, d1));
Assert.assertEquals(1, DateUtils.calcWeekDays1(d1, d2));
Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d3));
// end on week-end
Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d4));
Assert.assertEquals(2, DateUtils.calcWeekDays1(d1, d5));
// next week
Assert.assertEquals(3, DateUtils.calcWeekDays1(d1, d6));
Assert.assertEquals(4, DateUtils.calcWeekDays1(d1, d7));
Assert.assertEquals(5, DateUtils.calcWeekDays1(d1, d8));
Assert.assertEquals(6, DateUtils.calcWeekDays1(d1, d9));
Assert.assertEquals(7, DateUtils.calcWeekDays1(d1, d10));
// start on saturday
Assert.assertEquals(0, DateUtils.calcWeekDays1(d4, d5));
Assert.assertEquals(0, DateUtils.calcWeekDays1(d4, d6));
Assert.assertEquals(1, DateUtils.calcWeekDays1(d4, d7));
// start on sunday
Assert.assertEquals(0, DateUtils.calcWeekDays1(d5, d5));
Assert.assertEquals(0, DateUtils.calcWeekDays1(d5, d6));
Assert.assertEquals(1, DateUtils.calcWeekDays1(d5, d7));
// go to next week
Assert.assertEquals(10, DateUtils.calcWeekDays1(d1, d15));
// next month
Assert.assertEquals(25, DateUtils.calcWeekDays1(d1, dsep));
// start sat, go to next month
Assert.assertEquals(22, DateUtils.calcWeekDays1(d4, dsep));
}
And here is my proposed solution, quite simple. Just let java count the number of weeks, multiply by five, and add the number of days needed to compensate the difference ; the only trick is adjusting the start and end to avoid week-ends :
public static long calcWeekDays1(LocalDate start, LocalDate end) {
if (start.getDayOfWeek().getValue() > 5) {
start = start.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
}
if (end.getDayOfWeek().getValue() > 5) {
end = end.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
}
if (start.isAfter(end)) { // may happen if you start sat. and end sunday
return 0;
}
long weeks = ChronoUnit.WEEKS.between(start, end);
if (start.getDayOfWeek().getValue() > end.getDayOfWeek().getValue()) {
weeks += 1;
}
return 5 * weeks + end.getDayOfWeek().getValue() - start.getDayOfWeek().getValue();
}
And now I will look stupid if my code fails too :)
I don't have a Java based solution, but have a PHP one, hope it helps:
function getDate($days) {
for ($i = 0; $i < $days; $i ++) {
if (date('N' , strtotime('+' . ($i + 1) . ' days')) > 5) {
$days++;
}
}
return date('l, F jS', strtotime('+' . $days . ' days', time()));
}
This is my example without looping. Algorithm is same as 卢声远 Shengyuan Lus one but I used some features of JodaTime.
public static int getNumberOfBusinessDays(#Nonnull LocalDate from, #Nonnull LocalDate to) {
int fromDateDayOfWeek = from.getDayOfWeek();
int toDateDayOfWeek = to.getDayOfWeek();
int daysWithoutWeekends = 5 * Weeks.weeksBetween(
from.withDayOfWeek(DateTimeConstants.MONDAY), to).getWeeks();
if (fromDateDayOfWeek == DateTimeConstants.SUNDAY) {
fromDateDayOfWeek = DateTimeConstants.SATURDAY;
}
if (toDateDayOfWeek == DateTimeConstants.SUNDAY) {
toDateDayOfWeek = DateTimeConstants.SATURDAY;
}
return daysWithoutWeekends - (fromDateDayOfWeek - toDateDayOfWeek);
}
The do while in the solution of Piyush is wrong, it should be :
do {
if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
++workDays;
}
startCal.add(Calendar.DAY_OF_MONTH, 1);
} while (startCal.getTimeInMillis() < endCal.getTimeInMillis());
The startCal.add should add onto the Calendar.DATE field, not the Calendar.DAY_OF_MONTH, I was getting weird results with over Decemeber / January period.
This is my example without looping. It is a class in this example because I serialize it in some JSON output. Basically I work out the number of days between the two dates, divide by 7 and assign to a long to have a integer value for the number of weeks. Take the original number of days and subtract the number of weekends*2. This isn't quite perfect - you need to work out if there is a 'hangover' where the start is close to the end of the week and goes over the weekend. To correct for this I find the day of the week at the start and find the remainder of the number of days, and add those together to find the 'hangover' - and if it is more than 5 it is a weekend. It isn't quite perfect, and does not account for holidays at all. And no Joda in sight. That said there is also a issue with timezones.
import java.io.Serializable;
import java.util.Date;
public class BusinessDayCalculator implements Serializable {
private static long DAY = 86400000l;
private Date startTime;
private Date endTime;
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getStartTime() {
return startTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getEndTime() {
return endTime;
}
public long getHours() {
return (this.endTime.getTime() - this.startTime.getTime())/(1000*60*60);
}
public long getBusinessDays(){
long startDay = getDayFromDate(this.startTime);
long endDay = getDayFromDate(this.endTime);
long totalDays = endDay-startDay;
long totalWeekends = totalDays/7;
long day = getDay(this.startTime);
long hangover = totalDays % 7;
long intoWeekend = day + hangover;
if(intoWeekend>5){
totalWeekends++;
}
long totalBusinessDays = totalDays - (totalWeekends *2);
/*
System.out.println("Days = " + day );
System.out.println("Hangover = " + hangover );
System.out.println("Total Days = " + totalDays);
System.out.println("Total Weekends = " + totalWeekends);
System.out.println("Total Business Days = " + totalBusinessDays);
*/
return totalBusinessDays;
}
private long getDayFromDate( Date date ){
long d = date.getTime() / DAY;
return d;
}
private long getDay( Date date ){
long daysSinceEpoc = getDayFromDate(date);
long day = daysSinceEpoc % 7;
day = day + 4;
if(day>6) day = day - 7;
return day;
}
}
Solution for Java 8 without loop and INCLUSIVE intervals:
public long getDaysWithoutSundays(LocalDate startDate, LocalDate endDate) {
long numberOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
long numberOfSundays = numberOfDays / 7;
long rest = numberOfDays % 7;
if (rest > 0) {
int startToEnd = startDate.getDayOfWeek().getValue() - endDate.getDayOfWeek().getValue();
if (startToEnd > 0) {
numberOfSundays++;
}
else {
if (endDate.getDayOfWeek().equals(DayOfWeek.SUNDAY)) {
numberOfSundays++;
}
}
}
return numberOfDays - numberOfSundays;
}
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
*
* #author varun.vishwakarma
*
*/
public class FindWeekendsInDateRange {
static HashMap<Integer, String> daysOfWeek=null;
static {
daysOfWeek = new HashMap<Integer, String>();
daysOfWeek.put(new Integer(1), "Sun");
daysOfWeek.put(new Integer(2), "Mon");
daysOfWeek.put(new Integer(3), "Tue");
daysOfWeek.put(new Integer(4), "Wed");
daysOfWeek.put(new Integer(5), "Thu");
daysOfWeek.put(new Integer(6), "Fri");
daysOfWeek.put(new Integer(7), "Sat");
}
/**
*
* #param from_date
* #param to_date
* #return
*/
public static List<Date> calculateWeekendsInDateReange(Date fromDate, Date toDate) {
List<Date> listOfWeekends = new ArrayList<Date>();
Calendar from = Calendar.getInstance();
Calendar to = Calendar.getInstance();
from.setTime(fromDate);
to.setTime(toDate);
while (from.getTimeInMillis() < to.getTimeInMillis()) {
if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sat") {
Date sat = from.getTime();
listOfWeekends.add(sat);
} else if (daysOfWeek.get(from.get(Calendar.DAY_OF_WEEK)) == "Sun") {
Date sun = from.getTime();
listOfWeekends.add(sun);
}
from.add(Calendar.DAY_OF_MONTH, 1);
}
return listOfWeekends;
}
public static void main(String[] args) {
String fromDate = "7-Oct-2019";
String toDate = "25-Oct-2019";
System.out.println(FindWeekendsInDateRange.calculateWeekendsInDateReange(new Date(fromDate), new Date(toDate)));
}
}
I'm surprised that every solution given here fails to account for business holidays, which occur in most countries every few months at least, so any span more than a few months will give an incorrect answer. Fortunately, Ravindra's answer using Streams is, despite the inconsequential (for spans under ~10 years) overhead, the best one because it is trivial to correct:
import java.time.*;
import java.util.*;
import static java.time.temporal.ChronoUnit.DAYS;
public class WeekDaysLeft {
public static void main(String[] args) {
LocalDate startDate = LocalDate.now(); // Early 2023
LocalDate endDate = LocalDate.of(2023, 6, 23);
Set<DayOfWeek> weekendDays =
EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
Set<LocalDate> holidays =
Set.of(LocalDate.of(2023,4,7),
LocalDate.of(2023,4,10));
final long weekDaysBetween =
startDate.datesUntil(endDate)
.filter(d -> !weekendDays.contains(
d.getDayOfWeek()))
.filter(d -> !holidays.contains(d))
.count();
System.out.println(weekDaysBetween);
}
}
In regard to my claim that the overhead is inconsequential:
Run my solution under time(1) on Unix/Linux
e.g., time java WeekDaysLeft.java
Change the end-date to 2033, and repeat step 1.
On my system, the user time went DOWN slightly (user time up trivially).
And, of course, if you're running this after June, 2023, bump all the years given.
This program considers loop approach but consider activities happened on after work hours to next working day office start hour
public class BusinessDayCalculator {
private final String DATE_FORMAT = "dd/MM/yyyy HH:mm:ss";
private final int OFFICE_START_HOUR = 9;
private final int OFFICE_CLOSE_HOUR = 17;
private final int TOTAL_MINS_IN_BUSINESS_DAY = (OFFICE_CLOSE_HOUR - OFFICE_START_HOUR)*60;
public void dateDifference(String start, String end){
Date startDate = validateStringToDate(start);
Date endDate = validateStringToDate(end);
System.out.println(startDate);
System.out.println(endDate);
Calendar startDay = convertDateToCalendar(startDate);
Calendar tempDay = (Calendar) startDay.clone();
Calendar endDay = convertDateToCalendar(endDate);
System.out.println(startDay.getTime());
System.out.println(endDay.getTime());
int workDays = -1;
int startDayDifference = 0;
int endDayDifference = 0;
int hours = 0;
int minsRemainder = 0;
if(!(startDay.get(Calendar.DAY_OF_YEAR) == endDay.get(Calendar.DAY_OF_YEAR)
&& startDay.get(Calendar.YEAR) == endDay.get(Calendar.YEAR))){
do{
tempDay.add(Calendar.DAY_OF_MONTH, 1);
if(tempDay.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY
&& tempDay.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY){
workDays++;
}
}while(tempDay.getTimeInMillis() <= endDay.getTimeInMillis());
if(workDays > 0){
workDays = workDays - 1;
}
}
startDayDifference = hourDifferenceInMinutesOfStartDay(startDay);
endDayDifference = hourDifferenceInMinutesOfEndDay(endDay);
minsRemainder = (startDayDifference + endDayDifference) % TOTAL_MINS_IN_BUSINESS_DAY;
workDays = workDays + ((startDayDifference + endDayDifference) / TOTAL_MINS_IN_BUSINESS_DAY);
hours = minsRemainder/60;
minsRemainder = minsRemainder % 60;
System.out.println(workDays + "d "+ hours + "hrs " + minsRemainder + " mins");
}
private int hourDifferenceInMinutesOfEndDay(Calendar endDay) {
long endTimestamp = endDay.getTimeInMillis();
System.out.println(endTimestamp);
endDay.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
endDay.set(Calendar.MINUTE,0);
long endDayOfficeStartTimestamp = endDay.getTimeInMillis();
System.out.println(endDayOfficeStartTimestamp);
int difference = (int)((endTimestamp - endDayOfficeStartTimestamp) / 1000) / 60;
System.out.println(difference);
return difference;
}
private int hourDifferenceInMinutesOfStartDay(Calendar startDay) {
long starttimestamp = startDay.getTimeInMillis();
System.out.println(starttimestamp);
startDay.set(Calendar.HOUR_OF_DAY, OFFICE_CLOSE_HOUR);
startDay.set(Calendar.MINUTE,0);
long startDayOfficeCloseTimestamp = startDay.getTimeInMillis();
System.out.println(startDayOfficeCloseTimestamp);
int difference = (int)((startDayOfficeCloseTimestamp - starttimestamp) / 1000) / 60;
System.out.println(difference);
return difference;
}
public Calendar convertDateToCalendar(Date date){
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
|| calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
calendar = handleActivityOnAfterWorkHoursOrWeekendOrHolidays(calendar);
}
if(calendar.get(Calendar.HOUR_OF_DAY) >= OFFICE_CLOSE_HOUR
&& calendar.get(Calendar.MINUTE) > 0){
calendar = handleActivityOnAfterWorkHoursOrWeekendOrHolidays(calendar);
}
if(calendar.get(Calendar.HOUR_OF_DAY) < OFFICE_START_HOUR){
calendar.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
calendar.set(Calendar.MINUTE,0);
}
return calendar;
}
private Calendar handleActivityOnAfterWorkHoursOrWeekendOrHolidays(Calendar calendar) {
do{
calendar.add(Calendar.DAY_OF_MONTH, 1);
}while(isHoliday(calendar));
calendar.set(Calendar.HOUR_OF_DAY, OFFICE_START_HOUR);
calendar.set(Calendar.MINUTE,0);
return calendar;
}
private boolean isHoliday(Calendar calendar) {
if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
|| calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){
return true;
}
return false;
}
public Date validateStringToDate(String input){
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
Date date = null;
try{
date = dateFormat.parse(input);
}catch(ParseException exception){
System.out.println("invalid date format");
throw new RuntimeException("invalid date format");
}
return date;
}
public static void main(String[] args){
BusinessDayCalculator calc = new BusinessDayCalculator();
String startDate = "27/12/2016 11:38:00";
String endDate = "04/01/2017 12:38:00";
calc.dateDifference(startDate, endDate);
}
}
In groovy:
public static int getWorkingDaysBetweenDates (Date start, Date end) {
def totalDays = (Integer) (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)
def int workingDays = 0
(0..totalDays).each { def dow = (start + it)[Calendar.DAY_OF_WEEK]; if(dow != Calendar.SATURDAY && dow != Calendar.SUNDAY){workingDays++} }
workingDays
}
Using java 8 it can be easily done, example function:
long getBusinessDaysDifference(LocalDate startDate, LocalDate endDate) {
EnumSet<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
List<LocalDate> list = Lists.newArrayList();
LocalDate start = startDate;
while (start.isBefore(endDate)) {
list.add(start);
start = start.plus(1, ChronoUnit.DAYS);
}
long numberOfDays = list.stream().filter(d -> !weekend.contains(d.getDayOfWeek())).count();
return numberOfDays;
}
Description:
Define your off-days in an EnumSet (weekends in this case).
Create a list holding all the days between the startDate and endDate.
Reduce the outcome list by removing any occurrence of a day from the EnumSet.
Then finally count the size of this reduced list.
Note: this function can be optimized, but might be helpful as a starting point.
public long getNumberOfWeekDayBetweenDates(LocalDate startDate, LocalDate endDate, String dayOfWeek) {
long result = -1;
if (startDate != null && endDate != null && dayOfWeek != null && (startDate.isBefore(endDate) || startDate.isEqual(endDate))) {
java.time.DayOfWeek givenDayOfWeek = java.time.DayOfWeek.valueOf(dayOfWeek);
// find the first given day of week in the interval
LocalDate firstOccurrence = startDate.with(TemporalAdjusters.nextOrSame(givenDayOfWeek));
// similarly find last Monday
LocalDate lastOccurrence = endDate.with(TemporalAdjusters.previousOrSame(givenDayOfWeek));
if (firstOccurrence != null && lastOccurrence != null) {
// count the number of weeks between the first and last occurrence, then add 1 as end day is exclusive
result = ChronoUnit.WEEKS.between(firstOccurrence, lastOccurrence) + 1;
} else if (firstOccurrence == null && lastOccurrence == null) {
// no occurrence
result = 0;
} else {
result = 1;
}
}
return result;
}
Here is a set-based solution that completes in constant time for any given subset of weekdays, not just Monday-Friday. It splits the problem into counting full weeks and counting the days in the residual week. If you are interested, here's a detailed explanation and a formal proof that the algorithm is correct. Note that the intervals are inclusive, i.e. startDate and endDate are counted in. If startDate is after endDate, the result is zero rather than negative.
long countWeekDays(LocalDate startDate, LocalDate endDate, Set<DayOfWeek> daysOfWeek) {
long periodLength = Math.max(0, ChronoUnit.DAYS.between(startDate, endDate) + 1);
long fullWeeks = periodLength / 7;
long residualWeekLength = periodLength % 7;
Set<DayOfWeek> residualWeekDays = LongStream.range(0, residualWeekLength)
.mapToObj(offset -> startDate.plusDays(offset).getDayOfWeek())
.collect(Collectors.toSet());
residualWeekDays.retainAll(daysOfWeek);
return fullWeeks * daysOfWeek.size() + residualWeekDays.size();
}
For the original problem (Monday-Friday) it is called e.g. with:
countWeekDays(
LocalDate.of(2016, 2, 8),
LocalDate.of(2016, 2, 26),
new HashSet(Arrays.asList(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
)
)
)
This assumes you are working with inclusive intervals. If you want to skip the first day of the interval, just add one day to the first parameter:
countWeekDays(
LocalDate.of(2016, 2, 8).plusDays(1),
LocalDate.of(2016, 2, 26),
new HashSet(Arrays.asList(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
)
)
)
Likewise, if you want to skip the last day of the inclusive interval, subtract one day from the second parameter:
countWeekDays(
LocalDate.of(2016, 2, 8),
LocalDate.of(2016, 2, 26).minusDays(1),
new HashSet(Arrays.asList(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
)
)
)
Finally, if you need to skip both interval-delimiting days, combine the two previous modifications:
countWeekDays(
LocalDate.of(2016, 2, 8).plusDays(1),
LocalDate.of(2016, 2, 26).minusDays(1),
new HashSet(Arrays.asList(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
)
)
)
For LocalDate supported by latest java version you can try below function.
It provides support of functiongetDayOfWeek().
The getDayOfWeek() method of LocalDate class in Java gets the day-of-week field, which is an enum DayOfWeek.
public static int getWeekEndCount(LocalDate fromDate, LocalDate toDate) {
int saturday = 0;
int sunday = 0;
while (!fromDate.isAfter(toDate)) {
if (fromDate.getDayOfWeek().equals(DayOfWeek.SATURDAY))
saturday++;
else if (fromDate.getDayOfWeek().equals(DayOfWeek.SUNDAY))
sunday++;
fromDate = fromDate.plusDays(1);
}
System.out.println("Saturday count=="+saturday);
System.out.println("Sunday count=="+sunday);
return saturday+sunday;
}

Categories

Resources