-3

Good day to all! I badly needed your help on this. I have a requirement wherein I have to calculate the estimated start/end time of tasks. Given the figures in the image below, how would I do this in java? Please note that I am new to Java, so please don't yell at me on this :(

This is the desired Output I want :)

**This is what I've done so far based on master MadProgrammer**.

LocalTime openingTime = LocalTime.of(8, 0);
LocalTime closingTime = LocalTime.of(18, 0);
LocalDateTime jobStartAt = LocalDateTime.of(2012, 7, 31, 8, 0);
LocalDateTime closingAt = LocalDateTime.of(2012, 7, 31, 18, 0);
long minutesDuration = (long) 12*60;

System.out.println("start is " + printDateTime(jobStartAt));
jobStartAt = GetSpilledTime(openingTime, closingTime, closingAt, jobStartAt, minutesDuration);
System.out.println("end is " + printDateTime(jobStartAt)); 

private static LocalDateTime GetSpilledTime(LocalTime openingTime,
        LocalTime closingTime, LocalDateTime closingAt,
        LocalDateTime jobStartAt, long minutesDuration) {

    LocalDateTime estimatedEndTime = jobStartAt
            .plusMinutes(minutesDuration);

    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));

    } else {
        // Job will start on/at jobStartAt
        // and will end on/at estimatedEndTime
    }

    jobStartAt = estimatedEndTime;

    return jobStartAt;
}
IMDUMB
  • 43
  • 1
  • 8
  • Look at the `Date` class... and maybe some `SimpleDataFormat` voodoo – 3kings Apr 22 '16 at 01:32
  • So, given the start time, you need to add a number of hours to it? Something like [`LocalDateTime#plusHours`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html#plusHours-long-)? Then you need to determine if the "end time" is past the business end time, take the [difference between the two](http://stackoverflow.com/questions/32961391/java-finding-difference-between-times/32961667#32961667) and add it to the start time of the next day ... easy – MadProgrammer Apr 22 '16 at 02:28
  • Thanks 3kings and MadProgrammer! btw, I'm new in programming that's why i really need feedback from this site. – IMDUMB Apr 22 '16 at 04:40

1 Answers1

1

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.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Wow! this saves me from scratching my head and gives me the idea how to begin. Thank you so much MadProgrammer! you really saved my day! More power to you and God bless! Thanks also admin. – IMDUMB Apr 22 '16 at 04:43
  • Hi MadProgrammer, 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? :) – IMDUMB Apr 22 '16 at 07:35
  • Conceptually the idea is still the same, except now you need to split over that 2 hour period. The good news is, you don't need to increment the day. I might consider writing a method (or two) which can be used to better determine if you have reached this limit and split the value accordingly. The problem is, generally, you need to be able to return multiple values, for example, you need to be able to return (at least) the end date/time and the remaining duration – MadProgrammer Apr 22 '16 at 07:51
  • Hi MadProgrammer, I posted the code you've created. I think it works great. my problem now is they want it to accept multiple entries of business hours. I'm stuck. Please help me. – IMDUMB Apr 22 '16 at 11:14