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.
Related
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
Knowing the year, week of year and day of week is it possible to obtain the month of year and the day of month. For example
// corresponding to September 15, 2012 if week starts on Monday
int weekNum = 38;
int dayNum = 6;
int year = 2012;
// set the calendar instance the a week of year and day in the future
Calendar aGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
aGMTCalendar.setFirstDayOfWeek(Calendar.MONDAY);
aGMTCalendar.set(Calendar.WEEK_OF_YEAR,weekNum );
aGMTCalendar.set(Calendar.DAY_OF_WEEK,dayNum );
aGMTCalendar.set(Calendar.YEAR,year);
// get the month and day of month
int monthGMT = aGMTCalendar.get(Calendar.MONTH + 1); // returns 38 not 9
int dayOfMonthNumGMT = aGMTCalendar.get(Calendar.DAY_OF_MONTH);
// returns 14 but I wanted 15
Thank you
This should be
// +1 to the value of month returned, not to the value of MONTH constant.
int monthGMT = aGMTCalendar.get(Calendar.MONTH) + 1;
The way you obtain the monthGMT has a type. It should be:
int monthGMT = aGMTCalendar.get(Calendar.MONTH) + 1;
Put the line below after each aGMTCalendar.set() call and you will see that after calling the dayNum one, the date changes from 15 to 14. The aGMTCalendar.set(Calendar.DAY_OF_WEEK, dayNum) ignores the setFirstDayOfWeek, which is however considered when setting the WEEK_OF_YEAR.
System.out.println(aGMTCalendar.getTime());
// corresponding to September 15, 2012 if week starts on Monday
int weekNum = 38;
int dayNum = 6;
int year = 2012;
// set the calendar instance the a week of year and day in the future
Calendar aGMTCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
aGMTCalendar.setFirstDayOfWeek(Calendar.MONDAY);
aGMTCalendar.set(Calendar.WEEK_OF_YEAR,weekNum );
aGMTCalendar.set(Calendar.DAY_OF_WEEK,dayNum );
aGMTCalendar.set(Calendar.YEAR,year);
// get the month and day of month
int monthGMT = aGMTCalendar.get(Calendar.MONTH) + 1;
// should be 10
int dayOfMonthNumGMT = aGMTCalendar.get(Calendar.DAY_OF_MONTH) + 1;
// should be 15
Try Calendar.SATURDAY constant instead of 6 literal.
Calendar.SATURDAY is in fact 7 not 6.
I'd like to get the next Friday of even week (considerning the weeks of the year numbered from 1 to 54).
For example if today is Friday and the week is even I want the next Friday. If today is Thursday of an odd week I want the date of tomorrow.
The test input / output is the following:
14/09/2012 -> 14/09/2012
15/09/2012 -> 28/09/2012
21/09/2012 -> 28/09/2012
22/09/2012 -> 28/09/2012
29/09/2012 -> 12/10/2012
25/12/2012 -> 04/01/2013
29/12/2012 -> 04/01/2013
Calendar today = Calendar.getInstance();
int dayOfWeek = today.get(Calendar.DAY_OF_WEEK);
int daysUntilNextFriday = Calendar.FRIDAY - dayOfWeek;
if(daysUntilNextFriday < 0){
daysUntilNextFriday = daysUntilNextFriday + 7;
}
Calendar nextFriday = (Calendar)today.clone();
nextFriday.add(Calendar.DAY_OF_WEEK, daysUntilNextFriday);
if(nextFriday.get(Calendar.WEEK_OF_YEAR) % 2 == 0){
nextFriday.add(Calendar.DAY_OF_WEEK, 7);
}
System.out.println(new SimpleDateFormat("dd/MM/yyyy").format(nextFriday.getTime()));