Am very beginner and new to Java platform. I have the below 3 simple Java date difference calculation functions. I wanted to exclude weekends on the below calculations in all the 3 methods. Can anyone please help how to exclude weekends for the below dateDiff calculations?
public static String getDatesDiff(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
timeDiff = ""+diff;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDays(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = ""+(diff / (24 * 60 * 60 * 1000));
timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
timeDiff = timeDiff+" days";
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
public static String getDatesDiffAsDate(String date1, String date2) {
String timeDiff = "";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d1 = null;
Date d2 = null;
try {
d1 = format.parse(date1);
d2 = format.parse(date2);
long diff = d2.getTime() - d1.getTime();
String days = (diff / (24 * 60 * 60 * 1000))+" days";
String hours = (diff / (60 * 60 * 1000) % 24)+"h";
String minutes = (diff / 1000 % 60)+"mts";
String seconds = (diff / (60 * 1000) % 60)+"sec";
timeDiff = days;
timeDiff = timeDiff.replaceAll("-", "");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return timeDiff;
}
This code is fundamentally broken. java.util.Date doesn't represent a date, it represents a timestamp. But if you're working with moments in time, you have a problem: not all days are exactly 24 hours long. For example, daylight savings exists, making some days 25 or 23 hours. At specific moments in time in specific places on the planet, entire days were skipped, such as when a place switches which side of the international date line it is on, or when Russia was the last to switch from Julian to Gregorian (the famed October Revolution? Yeah, that happened in November actually!)
Use LocalDate which represents an actual date, not a timestamp. Do not use Date, or SimpleDateFormat – these are outdated and mostly broken takes on dates and times. The java.time package is properly thought through.
When is 'the weekend'? In some places, Friday and Saturday are considered the weekend, not Saturday and Sunday.
If you're excluding weekends, presumably you'd also want to exclude mandated holidays. Many countries state that Jan 1st, regardless of what day that is, counts as a Sunday, e.g. for the purposes of government buildings and services being open or not.
Lessons you need to take away from this:
java.util.Date, Calendar, GregorianCalendar, or SimpleDateFormat, ever. Use the stuff in java.time instead.ChronoUnit.DAYS.between(date1, date2) does all that math for you.throws X if you don't want to deal with it right now, or, put throw new RuntimeException("unhandled", e); in your catch blocks. Not this, this is horrible. It logs half of the error and does blindly keeps going, with invalid state.rzwitserloot has already brought up many valid points about problems in your code.
This is an example of how you could count the working days:
LocalDate startDate = ...;
LocalDate endDateExclusive = ...;
long days = startDate.datesUntil(endDateExclusive)
.filter(date -> isWorkingDay(date))
.count();
And, of course, you need to implement the isWorkingDay method. An example would be this:
public static boolean isWorkingDay(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY);
}
I used LocalDate to illustrate the example. LocalDate fits well if you are working with concepts like weekend days and holidays. However, if you want to also include the time component, then you should also take clock adjustments like DST into account; otherwise a "difference" does not make sense.
I assume the user to input an object representing some datetime value, not a String. The parsing of a string does not belong to this method, but should be handled elsewhere.
Already been said, but I repeat: don't use Date, Calendar and SimpleDateFormat. They're troublesome. Here are some reasons why.
If you want to take the time into consideration, it'll get a little more complex. For instance, ChronoUnit.DAYS.between(date1, date2) only supports a single, contiguous timespan. Gaps in the timespan, like excluding certain periods of time, is not. Then you have to walk over each date and get the associated duration of that portion of date.
First, we could create a LocalTimeRange class, which represents a time span at a certain day.
public record LocalTimeRange(LocalTime start, LocalTime endExclusive) {
public static final LocalTimeRange EMPTY = new LocalTimeRange(null, null);
public Duration toDuration(LocalDate date, ZoneId zone) {
if (this.equals(EMPTY)) {
return Duration.ZERO;
}
var s = ZonedDateTime.of(date, Objects.requireNonNullElse(start, LocalTime.MIN), zone);
var e = (endExclusive != null ? ZonedDateTime.of(date, endExclusive, zone) : ZonedDateTime.of(date.plusDays(1), LocalTime.MIN, zone));
return Duration.between(s, e);
}
}
Calculations are not done immediately, because the duration in between the two wall clock times, depends on the date and timezone. The toDuration method calculates this.
Then we'll create a method which defines what times on each day are counted as a non-weekend day. In this example, I have defined a weekend to be from Friday, 12:00 (noon) until Sunday, 23:59 (midnight).
private static Duration nonWeekendHours(LocalDate date, ZoneId zone) {
var result = switch (date.getDayOfWeek()) {
case MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY -> new LocalTimeRange(LocalTime.MIDNIGHT, null);
case FRIDAY -> new LocalTimeRange(LocalTime.MIDNIGHT, LocalTime.NOON);
case SATURDAY,
SUNDAY -> new LocalTimeRange(null, null);
};
return result.toDuration(date, zone);
}
The LocalTimeRange::toDuration method is called with the passed LocalDate and ZoneId arguments.
Note that passing null as LocalTimeRange's second argument means 'until the end of the day'.
At last we could stream over all dates of a certain period and calculate how much time are the non-weekend hours for each day, and then reduce them to get the total amount of time:
LocalDate startDate = ...;
LocalDate endDate = ...;
ZoneId zone = ...;
Duration result = startDate.datesUntil(endDate)
.map(date -> nonWeekendHours(date, zone))
.reduce(Duration.ZERO, Duration::plus);
With the retrieved Duration instance, you can easily get the time parts with the get<unit>Part() methods,
Online demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With