Java Design – Best Way to Model a Timeslot or Period for a Schedule

designjavaobject-oriented-design

I am making a sport events scheduler to calculate possible schedules out of initial data and configurations. There are three main domains: players, courts and timeslots. My question regards the latter.

The initial idea was that a timeslot would be just an abstract representation of a period of time, and giving it more sense would be responsibility of whoever defined the time domain. So, for example, you could decided that the "timeslot 1" maps to a time range from 10am to 12am, or that it maps to a particular date, like 10/04/2016.

However I ended up implementing a Timeslot class with a bit more information. But I dislike it and I really want to change it. It has a lower and an upper bound that holds a time range. The unit in which these values are stored are milliseconds. Then I also store a TimeUnit just to slightly beautify the string output.

Now, I want to redefine this whole class to be both flexible and abstract (in the sense of abstract representation of a period of time, not the Java keyword).

With flexibility I mean that a period of time could potentially be any of the following:

  • An anonymous day, week, etc… For example: day 1, day 2, day 3… or week 1, week 2, week 3.
  • An anonymous range: day 1 to day 3, day 3 to day 4, day 4 to day 10, hour 1 to hour 2, hour 2 to hour 3…
  • A specific date: 10/04/2016, 11/04/2016…
  • A specific time range in an anonymous date: 10:00-11:00 day 1, 11:00-12:00 day 1, 10:00-11:00 day 2…

And so on and so forth, you name it. Also, all of this for any time unit, you can have hours, days, weeks, years… even seconds, and why not milliseconds, an extreme case though, but possible.

So what could be the best approach to tackle this problem?

Here is the class I have so far (but as I said I want to completely change the way it's thought):

package models.tournaments.events.entities;

public class Timeslot extends Entity implements Comparable<Timeslot> {
    private int lowerBound;

    private int upperBound;

    public enum TimeUnit { MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS, YEARS };

    private TimeUnit timeUnit = TimeUnit.HOURS;

    public Timeslot(String name, int lb, int ub) {
        super(name);
        lowerBound = lb;
        upperBound = ub;
    }

    public Timeslot(int lb, int ub) {
        this("Timeslot", lb, ub);
    }

    public Timeslot(int lb, int ub, TimeUnit timeUnit) {
        this(lb, ub);
        this.timeUnit = timeUnit;
    }

    public int getLowerBound() {
        return lowerBound;
    }

    public int getUpperBound() {
        return upperBound;
    }

    public TimeUnit getTimeUnit() {
        return timeUnit;
    }

    public String getLowerBoundStr() {
        return formatStringWithTimeUnit(lowerBound);
    }

    public String getUpperBoundStr() {
        return formatStringWithTimeUnit(upperBound);
    }

    public String toString() {
        return getLowerBoundStr() + " - " + getUpperBoundStr();
    }

    private String formatStringWithTimeUnit(int value) {
        switch (timeUnit) {
            case MILLISECONDS:
                break;
            case SECONDS:
                value /= 1000;
                break;
            case MINUTES:
                value /= 1000 * 60;
                break;
            case HOURS:
                value /= 1000 * 60 * 60;
                break;
            case DAYS:
                value /= 1000 * 60 * 60 * 24;
                break;
            case YEARS:
                value /= 1000 * 60 * 60 * 24 * 365;
                break;
            default:
                break;
        }
        return Integer.toString(value);
    }

    public int compareTo(Timeslot timeslot) {
        if (getLowerBound() < timeslot.getLowerBound())
            return -1;
        else if (timeslot.getLowerBound() < getLowerBound())
            return 1;
        else if (getUpperBound() < timeslot.getUpperBound())
            return -1;
        else if (timeslot.getUpperBound() < getUpperBound())
            return 1;
        return 0;
    }
}

I hope the question makes sense and that it's not too wide.

Best Answer

I believe this should cover most of your needs:

import java.time.TemporalAccessor;
import java.time.TemporalAmount;
import java.util.Optional;

public class Timeslot {
    private final Optional<TemporalAccessor> start;
    private final TemporalAmount temporalAmount;

    public Timeslot(final TemporalAccessor start, final TemporalAmount temporalAmount) {
        this.start = Optional.of(start);
        this.temporalAmount = temporalAmount;
    }

    public Timeslot(final TemporalAmount temporalAmount) {
        this.start = Optional.empty()
        this.temporalAmount = temporalAmount;
    }

    public Optional<TemporalAccessor> getStart() {
        return this.start;
    }

    public TemporalAmount getTemporalAmount() {
        return this.temporalAmount;
    }
}

For the optional start time it uses the java.time.TemporalAccessor interface which has, for example, such implementations as DayOfWeek, LocalTime and LocalDateTime. The start time is Optional because you specified that you need to support abstract timeslots as well.

The "size" of the timeslot is stored in a java.time.TemporalAmount field. The TemporalAmount interface is implemented by java.time.Duration and java.time.Period for time and date based durations, respectively.

Related Topic