/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.wicket.util.time; import org.apache.wicket.util.lang.Objects; /** * Immutable class which represents an interval of time with a beginning and an end. The beginning * value is inclusive and the end value is exclusive. In other words, the time frame of 1pm to 2pm * includes 1pm, but not 2pm. 1:59:59 is the last value in the <code>TimeFrame</code>. * <p> * <code>TimeFrame</code>s can be constructed by calling the <code>valueOf</code> static factory * methods <code>valueOf(Time, Time)</code> (yielding a <code>TimeFrame</code> between two absolute * times) and <code>valueOf(Time, Duration)</code> yielding a <code>TimeFrame</code> starting at an * absolute time and having a given length. * <p> * The start and end of a <code>TimeFrame</code> can be retrieved by calling <code>getStart</code> * and <code>getEnd</code>. Its duration can be retrieved by calling <code>getDuration</code>. * <p> * The <code>contains(Time)</code> method can be called to determine if a <code>TimeFrame</code> * contains a given point in time. The <code>overlaps(TimeFrame)</code> method can be called to * determine if two <code>TimeFrames</code> overlap. * <p> * The <code>eachDay(TimeOfDay, TimeOfDay)</code> will return a <code>TimeFrameSource</code> which * generates a <code>TimeFrame</code> using the two times of day. In other words, if the start is * 3pm and the end is 4pm, the <code>TimeFrameSource</code> returned will yield 3-4pm on the day it * is called (each day). * * @author Jonathan Locke * @since 1.2.6 */ public final class TimeFrame implements ITimeFrameSource { private static final long serialVersionUID = 1L; /** end of this <code>TimeFrame</code> */ private final Time end; /** beginning of this <code>TimeFrame</code> */ private final Time start; /** * Creates an <code>ITimeFrameSource</code> source for start and end <code>TimeOfDay</code>s. * For example, called with 3pm and 5pm as parameters, the <code>TimeFrame</code> source * returned would produce <code>TimeFrame</code> objects representing 3pm-5pm on whatever day it * is when the caller calls the <code>TimeFrameSource</code> interface. * * @param startTimeOfDay * the start <code>TimeOfDay</code> for this <code>TimeFrame</code> each day * @param endTimeOfDay * the end <code>TimeOfDay</code> for this <code>TimeFrame</code> each day * @return a <code>TimeFrameSource</code> which will return the specified <code>TimeFrame</code> * each day */ public static ITimeFrameSource eachDay(final TimeOfDay startTimeOfDay, final TimeOfDay endTimeOfDay) { check(startTimeOfDay, endTimeOfDay); return new ITimeFrameSource() { private static final long serialVersionUID = 1L; @Override public TimeFrame getTimeFrame() { return new TimeFrame(Time.valueOf(startTimeOfDay), Time.valueOf(endTimeOfDay)); } }; } /** * Creates a <code>TimeFrame</code> for a start <code>Time</code> and <code>Duration</code>. * * @param start * the start <code>Time</code> * @param duration * the <code>Duration</code> * @return the <code>TimeFrame</code> * @throws IllegalArgumentException * thrown if start <code>Time</code> value is before end <code>Time</code> value */ public static TimeFrame valueOf(final Time start, final Duration duration) { return new TimeFrame(start, start.add(duration)); } /** * Creates a <code>TimeFrame</code> for given start and end <code>Time</code>s. * * @param start * the start <code>Time</code> * @param end * the end <code>Time</code> * @return the <code>TimeFrame</code> * @throws IllegalArgumentException * thrown if start <code>Time</code> value is before end <code>Time</code> value */ public static TimeFrame valueOf(final Time start, final Time end) { return new TimeFrame(start, end); } /** * Checks consistency of start and end <code>AbstractTimeValue</code> values, ensuring that the * end value is less than the start value. * * @param start * start <code>AbstractTimeValue</code> value * @param end * end <code>AbstractTimeValue</code> value * @throws IllegalArgumentException * thrown if end is less than start */ private static void check(final AbstractTimeValue start, final AbstractTimeValue end) { // Throw illegal argument exception if end is less than start if (end.lessThan(start)) { throw new IllegalArgumentException("Start time of time frame " + start + " was after end time " + end); } } /** * Private constructor to force use of static factory methods. * * @param start * the start <code>Time</code> * @param end * the end <code>Time</code> * @throws IllegalArgumentException * thrown if start <code>Time</code> value is before end <code>Time</code> value */ private TimeFrame(final Time start, final Time end) { check(start, end); this.start = start; this.end = end; } /** * Determines if this <code>TimeFrame</code> contains a given point in time. * * @param time * the <code>Time</code> to check * @return <code>true</code> if this <code>TimeFrame</code> contains the given time */ public boolean contains(final Time time) { return (start.equals(time) || start.before(time)) && end.after(time); } /** * Retrieves the <code>Duration</code> of this <code>TimeFrame</code>. * * @return the <code>Duration</code> of this <code>TimeFrame</code> */ public Duration getDuration() { return end.subtract(start); } /** * Retrieves the end <code>Time</code> of this <code>TimeFrame</code>. * * @return the end of this <code>TimeFrame</code> */ public Time getEnd() { return end; } /** * Retrieves the start <code>Time</code> of this <code>TimeFrame</code>. * * @return the start of this <code>TimeFrame</code> */ public Time getStart() { return start; } /** * Implementation of <code>ITimeFrameSource</code> that simply returns this * <code>TimeFrame</code>. * * @return this <code>TimeFrame</code> */ @Override public TimeFrame getTimeFrame() { return this; } /** * Determines if two <code>TimeFrame</code>s overlap. * * @param timeframe * the <code>TimeFrame</code> to test * @return <code>true</code> if the given <code>TimeFrame</code> overlaps this one */ public boolean overlaps(final TimeFrame timeframe) { return contains(timeframe.start) || contains(timeframe.end) || timeframe.contains(start) || timeframe.contains(end); } @Override public int hashCode() { return Objects.hashCode(start, end); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TimeFrame other = (TimeFrame)obj; return Objects.equal(start, other.start) && Objects.equal(end, other.end); } /** * Converts this <code>TimeFrame</code> to a <code>String</code> representation. * * @return a <code>String</code> representation of this object */ @Override public String toString() { return "[start=" + start + ", end=" + end + "]"; } }