package com.globant.katari.sample.time.domain;
import javax.persistence.Basic;
import javax.persistence.Embeddable;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
/** Class that represents a period of time in the form of start time (hh:mm)
* and duration (minutes). It's used to improve the readability and consistency
* of a time entry (instead of using a double for the duration).
*
* @author pablo.saavedra
*/
@Embeddable
public class TimePeriod {
/** The maximum starting hours.
*/
public static final int MAX_HOURS = 23;
/** The maximum starting hours.
*/
public static final int MAX_MINUTES = 59;
/** Duration of the day, in hours.
*/
public static final int DAY_DURATION = 24;
/** Duration of the hour, in minutes.
*/
public static final int HOUR_DURATION = 60;
/** The starting hour of the period, from 0 to 23.
*/
@Basic
private int startHour;
/** The starting minutes of the period, from 0 to 59.
*/
@Basic
private int startMinutes;
/** The duration (in minutes) of the period, should not extend to the
* following day.
*/
@Basic
private int duration;
/** Protected default constructor, for hibernate's sake.
*/
protected TimePeriod() {
}
/** Constructs a TimePeriod instance with the given parameters. Instances
* shouldn't be constructed directly, use the factory methods provided by the
* class instead.
*
* @param theStartingHour The starting hour for the period, from 0 to 23.
*
* @param theStartingMins The starting minutes for the period, from 0 to 59.
*
* @param theDuration The duration of the period, should be greater than 0.
*/
public TimePeriod(final int theStartingHour, final int theStartingMins,
final int theDuration) {
validatePeriod(theStartingHour, theStartingMins, theDuration);
startHour = theStartingHour;
startMinutes = theStartingMins;
duration = theDuration;
}
/** Constructs a time period with the start time given as a String.
*
* @param startingTime The starting time of the period, in hh:mm or h:mm
* format. Cannot be null.
*
* @param theDuration The duration of the period in minutes, greater than 0.
*/
public TimePeriod(final String startingTime, final int theDuration) {
Validate.notEmpty(startingTime, "The starting time cannot be empty");
if (startingTime.indexOf(':') == -1) {
throw new IllegalArgumentException(
"The starting time is not in hh:mm format");
}
String[] hoursAndMinutes = startingTime.split(":");
int hours = Integer.parseInt(hoursAndMinutes[0]);
int minutes = Integer.parseInt(hoursAndMinutes[1]);
validatePeriod(hours, minutes, theDuration);
startHour = hours;
startMinutes = minutes;
duration = theDuration;
}
/** Validation logic for the period elements.
*
* @param theStartHour The starting hour.
*
* @param theStartMinute The starting minutes.
*
* @param theDuration The duration of the period.
*/
private void validatePeriod(final int theStartHour, final int theStartMinute,
final int theDuration) {
if ((theStartHour < 0) || (theStartHour > MAX_HOURS)) {
throw new IllegalArgumentException("The starting hour " + theStartHour
+ " is invalid, it should be between 0 and 23");
}
if ((theStartMinute < 0) || (theStartMinute > MAX_MINUTES)) {
throw new IllegalArgumentException("The starting minutes "
+ theStartMinute + " are invalid, it should be between 0 and 59");
}
if (theDuration <= 0) {
throw new IllegalArgumentException("Duration should be greater than 0");
}
int endingMinutes = getEndingMinutes(
theStartHour, theStartMinute, theDuration);
if (endingMinutes > DAY_DURATION * HOUR_DURATION) {
throw new IllegalArgumentException(
"The period expands to the following day");
}
}
/** Utility method for calculating the ending minutes of an entry
* given its components.
*
* @param theStartHour The starting hour.
*
* @param theStartMinutes The starting minutes.
*
* @param theDuration The duration.
*
* @return The ending minutes (total) of this period.
*/
private static int getEndingMinutes(final int theStartHour,
final int theStartMinutes, final int theDuration) {
return theStartHour * HOUR_DURATION + theStartMinutes + theDuration;
}
/** The starting hour for the period.
*
* @return the start hour, from 0 to 23.
*/
public int getStartHour() {
return startHour;
}
/** The starting minutes for the period.
*
* @return the start minutes, from 0 to 59.
*/
public int getStartMinutes() {
return startMinutes;
}
/** The duration of the period.
*
* @return the duration, in minutes.
*/
public int getDuration() {
return duration;
}
/** Returns the finishing hour of this period, based on the starting hour and
* the duration.
*
* @return The finishing hour.
*/
public int getFinishHour() {
int finishMinutes = getEndingMinutes(startHour, startMinutes, duration);
return finishMinutes / HOUR_DURATION;
}
/** Returns the finishing minutes of this period, based on the starting hour
* and the duration.
*
* @return The finishing hour.
*/
public int getFinishMinutes() {
int finishMinutes = getEndingMinutes(startHour, startMinutes, duration);
return finishMinutes % HOUR_DURATION;
}
/** Returns a string representation of this time period.
*
* @return A human readable string.
*/
@Override
public String toString() {
return "Time period, from " + startHour + ":" + padWithZeros(startMinutes)
+ " to " + padWithZeros(getFinishHour()) + ":"
+ padWithZeros(getFinishMinutes());
}
/** Completes an integer with a leading 0 if it's only one digit long.
*
* @param value The integer to pad.
*
* @return The padded integer.
*/
private static String padWithZeros(final int value) {
return StringUtils.leftPad(Integer.toString(value), 2, '0');
}
}