0

my request for help is kinda weird. I'll try to be short.

I have this line where i parse a schedule of Ex. 18:00-19:00 ; 22:00-23:00 The schedule 18:00 to 19:00 is one object and the other 22:00 to 23:00 is seperated by ";". Which mean you can add infinite schedules.

Now i parse this line using:

Arrays.asList(schedule.replaceAll("\\s", "").split(";")).forEach(s -> _schedules.add(new ScheduleCalendar(Integer.parseInt(s.split("-")[0].split(":")[0]), Integer.parseInt(s.split("-")[0].split(":")[1]), Integer.parseInt(s.split("-")[1].split(":")[0]), Integer.parseInt(s.split("-")[1].split(":")[1]))));

And i use this class

public static class ScheduleCalendar
    {
        private final Calendar[] _calendar = new Calendar[2];

        public ScheduleCalendar(final int startingHour, final int startingMinute, final int endingHour, final int endingMinute)
        {
            final Calendar starting = Calendar.getInstance();
            starting.set(Calendar.HOUR_OF_DAY, startingHour);
            starting.set(Calendar.MINUTE, startingMinute);
            starting.set(Calendar.SECOND, 0);
            starting.set(Calendar.MILLISECOND, 0);

            _calendar[0] = starting;

            final Calendar ending = Calendar.getInstance();
            ending.set(Calendar.HOUR_OF_DAY, endingHour);
            ending.set(Calendar.MINUTE, endingMinute);
            ending.set(Calendar.SECOND, 0);
            ending.set(Calendar.MILLISECOND, 0);

            _calendar[1] = ending;
        }

        public boolean isBefore(final ScheduleCalendar schedule)
        {
            return false;
        }

        public Calendar[] getCalendars()
        {
            return _calendar;
        }

        public boolean isNow()
        {
            return _calendar[0].getTimeInMillis() > System.currentTimeMillis() && _calendar[1].getTimeInMillis() < System.currentTimeMillis();
        }
    }

and finally store in

private final List<ScheduleCalendar> _schedules = new ArrayList<>();

Now i want to make a method which retrieve the closest schedule or active if is possible. Is there any fancy and fast way using java 8-16 to reproduce this code without having to write so long code? Any library maybe or so?

1 Answers1

0

There is no good reason to use Calendar if we are using java 8+

Suppose the time range 'from' < 'to', i.e. "23:00-01:00" is not allowed, using LocalTime with lambda function can greatly simplify the code.

The algorithm for finding closest or active:

  1. Discard outdated time ranges ('from' > active time)
  2. For those 'to' < active time, choose the one with max 'to' (closest)
  3. If no time range in (2), for those 'from' < active time < 'to', choose the one with max 'from'.

Of course you may swap the ordering of 2 and 3 depends on your priority for closest vs active.

import java.time.LocalTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class Scheduler {
    public static void main(String[] args) {
        String values = "21:00-23:00;20:00-21:00;18:00-19:00;22:30-23:00";
        LocalTime now = LocalTime.parse("22:00");
        List<LocalTimeRange> timeRanges = Arrays.stream(values.split(";")).map(s -> {
            String[] arr = s.split("-");
            return new LocalTimeRange(LocalTime.parse(arr[0]), LocalTime.parse(arr[1]));
        }).collect(Collectors.toList());
        getClosetOrActive(timeRanges, now).ifPresentOrElse(System.out::println, () -> System.out.println("No time range match"));
    }

    private static Optional<LocalTimeRange> getClosetOrActive(List<LocalTimeRange> timeRanges, LocalTime activeTime) {
        // Outdated range is discarded
        List<LocalTimeRange> notOutdatedRanges = timeRanges.stream().
                filter(timeRange -> !timeRange.getFrom().isAfter(activeTime)).collect(Collectors.toList());
        // For range before active time, retrieve the one with 'to' closest to activeTime
        Optional<LocalTimeRange> closet = notOutdatedRanges.stream().filter(localTimeRange -> localTimeRange.getTo().isBefore(activeTime))
                .sorted(Comparator.comparing(LocalTimeRange::getTo).reversed()).findFirst();
        if (closet.isPresent()) {
            return closet;
        }
        // For range cover active time, retrieve the one with latest 'from'
        return notOutdatedRanges.stream().filter(localTimeRange -> !localTimeRange.getTo().isBefore(activeTime))
                .sorted(Comparator.comparing(LocalTimeRange::getFrom).reversed()).findFirst();
    }

    public static class LocalTimeRange {
        private final LocalTime from;
        private final LocalTime to;

        public LocalTime getFrom() {
            return from;
        }

        public LocalTime getTo() {
            return to;
        }

        @Override
        public String toString() {
            return from + "-" + to;
        }

        public LocalTimeRange(LocalTime from, LocalTime to) {
            this.from = from;
            this.to = to;
        }
    }
}
samabcde
  • 6,988
  • 2
  • 25
  • 41
  • Thank you for doing all this work. I used also LocalTime. Your code tho is not working. Try using LocalTime now = LocalTime.now(); and check. My current time is 19:37 and it print out 18:00-19:00. It should print 20:00-21:00 which is the closest schedule (in 23 minutes) or any schedule that is current. – Girls are beautiful Sep 09 '21 at 16:38
  • Please include a few examples on the input and the expected output you want. As `closest` is not clearly defined in your question. – samabcde Sep 10 '21 at 05:45