Start by become familiar with the Java 8's Time APIs, the java.time framework.
you're going to be spending a bit of time here.
Next, know what you have...
- A "start date" (
01/01/2017
)
- A "start of business" time (
08:00am
)
- A "end of business" time (
06:00pm
)
- A "duration in hours" of each task
What we need to know is, when a task's expected end time exceeds the "end of business" time and by how much, this requires us to add the remaining time to the "start of business" of the next day and essentially repeat the process until the estimated end time is before the "end of business" time
Let's start with the basic "known" information...
LocalTime openingTime = LocalTime.of(8, 0);
LocalTime closingTime = LocalTime.of(18, 0);
LocalDate startAt = LocalDate.of(2017, Month.JANUARY, 1);
Then based on that information, we can take the duration of a task and calculate its estimated end date/time
LocalDateTime estimatedEndTime = jobStartAt.plusHours(task.getDurationInHours());
if (estimatedEndTime.isAfter(closingAt) || estimatedEndTime.isEqual(closingAt)) {
LocalDateTime fallOverStartAt = jobStartAt;
do {
Duration duration = Duration.between(closingAt, estimatedEndTime);
estimatedEndTime = LocalDateTime.of(fallOverStartAt.toLocalDate(), closingTime);
fallOverStartAt = fallOverStartAt.plusDays(1);
fallOverStartAt = LocalDateTime.of(fallOverStartAt.toLocalDate(), openingTime);
estimatedEndTime = fallOverStartAt.plusHours(duration.toHours());
closingAt = closingAt.plusDays(1);
} while (estimatedEndTime.isAfter(closingAt));
// Job will start on/at jobStartAt
// and will end on/at estimatedEndTime
} else {
// Job will start on/at jobStartAt
// and will end on/at estimatedEndTime
}
// The next job starts here
jobStartAt = estimatedEndTime;
Okay, so conceptually, all this does is, starting at a given point in time, adds the number of hours to it. It checks to see if the time is greater than or equal to the closing of business, if it is, it loops, adding the "overflow" from each day to the start of business the next day until the work finishes before the close of business.
You will then have the start time/date in jobStartAt
and the end time/date in estimatedEndTime
.
The estimatedEndTime
then becomes the next job's start date/time and you basically repeat until you are complete.
how about if business hours have multiple entries? Lets say I have these: 8:00AM-12:00PM and 2:00:PM-6:00PM. - How will I be able to tweak your code?
...I'd tell your employer I'm available for paid work...
Okay, let's turn this problem on it's head, essentially we know the opening time (08:00am
) and the closing time (06:00pm
) which gives us ten hours. We know that there is at least one break (12pm-2pm
) of two hours, this leaves us with a window of 8 available hours in the day.
Now, we just need an algorithm to squeeze as much work into the available time for each day, obviously allow for tasks to start at the end of the previous task...
So again, we start with initial data...
LocalTime openingTime = LocalTime.of(8, 0);
LocalTime closingTime = LocalTime.of(18, 0);
// This is a list of all the breaks, starting at to ending at
// This way, you can add more breaks
List<LocalTime[]> breaks = new ArrayList<>();
breaks.add(new LocalTime[]{LocalTime.of(12, 0), LocalTime.of(14, 0)});
LocalDate startAt = LocalDate.of(2017, Month.JANUARY, 1);
LocalDateTime startDateTime = startAt.atTime(openingTime);
Now, we need to calculate the available amount of time to complete work...
Duration wholeDay = Duration.between(openingTime, closingTime);
for (LocalTime[] hole : breaks) {
wholeDay = wholeDay.minus(Duration.between(hole[0], hole[1]));
}
Then, we need some messed up way to figure how to squeeze each job into the available time...
public LocalDateTime getDateTimeToFit(long duration, Duration wholeDay, LocalDateTime jobStartAt, LocalTime openingTime, LocalTime closingTime) {
LocalDateTime estimatedEndTime = jobStartAt;
do {
// Basically, we're going to calculate out the
// number of days and hours the job would
// require to be completed within the available
// time of a whole day...
int days = (int) (duration / wholeDay.toHours());
int hours = (int) (duration % wholeDay.toHours());
if (hours == 0) {
days--;
hours += wholeDay.toHours();
}
estimatedEndTime = jobStartAt.plusDays(days).plusHours(hours);
LocalDateTime closingAt = estimatedEndTime.toLocalDate().atTime(closingTime);
// This is how much overflow there is at the end of the
// day, if it's more than 0, we need to cycle around
// again and squeeze some more time of the next day
Duration fallOver = Duration.between(closingAt, estimatedEndTime);
duration = fallOver.toHours();
// We increment the day here simply because it's convenient
// to do so, if we exit the loop, it won't make any
// difference
jobStartAt = estimatedEndTime.plusDays(1).toLocalDate().atTime(openingTime);
} while (duration > 0);
return estimatedEndTime;
}
Based on your available durations of int[] durations = new int[]{12, 8, 15};
, this gives me the following output...
2017-01-01T08:00 - 2017-01-02T12:00
2017-01-02T12:00 - 2017-01-03T10:00
2017-01-03T10:00 - 2017-01-04T17:00
Now, I warn you, this is a "conceptual" idea, hacked out late at night when I really should have gone to bed, so you are going to need to sit down and come up with adequate test data and known results and test this properly, making tweaks as you go.