Rakesh KR
Rakesh KR

Reputation: 6527

Time completes full day check in Java

I have a List containing time info as follows,

List<String> dayList = new LinkedList<String>();
dayList.add("00:00-23:59");

I need to find out the list satisfies the full day or not.

And what i tried is,

List<String> dayList = new LinkedList<String>();
dayList.add("00:00-12:59");
dayList.add("13:00-20:30");
dayList.add("20:31-23:59");
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
long totalMinutes = 0;

for(String data : dayList){
    Date startDate = sdf.parse(data.split("-")[0]);
    Date endDate   = sdf.parse(data.split("-")[1]);
    totalMinutes += TimeUnit.MILLISECONDS.toMinutes(endDate.getTime()-startDate.getTime());
}

if(totalMinutes==(1439-(dayList.size()-1))){
     System.out.println("Completes Full Day");
} else{
     System.out.println("Not Completes Full Day");
}

NB : The List item can be overlap each other.

This logic fails if list contains

Can any one suggest any other logic ?

Upvotes: 2

Views: 425

Answers (5)

Alexander
Alexander

Reputation: 1421

I would suggest the following approach. It uses LocalTime to simplify the time operations and a Pattern for parsing the input.

  1. Parsing the Input using a regular expression creating LocalTimes. Store them in a Map using start as key and end as value.
  2. Sort the keys of the Map.
  3. Check the interval boundaries using LocalTime operations. Do not forget start end end of day.

Main method:

public static void main(String[] args) {
    List<String> dayList = new LinkedList<String>();
    dayList.add("00:00-12:59");
    dayList.add("13:00-20:30");
    dayList.add("18:31-23:59");

    Pattern pattern = Pattern.compile("([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2})");

    DayCoverage dayCoverage = new DayCoverage();
    for (String day : dayList) {
        Matcher matcher = pattern.matcher(day);
        if (!matcher.matches()) {
            System.err.println("Invalid day entry: " + day);
            return;
        }
        LocalTime start = LocalTime.parse(matcher.group(1));
        LocalTime end = LocalTime.parse(matcher.group(2));

        dayCoverage.addIntervall(start, end);
    }

    if (dayCoverage.isComplete()) {
        System.out.println("Completes Full Day");
    } else {
        System.out.println("Not Completes Full Day");
    }
}

Class DayCoverage:

static class DayCoverage {

    private Map<LocalTime, LocalTime> cover = new HashMap<>();

    public void addIntervall(LocalTime start, LocalTime end) {
        if(end.isBefore(start)){
            this.cover.put(end, start); 
        } else {
            this.cover.put(start, end);
        }
    }

    public boolean isComplete() {
        if(this.cover.isEmpty()){
            System.err.println("Coverage empty.");
            return false;
        }

        Set<LocalTime> startTimes = this.cover.keySet();
        List<LocalTime> sortedStartTimes = new ArrayList<>(startTimes);
        Collections.sort(sortedStartTimes);

        LocalTime first = sortedStartTimes.get(0);
        if(! LocalTime.MIN.equals(first)){
            System.err.println("Coverage does not start with 00:00.");
            return false;
        }

        LocalTime lastEnd= LocalTime.MIN;
        for (LocalTime start : sortedStartTimes) {
            if(lastEnd.plus(1, ChronoUnit.MINUTES).isBefore(start)){
                System.err.println("Missing coverage between: " + lastEnd + " and " + start);
                return false;
            }
            lastEnd = this.cover.get(start);
        }

        if(LocalTime.MAX.truncatedTo(ChronoUnit.MINUTES).isAfter(lastEnd)){
            System.err.println("Missing coverage between: " + lastEnd + " and 23:59");
            return false;
        }

        return true;
    }

}

Upvotes: 2

Anton Kislitsyn
Anton Kislitsyn

Reputation: 9

Little change in VGR version:

    List<String> dayList = new LinkedList<>();
    dayList.add("11:00-09:59");
    dayList.add("09:00-10:30");
    dayList.add("10:10-10:59");

    int minutesPerDay = 24 * 60;

    BitSet uncoveredMinutes = new BitSet(minutesPerDay);
    uncoveredMinutes.set(0, minutesPerDay);

    DateTimeFormatter sdf = DateTimeFormatter.ofPattern("HH:mm");

    for (String dayItem : dayList) {
        String[] data = dayItem.split("-");
        LocalDateTime startDate = LocalDateTime.of(LocalDate.now(),LocalTime.parse(data[0], sdf));
        LocalDateTime endDate = LocalDateTime.of(LocalDate.now(),LocalTime.parse(data[1], sdf));

        if (endDate.isBefore(startDate)){
            endDate = endDate.plusMinutes(Duration.ofDays(1).toMinutes());
        }

        LocalDateTime terminateDate = endDate.plusMinutes(1);
        while(startDate.isBefore(terminateDate)) {
            int hours = startDate.getHour();
            int minutes = startDate.getMinute();
            int start = hours * 60 + minutes;
            uncoveredMinutes.clear(start);
            startDate = startDate.plusMinutes(1);
        }
    }

    System.out.println(uncoveredMinutes);
    if (uncoveredMinutes.isEmpty()) {
        System.out.println("Completes full day");
    } else{
        System.out.println("Does not complete full day");
    }

Upvotes: -1

VGR
VGR

Reputation: 44385

There aren’t a lot of minutes in a day (in computing terms), so I would just use a BitSet to keep a flag for every single minute in the day:

int minutesPerDay = 24 * 60;

BitSet uncoveredMinutes = new BitSet(minutesPerDay);
uncoveredMinutes.set(0, minutesPerDay);

for (String dayItem : dayList) {
    String[] parts = dayItem.split("-");

    String[] hoursAndMinutes = parts[0].split(":");
    int hours = Integer.parseInt(hoursAndMinutes[0]);
    int minutes = Integer.parseInt(hoursAndMinutes[1]);
    int start = hours * 60 + minutes;

    hoursAndMinutes = parts[1].split(":");
    hours = Integer.parseInt(hoursAndMinutes[0]);
    minutes = Integer.parseInt(hoursAndMinutes[1]);
    int end = hours * 60 + minutes;

    uncoveredMinutes.clear(start, end + 1);
}

if (uncoveredMinutes.isEmpty()) {
    System.out.println("Completes full day");
} else{
    System.out.println("Does not complete full day");
}

Note that BitSet.clear expects the second argument to be an exclusive bound (just like String.substring and List.subList); that is the reason for passing end + 1.

Upvotes: 1

Danikov
Danikov

Reputation: 725

The problem is not 100% clear, but I'm assuming that the smallest time unit of consequence are minutes, that is that "00:00-23:59" covers the whole day.

This logic is not fine: you're only measuring the durations and ensuring they sum up to the correct total. This means that if you add "00:00-11:59" twice, it will sum up to the whole day, but only cover the morning. If there are overlaps, as clarified, it will also fail as they can quite validly sum up to more than a full day due to the overlaps.

The first thing you want to do is transform the list to remove overlaps. First, sort your timespans by start time. Then, for each timespan, if any of the subsequent timespans have a start time within its range, you can combine them into a single timespan.

This should give you a list of non-overlapping timespans. With the overlaps eliminated, summing the total duration and comparing against the 1439 for a full day would be valid.

Upvotes: 0

Piotr Wilkin
Piotr Wilkin

Reputation: 3501

Nope, the logic is not fine. Try adding 100 entries of the form "00:00-00:50". Obviously, they do not fullfill the entire day, yet they satisfy your algorithm.

To actually implement this, you need something that's called an Interval Tree (https://en.wikipedia.org/wiki/Interval_tree). One such sample implementation that I have used is present in Guava. If you want to use it, you will need to add the dates as Ranges and then ask the RangeSet whether it contains the full day (using encloses()):

https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/RangeSet.html

Upvotes: 2

Related Questions