I am using Joda time api in a Spring 3.0 project for to calculate dates. Now I have a start and end date and I want to get everyday exept weekend or Saturday or Sunday between these two dates. How can I achieve this ?
I looked at this post Joda time - all mondays between two dates.
It offered some guidance but still vague as how to exclude two dates.
I assume your question is how to
get every day except weekend or Saturday or Sunday between two dates.
Solution :
public static void main(String[] args) {
final LocalDate start = LocalDate.now();
final LocalDate end = new LocalDate(2012, 1, 14);
LocalDate weekday = start;
if (start.getDayOfWeek() == DateTimeConstants.SATURDAY ||
start.getDayOfWeek() == DateTimeConstants.SUNDAY) {
weekday = weekday.plusWeeks(1).withDayOfWeek(DateTimeConstants.MONDAY);
}
while (weekday.isBefore(end)) {
System.out.println(weekday);
if (weekday.getDayOfWeek() == DateTimeConstants.FRIDAY)
weekday = weekday.plusDays(3);
else
weekday = weekday.plusDays(1);
}
}
I'm new here.
I've been looking for a solution to this problem and that did not use loop but have not found a suitable algorithm. So I decided to create this solution does not use loop, very efficient and the code has been tested.
public int betweenDaysIgnoreWeekends(DateTime startDate, DateTime endDate) {
//Um numero que representa o dia da semana para a data final, exemplo segunda=1, terça=2, quarta=3...
int dayOfWeekEndDateNumber = Integer.valueOf(endDate.dayOfWeek()
.getAsString());
//Um numero que representa o dia da semana para a data inicial, exemplo segunda=1, terça=2, quarta=3...
int dayOfWeekStartDateNumber = Integer.valueOf(startDate.dayOfWeek()
.getAsString());
//Se a data final for sabado ou domingo, finja ser sexta-feira
if (dayOfWeekEndDateNumber == 6 || dayOfWeekEndDateNumber == 7) {
int DaysToAdd = 8 - dayOfWeekEndDateNumber;
endDate = endDate.plusDays(DaysToAdd);
dayOfWeekEndDateNumber = Integer.valueOf(endDate.dayOfWeek()
.getAsString());
}
//Se a data inicial for sabado ou domingo, finja ser segunda-feira
if (dayOfWeekStartDateNumber == 6 || dayOfWeekStartDateNumber == 7) {
int DaysToAdd = 8 - dayOfWeekStartDateNumber;
startDate = startDate.plusDays(DaysToAdd);
dayOfWeekStartDateNumber = Integer.valueOf(startDate.dayOfWeek()
.getAsString());
}
//Quantos dias se passaram contando os fins de semana
int days = Days.daysBetween(startDate, endDate).getDays();
//Quantas semanas se passaram exatamente
int weeks = days / 7;
//O excesso de dias que sobrou, exemplo: 1 semana e 3 dias o excess=3 e weeks=1
int excess = days % 7;
//Se a data inicial for igual a data final, passou 0 dia
if (startDate.equals(endDate)) {
return 0;
} else {
//O excesso de dias passou pelo fim de semana, então deve-se retirar 2 dias
//da quantidade final de dias
if (excess + dayOfWeekStartDateNumber >= 6) {
//Quantidade de semanas * 5 dias uteis + o excesso de dias - o final de semana que o excesso atravessou
return weeks * 5 + excess - 2;
}
//Quantidade de semanas * 5 dias uteis + o excesso de dias
return weeks * 5 + excess;
}
}
I have been using #Josh Maag's logic for almost a year now but recently found that it returns wrong value when endDate happens to fall on a Saturday.
Here is the version I would like to share which considers to subtract days from endDate when it happens to be a Saturday or a Sunday.
public static int getDaysBetweenIgnoreWeekends(org.joda.time.DateTime startDate, org.joda.time.DateTime endDate, boolean ignoreTimeOfDay) {
// If the start date is equal to the closing date, spent 0 days
if (startDate.equals(endDate))
return 0;
if (ignoreTimeOfDay && startDate.toLocalDate().equals(endDate.toLocalDate()))
return 0;
// A number that represents the day for the start date, Monday = 1 , Tuesday = 2 , Wednesday = 3 ...
int dayOfWeekStartDateNumber = startDate.getDayOfWeek();
int dayOfWeekEndDateNumber = endDate.getDayOfWeek();
// If the starting date is Saturday or Sunday , pretend to be Monday
if (dayOfWeekStartDateNumber == 6 || dayOfWeekStartDateNumber == 7) {
int DaysToAdd = 8 - dayOfWeekStartDateNumber;
startDate = startDate.plusDays(DaysToAdd);
dayOfWeekStartDateNumber = Integer.valueOf(startDate.dayOfWeek().getAsString());
}
org.joda.time.DateTime effectiveEndDate = endDate;
if (dayOfWeekEndDateNumber == 6 || dayOfWeekEndDateNumber == 7) {
effectiveEndDate = endDate.minusDays(Math.abs(5 - dayOfWeekEndDateNumber));
}
// How many days have passed counting weekends
int days;
if(ignoreTimeOfDay) {
days = org.joda.time.Days.daysBetween(startDate.toLocalDate(), effectiveEndDate.toLocalDate()).getDays();
} else {
days = org.joda.time.Days.daysBetween(startDate, effectiveEndDate).getDays();
}
// How many weeks have passed
int weeks = days / 7;
// Excess days left. E.g. one week and three days the excess will be 3
int excess = days % 7;
// Excess of days spent for the weekend , then it must be removed two days
// the final number of days
if (excess + dayOfWeekStartDateNumber >= 6) {
// Week count * 5 working days + excess days - the weekend that excess crossed
return weeks * 5 + excess - 2;
}
// Weeks count * 5 working days + excess days
return weeks * 5 + excess;
}
In the earlier version - following snippet was subtracting an extra day. Subtracting -2 no matter if endDate was a Saturday or Sunday.
if (excess + dayOfWeekStartDateNumber >= 6) {
// Week count * 5 working days + excess days - the weekend that excess crossed
return weeks * 5 + excess - 2;
}
Hope that helps!
To improve upon what #samir-machado-de-oliveira posted, here is a function that will calculate days sans weekends without using a loop. I have not benchmarked this against the loop version, but it appears as though it would be faster:
/**
* Gets number of days between two dates. Ignoring weekends.
* #param startDate
* #param endDate
* #return
*/
public static int getDaysBetweenIgnoreWeekends(DateTime startDate, DateTime endDate) {
// If the start date is equal to the closing date, spent 0 days
if (startDate.equals(endDate))
return 0;
// A number that represents the day for the start date, Monday = 1 , Tuesday = 2 , Wednesday = 3 ...
int dayOfWeekStartDateNumber = startDate.getDayOfWeek();
// If the starting date is Saturday or Sunday , pretend to be Monday
if (dayOfWeekStartDateNumber == 6 || dayOfWeekStartDateNumber == 7) {
int DaysToAdd = 8 - dayOfWeekStartDateNumber;
startDate = startDate.plusDays(DaysToAdd);
dayOfWeekStartDateNumber = Integer.valueOf(startDate.dayOfWeek().getAsString());
}
// How many days have passed counting weekends
int days = Days.daysBetween(startDate, endDate).getDays();
// How many weeks have passed
int weeks = days / 7;
// Excess days left. E.g. one week and three days the excess will be 3
int excess = days % 7;
// Excess of days spent for the weekend , then it must be removed two days
// the final number of days
if (excess + dayOfWeekStartDateNumber >= 6) {
// Week count * 5 working days + excess days - the weekend that excess crossed
return weeks * 5 + excess - 2;
}
// Weeks count * 5 working days + excess days
return weeks * 5 + excess;
}
Also, here is a version that will allow you to ignore the time of the day, so that if the start date is at 11AM on the start time and the end time is the next day at 10AM it will show as 1 day instead of 0 days.
/**
* Gets number of days between two dates. Ignoring weekends. Ignores Hours.
* #param startDate
* #param endDate
* #return
*/
public static int getDaysBetweenIgnoreWeekends(DateTime startDate, DateTime endDate) {
return getDaysBetweenIgnoreWeekends(startDate,endDate,true);
}
/**
* Gets number of days between two dates. Ignoring weekends.
* #param startDate
* #param endDate
* #param ignoreTimeOfDay
* #return
*/
public static int getDaysBetweenIgnoreWeekends(DateTime startDate, DateTime endDate, boolean ignoreTimeOfDay) {
// If the start date is equal to the closing date, spent 0 days
if (startDate.equals(endDate))
return 0;
if (ignoreTimeOfDay && startDate.toLocalDate().equals(endDate.toLocalDate()))
return 0;
// A number that represents the day for the start date, Monday = 1 , Tuesday = 2 , Wednesday = 3 ...
int dayOfWeekStartDateNumber = startDate.getDayOfWeek();
// If the starting date is Saturday or Sunday , pretend to be Monday
if (dayOfWeekStartDateNumber == 6 || dayOfWeekStartDateNumber == 7) {
int DaysToAdd = 8 - dayOfWeekStartDateNumber;
startDate = startDate.plusDays(DaysToAdd);
dayOfWeekStartDateNumber = Integer.valueOf(startDate.dayOfWeek().getAsString());
}
// How many days have passed counting weekends
int days;
if(ignoreTimeOfDay) {
days = Days.daysBetween(startDate.toLocalDate(), endDate.toLocalDate()).getDays();
} else {
days = Days.daysBetween(startDate, endDate).getDays();
}
// How many weeks have passed
int weeks = days / 7;
// Excess days left. E.g. one week and three days the excess will be 3
int excess = days % 7;
// Excess of days spent for the weekend , then it must be removed two days
// the final number of days
if (excess + dayOfWeekStartDateNumber >= 6) {
// Week count * 5 working days + excess days - the weekend that excess crossed
return weeks * 5 + excess - 2;
}
// Weeks count * 5 working days + excess days
return weeks * 5 + excess;
}
Neatly using Java 8 Streams:
public LocalDate[] filterWeekdaysForRange(final LocalDate start,final LocalDate end) {
return Stream.iterate(start, date -> date.plusDays(1))
.limit(ChronoUnit.DAYS.between(start, end)+1)
.filter(d->d.getDayOfWeek() != SATURDAY)
.filter(d->d.getDayOfWeek() != SUNDAY)
.toArray(LocalDate[]::new);
}
This is an adaption of a solution provided here: https://stackoverflow.com/a/38220748/744133
You can use the gregorian calendar to retrieve the day for a particular date. If the string is Saturday or Sunday you can neglect it.
This will return the end date by excluding weekends
public static Date addDaysBySkipWeekend(Date startDate, int numDays) {
Calendar dateCal = Calendar.getInstance();
dateCal.setTime(startDate);
for (int i = 0; i < numDays-1; i++) {
dateCal.add(dateCal.DATE, 1);
if(dateCal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
|| dateCal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY ){
dateCal.add(dateCal.DATE, 1);
i--;
}
}
return dateCal.getTime();
}
Related
I got this class in java to calculate efferent between two dates but
the result coming with year I need this only month and days. Example 1
year 2 months 5 days is result. I need Result 14 month 5 days
Java code for date different calculation
public String getPeriod(Date a, Date b) {
Calendar startDay = Calendar.getInstance();
startDay.setTimeInMillis(a.getTime());
//create calendar object for current day
Calendar endDay = Calendar.getInstance();
endDay.setTimeInMillis(b.getTime());
//Get difference between years
years = endDay.get(Calendar.YEAR) - startDay.get(Calendar.YEAR);
int deathMonth = endDay.get(Calendar.MONTH) + 1;
int openMonth = startDay.get(Calendar.MONTH) + 1;
months = deathMonth - openMonth;
//if month difference is in negative then reduce years by one
//and calculate the number of months.
if (months < 0)
{
years--;
months = 12 - openMonth + deathMonth;
if (endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
months--;
}
else if (months == 0 && endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
{
years--;
months = 11;
}
//Calculate the days
if (endDay.get(Calendar.DATE) > startDay.get(Calendar.DATE))
days = endDay.get(Calendar.DATE) - startDay.get(Calendar.DATE);
else if (endDay.get(Calendar.DATE) < startDay.get(Calendar.DATE))
{
int today = endDay.get(Calendar.DAY_OF_MONTH);
endDay.add(Calendar.MONTH, -1);
days = endDay.getActualMaximum(Calendar.DAY_OF_MONTH) - startDay.get(Calendar.DAY_OF_MONTH) + today;
}
else
{
days = 0;
if (months == 12)
{
years++;
months = 0;
}
}
return (months+","+days+","+years);
}
Consider implementing your getPeriod() method using java's date-time API that was introduced in Java 8. Below is such an implementation:
/* Required imports.
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
*/
private static String getPeriod(LocalDate start, LocalDate end) {
long months = ChronoUnit.MONTHS.between(start, end);
long days = ChronoUnit.DAYS.between(start.plusMonths(months), end);
return String.format("%d months %d days", months, days);
}
If you call the method like so:
LocalDate start = LocalDate.of(2020, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2020, Month.APRIL, 29);
getPeriod(start, end);
The method will return the following string:
3 months 28 days
Refer to the Date Time trail in Oracle's java tutorials.
I've been stuck on this issue for the past 4 hours trying different strategies which haven't prevailed much at all having checked countless posts on here and Google. Basically what I am trying to do is print out only the business working days within a week mon-fri. I have tried incrementing the day of the year each time the weekend days come up but of course this messes up the order of the days after. For example instead of having:
Iteration: 0, 1, 2, 3, 4, 5, 6, 7, 8....
Day: (0)Friday, (1)Saturday, (2)Sunday, (3)Monday, (4)Tuesday, (5)Wednesday, (6)Thursday, (7)Friday, (8)Saturday....etc
I require:
Day: (0)Friday, (1)Monday, (2)Tuesday (3)Wednesday, (4)Thursday, (5)Friday, (6)Monday, (7)Tuesday, (8)Wednesday....etc
Here's the code:
public static void date(int day) {
now = Calendar.getInstance();
//SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");//version 1
SimpleDateFormat format = new SimpleDateFormat("EEEE d MMMM yyyy"); //version 2
String[] days = new String[maxDayCount]; //limits the number of days to print out(ordinarily h)
now.add(Calendar.DAY_OF_YEAR, day + 1); //Increments from test count + 1 due to count starting at 0 (would be today). Increments the date to get from today.
int DayOfWeek = now.get(Calendar.DAY_OF_WEEK);
boolean WorkingDayCheck = ((DayOfWeek >= Calendar.MONDAY) && (DayOfWeek <= Calendar.FRIDAY));
if(WorkingDayCheck) {
days[day] = format.format(now.getTime());
} else {
//
}
now.add(Calendar.DAY_OF_MONTH, 1);
System.out.println("-----------Day " + day + ": " + String.valueOf(days[day])); //v2: print out each line
}//END OF METHOD: date
The idea is that I have an iteration elsewhere which passes the day number to this method in order for this to use that number to then print out the date. I am now back to what I was with originally before trying a load of things which at the moment with the check I am doing with the WorkingDayCheck boolean the print out is of course returning null when the weekend days come about.
Any ideas chaps?
Thanks for your time.
I think this will solve your problem.
First, why do you define the array days each time you enter the method then you populate only one value in it?
Your String[] days = new String[maxDayCount]; should be a member variable not a local variable. So initialize this array outside this method.
Also initialize another int that represent the last empty location in your array. (defaults to 0)
public class Foo {
int maxDayCount = 365;
String[] days = new String[maxDayCount];
int finalIndex = 0;
public static void date(int day) {
now = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat("EEEE d MMMM yyyy");
String[] days = new String[maxDayCount]; //limits the number of days to print out(ordinarily h)
now.add(Calendar.DAY_OF_YEAR, day + 1); //Increments from test count + 1 due to count starting at 0 (would be today). Increments the date to get from today.
int DayOfWeek = now.get(Calendar.DAY_OF_WEEK);
boolean WorkingDayCheck = ((DayOfWeek >= Calendar.MONDAY) && (DayOfWeek <= Calendar.FRIDAY));
if(WorkingDayCheck) {
days[finalIndex++] = format.format(now.getTime());
System.out.print("(" + (finalIndex - 1) + ")" + String.valueOf(days[finalIndex - 1]))
} else {
//
}
}
public static void main(String[]args) {
int day = 0;
while(finalIndex != maxDaysCount) {
date(day++);
}
}
}
Now your days array will be populated with working days only in their respective indices. For example in days[0] will be Friday then days[1] will be Saturday and so on since you have a pointer finalIndex that is independent of the value of day that is input to your method from the iteration.
If I get your problem correctly you want to do somewhat like following,
Working Day #1 = 0 Week + 1 Day = 1st Day
..
Working Day #5 = 0 Week + 5 Days = 5th Day
Working Day #6 = 1 Week + 1 Day = 8th Day
..
Working Day #10 = 1 Week + 5 Days = 12th Day
And then print the 1st,2nd,3rd calendar day of the year.
If that is the case you can try finding the calendar day from working day like something,
int div = day/5;
int rem = day%5;
int calendarDay = 0;
if(day>5){
if(rem == 0)
calendarDay = (div-1)*7 + 5;
else
calendarDay = div*7 + rem;
}else
calendarDay = day;
And then find the Calendar day for that date like
Calendar calendar = new GregorianCalendar();
calendar.set(Calendar.DAY_OF_YEAR, day);
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb, Locale.ENGLISH);
formatter.format("%tD", calendar);
System.out.println(sb);
The code is not a solution, but I guess it points to where you want to go.
Having played around with the code I have achieved what I was after now with:
public static String getDate4(int day) {
now = Calendar.getInstance();
//SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");//version 1
SimpleDateFormat format = new SimpleDateFormat("EEEE d MMMM yyyy"); //version 2
now.add(Calendar.DAY_OF_YEAR, day + 1); //Increments from test count + 1 due to count starting at 0 (would be today). Increments the date to get from today.
int DayOfWeek = now.get(Calendar.DAY_OF_WEEK);
boolean WorkingDayCheck = ((DayOfWeek >= Calendar.MONDAY) && (DayOfWeek <= Calendar.FRIDAY));
if(WorkingDayCheck) {
days[finalIndex++] = format.format(now.getTime());
return "\n(" + (finalIndex - 1) + ") " + String.valueOf(days[finalIndex - 1]);
} else {
now.add(Calendar.DATE, 2); //advance dates by two
dayCount = dayCount + 2; //Increase the day count by 2 to ensure that later dates are also incremented to avoid duplication i.e. Monday(Sat), Tuesday(Sun), Monday, Tuesday, Wed...
days[finalIndex++] = format.format(now.getTime());
return "\n(" + (finalIndex - 1) + ") " + String.valueOf(days[finalIndex - 1]);
}//END OF if else
}//END OF getDate4 method
I had to add and use an external dayCount (similar to the day int #Mohammed Osama used) separate from the count used in the mains methods iteration which I used previously to feed into this method and then when a weekend came up I incremented the date by 2 which is something I had experimented with before however the problem came when the weekend ended and it was back onto non weekend dates which ended up with:
Friday
Monday (previously Saturday)
Tuesday (previously Sunday)
Monday (weekend over, now back onto Monday) <--- Issue here and onwards
Tuesday
This is where the new dayCount came in which is now fed into this method simply as:
//Main method
//Iteration code start
getDate4(dayCount++);
//Iteration end
Incrementing this by 2 each time a weekend was encountered pushed the later dates to their appropriate position therefore now I am getting what I was always after being:
Friday
Monday (previously Saturday) <-Pushed ahead by 2 dates to Monday
Tuesday (previously Sunday) <-Pushed ahead by 2 dates to Tuesday
Wednesday (previously Monday) <-Pushed ahead 2 dates to Wednesday due to weekends incrementing the dayCount by 2
Thursday <--- etc. etc.
Thanks a lot for the help folks, can move on now finally :>
Other answers here refer to Joda API.
I want to do it using java.time.
Suppose today's date is 26th Nov 2015-Thursday, when I add 2 business days to it,
I want the result as Monday 30th Nov 2015.
I am working on my own implementation but it would be great if something already exists!
EDIT:
Is there a way to do it apart from looping over?
I was trying to derive a function like:
Y = f(X1,X2) where
Y is actual number of days to add,
X1 is number of business days to add,
X2 is day of the week (1-Monday to 7-Sunday)
Then given X1 and X2 (derived from day of week of the date), we can find Y and then use plusDays() method of LocalDate.
I have not been able to derive it so far, its not consistent. Can anyone confirm that looping over until desired number of workdays are added is the only way?
The following method adds days one by one, skipping weekends, for positive values of workdays:
public LocalDate add(LocalDate date, int workdays) {
if (workdays < 1) {
return date;
}
LocalDate result = date;
int addedDays = 0;
while (addedDays < workdays) {
result = result.plusDays(1);
if (!(result.getDayOfWeek() == DayOfWeek.SATURDAY ||
result.getDayOfWeek() == DayOfWeek.SUNDAY)) {
++addedDays;
}
}
return result;
}
After some fiddling around, I came up with an algorithm to calculate the number of workdays to add or subtract.
/**
* #param dayOfWeek
* The day of week of the start day. The values are numbered
* following the ISO-8601 standard, from 1 (Monday) to 7
* (Sunday).
* #param businessDays
* The number of business days to count from the day of week. A
* negative number will count days in the past.
*
* #return The absolute (positive) number of days including weekends.
*/
public long getAllDays(int dayOfWeek, long businessDays) {
long result = 0;
if (businessDays != 0) {
boolean isStartOnWorkday = dayOfWeek < 6;
long absBusinessDays = Math.abs(businessDays);
if (isStartOnWorkday) {
// if negative businessDays: count backwards by shifting weekday
int shiftedWorkday = businessDays > 0 ? dayOfWeek : 6 - dayOfWeek;
result = absBusinessDays + (absBusinessDays + shiftedWorkday - 1) / 5 * 2;
} else { // start on weekend
// if negative businessDays: count backwards by shifting weekday
int shiftedWeekend = businessDays > 0 ? dayOfWeek : 13 - dayOfWeek;
result = absBusinessDays + (absBusinessDays - 1) / 5 * 2 + (7 - shiftedWeekend);
}
}
return result;
}
Usage Example:
LocalDate startDate = LocalDate.of(2015, 11, 26);
int businessDays = 2;
LocalDate endDate = startDate.plusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));
System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
+ " business days: " + endDate);
businessDays = -6;
endDate = startDate.minusDays(getAllDays(startDate.getDayOfWeek().getValue(), businessDays));
System.out.println(startDate + (businessDays > 0 ? " plus " : " minus ") + Math.abs(businessDays)
+ " business days: " + endDate);
Example Output:
2015-11-26 plus 2 business days: 2015-11-30
2015-11-26 minus 6 business days: 2015-11-18
Here is a version which supports both positive and negative number of days and exposes the operation as a TemporalAdjuster. That allows you to write:
LocalDate datePlus2WorkingDays = date.with(addWorkingDays(2));
Code:
/**
* Returns the working day adjuster, which adjusts the date to the n-th following
* working day (i.e. excluding Saturdays and Sundays).
* <p>
* If the argument is 0, the same date is returned if it is a working day otherwise the
* next working day is returned.
*
* #param workingDays the number of working days to add to the date, may be negative
*
* #return the working day adjuster, not null
*/
public static TemporalAdjuster addWorkingDays(long workingDays) {
return TemporalAdjusters.ofDateAdjuster(d -> addWorkingDays(d, workingDays));
}
private static LocalDate addWorkingDays(LocalDate startingDate, long workingDays) {
if (workingDays == 0) return nextOrSameWorkingDay(startingDate);
LocalDate result = startingDate;
int step = Long.signum(workingDays); //are we going forward or backward?
for (long i = 0; i < Math.abs(workingDays); i++) {
result = nextWorkingDay(result, step);
}
return result;
}
private static LocalDate nextOrSameWorkingDay(LocalDate date) {
return isWeekEnd(date) ? nextWorkingDay(date, 1) : date;
}
private static LocalDate nextWorkingDay(LocalDate date, int step) {
do {
date = date.plusDays(step);
} while (isWeekEnd(date));
return date;
}
private static boolean isWeekEnd(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return dow == SATURDAY || dow == SUNDAY;
}
Determining business days is fundamentally a question of looping over dates, checking if each is a weekend or holiday.
The Strata project from OpenGamma (I am a committer) has an implementation of a holiday calendar. The API covers the case of finding the date 2 business days later. The implementation has an optimized bitmap design that performs better than day by day looping. It may be of interest here.
This is a way to add business days using java.time Classes, some functional interfaces & lambda...
IntFunction<TemporalAdjuster> addBusinessDays = days -> TemporalAdjusters.ofDateAdjuster(
date -> {
LocalDate baseDate =
days > 0 ? date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
: days < 0 ? date.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)) : date;
int businessDays = days + Math.min(Math.max(baseDate.until(date).getDays(), -4), 4);
return baseDate.plusWeeks(businessDays / 5).plusDays(businessDays % 5);
});
LocalDate.of(2018, 1, 5).with(addBusinessDays.apply(2));
//Friday Jan 5, 2018 -> Tuesday Jan 9, 2018
LocalDate.of(2018, 1, 6).with(addBusinessDays.apply(15));
//Saturday Jan 6, 2018 -> Friday Jan 26, 2018
LocalDate.of(2018, 1, 7).with(addBusinessDays.apply(-10));
//Sunday Jan 7, 2018 -> Monday Dec 25, 2017
Supports negative values and from any week day!
This is a method which is adding or subtracting workdays to a given calendar object:
/**
* This method adds workdays (MONDAY - FRIDAY) to a given calendar object.
* If the number of days is negative than this method subtracts the working
* days from the calendar object.
*
*
* #param cal
* #param days
* #return new calendar instance
*/
public static Calendar addWorkDays(final Calendar baseDate, final int days) {
Calendar resultDate = null;
Calendar workCal = Calendar.getInstance();
workCal.setTime(baseDate.getTime());
int currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
// test if SATURDAY ?
if (currentWorkDay == Calendar.SATURDAY) {
// move to next FRIDAY
workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -1 : +2));
currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
}
// test if SUNDAY ?
if (currentWorkDay == Calendar.SUNDAY) {
// move to next FRIDAY
workCal.add(Calendar.DAY_OF_MONTH, (days < 0 ? -2 : +1));
currentWorkDay = workCal.get(Calendar.DAY_OF_WEEK);
}
// test if we are in a working week (should be so!)
if (currentWorkDay >= Calendar.MONDAY && currentWorkDay <= Calendar.FRIDAY) {
boolean inCurrentWeek = false;
if (days > 0)
inCurrentWeek = (currentWorkDay + days < 7);
else
inCurrentWeek = (currentWorkDay + days > 1);
if (inCurrentWeek) {
workCal.add(Calendar.DAY_OF_MONTH, days);
resultDate = workCal;
} else {
int totalDays = 0;
int daysInCurrentWeek = 0;
// fill up current week.
if (days > 0) {
daysInCurrentWeek = Calendar.SATURDAY - currentWorkDay;
totalDays = daysInCurrentWeek + 2;
} else {
daysInCurrentWeek = -(currentWorkDay - Calendar.SUNDAY);
totalDays = daysInCurrentWeek - 2;
}
int restTotalDays = days - daysInCurrentWeek;
// next working week... add 2 days for each week.
int x = restTotalDays / 5;
totalDays += restTotalDays + (x * 2);
workCal.add(Calendar.DAY_OF_MONTH, totalDays);
resultDate = workCal;
}
}
return resultDate;
}
Example:
Calculate the total number of days from the date I started working except Saturday and Sunday.
public class App {
public static void main(String[] args) throws Exception {
/** I write the code when 2019-8-15 */
LocalDate now = LocalDate.now();
LocalDate startWork = LocalDate.parse("2019-06-17");
/** get all days */
long allDays = Duration.between(startWork.atStartOfDay(), now.atStartOfDay()).toDays() + 1;
System.out.println("This is the " + allDays + "th day you enter the company.");
/** variable to store day except sunday and saturday */
long workDays = allDays;
for (int i = 0; i < allDays; i++) {
if (startWork.getDayOfWeek() == DayOfWeek.SATURDAY || startWork.getDayOfWeek() == DayOfWeek.SUNDAY) {
workDays--;
}
startWork = startWork.plusDays(1);
}
System.out.println("You actually work for a total of " + workDays + " days.");
}
}
/**
This is the 60th day you enter the company.
You actually work for a total of 44 days.
*/
Hope that can help you.
I am doing a compound if statement that calculates whether a certain date is a daylight savings date, but I have become stuck when trying to find a way to calculate if the date is before the first Sunday of November or if it is after. Could someone help me?
Here is my code right now:
public class Lab5 {
/**
* Return true if the given date/time is daylight savings.
* Daylight savings time begins 2am the second Sunday of March and ends 2am the first Sunday of November.
*
* #param month - represents the month with 1 = January, 12 = December
* #param date - represents the day of the month, between 1 and 31
* #param day - represents the day of the week with 1 = Sunday, 7 = Saturday
* #param hour - represents the hour of the day with 0 = midnight, 12 = noon
*
* Precondition: the month is between 1 and 12, the date is between 1 and 31, the day is between 1 and 7
* and the hour is between 0 and 23.
*/
public static boolean isDayLightSavings (int month, int date, int day, int hour) {
if (month == 1 || month == 2 || month == 12)
return false;
else if (month == 11) {
if (day == 1 && hour < 2 && date < 8)
return true;
else
return false;
}
else
return true;
}
}
Edit: I believe that my question wasn't clear enough. I know how to find the first Sunday of November
else if (month == 11) {
if (day == 1 && hour < 2 && date < 8)
what I seem to be unable to do is find whether my date is before or after this first Sunday of November. And I need to do it using an if statement, not preloaded libraries, methods or classes.
Please see my inline comments
public class Lab5 {
/**
* Return true if the given date/time is daylight savings.
* Daylight savings time begins 2am the second Sunday of March and ends 2am the first Sunday of November.
*
* #param month - represents the month with 1 = January, 12 = December
* #param date - represents the day of the month, between 1 and 31
* #param day - represents the day of the week with 1 = Sunday, 7 = Saturday
* #param hour - represents the hour of the day with 0 = midnight, 12 = noon
*
* Precondition: the month is between 1 and 12, the date is between 1 and 31, the day is between 1 and 7
* and the hour is between 0 and 23.
*/
public static boolean isDayLightSavings (int month, int date, int day, int hour) {
if (month == 1 || month == 2 || month == 12)
return false;
else if (month == 11) {
if (date > 7) // after 7th, it would be second week'day' of the month
return false;
else if ((date - day) >= 0) {
// As we ruled out all dates above 7, we would get only 1-7
// here. Now, lets take 3rd as Monday, date == 3, day == 2
// so we know that 2 is Sunday. that means if date - day is positive or zero,
// Sunday is already past. one border case is, when Sunday falls on the date entered and we
// we need to consider the first 2 hours of the day
if((day == 1) && (hours < 2))
return true;
else
return false;
}
else {
// we will come here if date - day is less than zero
// example, 3rd came on Thursday, 3 - 5 = -2 and so
// first sunday is not yet past
return true;
}
}
else
return true;
}
}
Instead of re-inventing the wheel, I would suggest you to use joda time, which takes daylight savings time (DST) into account.
http://joda-time.sourceforge.net/faq.html
I ran into a simple problem, which I solved ( I didn't give up ). However, I think that there is some more neat and tricky solution.
The problem is as follow : Return the date of the last X day before today. For example If today is Tuesday, July 09, 2013 and I want the last Friday the answer would be Friday, July 05, 2013.
My solution is as follow :
public Date dateOfLast(int day) {
int today = calendar.get(Calendar.DAY_OF_WEEK);
int daysDifferences = today - day;
int daysToSubtract;
if (day < today) {
//last day seems to be in current week !
//for example Fr > Tu.
daysToSubtract = -(Math.abs(daysDifferences));
} else {
//7- ( difference between days )!
//last day seems to be in the previous,thus we subtract the the days differences from 7
// and subtract the result from days of month.
daysToSubtract = -(7 - Math.abs(daysDifferences));
}
//subtract from days of month.
calendar.add(Calendar.DAY_OF_MONTH, daysToSubtract);
return calendar.getTime();
}
Anyone give me a mathematical formula or a simpler solution, if any ?
int daysToSubtract = ((today - day) + 7) % 7;
should be OK, if I'm not mistaken.
For example
today = 4
day = 2
daysToSubtract = ((4 - 2) + 7) % 7 = 2 : correct
today = 2
day = 4
daysToSubtract = ((2 - 4) + 7) % 7 = 5 : correct
Your solution looks good to me. But a tip: you shouldn't need to use Math.abs here, you should know which of your variables, today or day, is bigger in each branch of your if-statement:
if (day < today)
daysToSubtract = day - today; // 'today' is bigger
else
daysToSubtract = day - today - 7; // 'day' is bigger
or just
int daysToSubtract = day - today - ((day < today) ? 0 : 7);
Notice that we don't need the daysDifferences variable anymore.