You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by ad...@apache.org on 2019/05/06 13:01:09 UTC

[wicket] branch WICKET-6662 updated: Reintroduced deprecated classes from package org.apache.wicket.util.time

This is an automated email from the ASF dual-hosted git repository.

adelbene pushed a commit to branch WICKET-6662
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/WICKET-6662 by this push:
     new a0dd2df  Reintroduced deprecated classes from package org.apache.wicket.util.time
a0dd2df is described below

commit a0dd2df5fa04a4ec8727ce17e25c34b1f5a4d4cb
Author: Andrea Del Bene <an...@gmail.com>
AuthorDate: Mon May 6 13:10:30 2019 +0200

    Reintroduced deprecated classes from package org.apache.wicket.util.time
---
 .../org/apache/wicket/util/time/AbstractTime.java  | 121 ++++
 .../apache/wicket/util/time/AbstractTimeValue.java |  54 ++
 .../java/org/apache/wicket/util/time/Duration.java | 554 +++++++++++++++++++
 .../apache/wicket/util/time/ITimeFrameSource.java  |  40 ++
 .../java/org/apache/wicket/util/time/Time.java     | 614 +++++++++++++++++++++
 .../org/apache/wicket/util/time/TimeFrame.java     | 260 +++++++++
 .../java/org/apache/wicket/util/time/TimeMap.java  | 110 ++++
 .../org/apache/wicket/util/time/TimeOfDay.java     | 419 ++++++++++++++
 8 files changed, 2172 insertions(+)

diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTime.java b/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTime.java
new file mode 100755
index 0000000..01d2f42
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTime.java
@@ -0,0 +1,121 @@
+/*
+ * 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 java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Abstract base class for subclasses that represent a point in time (as opposed to a
+ * {@link Duration} of time).
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+abstract class AbstractTime extends AbstractTimeValue
+{
+	private static final long serialVersionUID = 1L;
+
+	/** calendar for the local time zone */
+	static final Calendar localtime = Calendar.getInstance();
+
+	/** time format */
+	static final SimpleDateFormat timeFormat = new SimpleDateFormat("h.mma", Locale.ENGLISH);
+
+	/**
+	 * @param milliseconds
+	 * @see AbstractTimeValue
+	 */
+	AbstractTime(final long milliseconds)
+	{
+		super(milliseconds);
+	}
+
+	/**
+	 * Returns <code>true</code> if this <code>Time</code> value is after the given
+	 * <code>Time</code> argument's value.
+	 * 
+	 * @param that
+	 *            the <code>AbstractTimeValue</code> to compare with
+	 * @return <code>true</code> if this <code>Time</code> value is after <code>that</code>
+	 *         <code>Time</code> value
+	 */
+	public final boolean after(final AbstractTimeValue that)
+	{
+		return greaterThan(that);
+	}
+
+	/**
+	 * Returns <code>true</code> if this <code>Time</code> value is before the given
+	 * <code>Time</code> argument's value.
+	 * 
+	 * @param that
+	 *            the <code>AbstractTimeValue</code> to compare with
+	 * @return <code>true</code> if this <code>Time</code> value is before <code>that</code>
+	 *         <code>Time</code> value
+	 */
+	public final boolean before(final AbstractTimeValue that)
+	{
+		return lessThan(that);
+	}
+
+	/**
+	 * Converts this <code>Time</code> to a time <code>String</code> using the formatter 'h.mma'.
+	 * 
+	 * @return the <code>Time</code> <code>String</code>
+	 */
+	public final String toTimeString()
+	{
+		return toTimeString(localtime);
+	}
+
+	/**
+	 * Converts this <code>Time</code> to a <code>Date String</code> using the <code>Date</code>
+	 * formatter 'h.mma'.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use in the conversion
+	 * @return the <code>Date</code> <code>String</code>
+	 */
+	public final String toTimeString(final Calendar calendar)
+	{
+		synchronized (timeFormat)
+		{
+			synchronized (calendar)
+			{
+				timeFormat.setCalendar(calendar);
+				return timeFormat.format(new Date(getMilliseconds())).toLowerCase(Locale.ROOT);
+			}
+		}
+	}
+
+	/**
+	 * Converts this <code>Time</code> to a <code>String</code> suitable for use in a file system
+	 * name.
+	 * 
+	 * @return this <code>Time</code> as a formatted <code>String</code>
+	 */
+	@Override
+	public String toString()
+	{
+		return toTimeString();
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTimeValue.java b/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTimeValue.java
new file mode 100755
index 0000000..d1bc813
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/AbstractTimeValue.java
@@ -0,0 +1,54 @@
+/*
+ * 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.value.LongValue;
+
+/**
+ * Package local class for representing immutable time values in milliseconds and typical operations
+ * on such values.
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+abstract class AbstractTimeValue extends LongValue
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Package local constructor for package subclasses only.
+	 * 
+	 * @param milliseconds
+	 *            the number of milliseconds in this <code>Time</code> value
+	 */
+	AbstractTimeValue(final long milliseconds)
+	{
+		super(milliseconds);
+	}
+
+	/**
+	 * Retrieves the number of milliseconds in this <code>Time</code> value.
+	 * 
+	 * @return the number of milliseconds in this <code>Time</code> value
+	 */
+	public final long getMilliseconds()
+	{
+		return value;
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/Duration.java b/wicket-util/src/main/java/org/apache/wicket/util/time/Duration.java
new file mode 100755
index 0000000..2b5345c
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/Duration.java
@@ -0,0 +1,554 @@
+/*
+ * 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 java.util.Locale;
+import java.util.Locale.Category;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.wicket.util.string.StringValue;
+import org.apache.wicket.util.string.StringValueConversionException;
+import org.apache.wicket.util.thread.ICode;
+import org.slf4j.Logger;
+
+
+/**
+ * A <code>Duration</code> is an immutable length of time stored as a number of milliseconds.
+ * Various factory and conversion methods are available for convenience.
+ * <p>
+ * These static factory methods allow easy construction of value objects using either long values
+ * like <code>seconds(2034)</code> or <code>hours(3)</code>:
+ * <p>
+ * <ul>
+ * <li><code>Duration.milliseconds(long)</code>
+ * <li><code>Duration.seconds(int)</code>
+ * <li><code>Duration.minutes(int)</code>
+ * <li><code>Duration.hours(int)</code>
+ * <li><code>Duration.days(int)</code>
+ * </ul>
+ * <p>
+ * ...or double-precision floating point values like <code>days(3.2)</code>:
+ * <p>
+ * <ul>
+ * <li><code>Duration.milliseconds(double)</code>
+ * <li><code>Duration.seconds(double)</code>
+ * <li><code>Duration.minutes(double)</code>
+ * <li><code>Duration.hours(double)</code>
+ * <li><code>Duration.days(double)</code>
+ * </ul>
+ * <p>
+ * In the case of <code>milliseconds(double)</code>, the value will be rounded off to the nearest
+ * integral millisecond using <code>Math.round()</code>.
+ * <p>
+ * The precise number of milliseconds represented by a <code>Duration</code> object can be retrieved
+ * by calling the <code>getMilliseconds</code> method. The value of a <code>Duration</code> object
+ * in a given unit like days or hours can be retrieved by calling one of the following unit methods,
+ * each of which returns a double-precision floating point number:
+ * <p>
+ * <ul>
+ * <li><code>seconds()</code>
+ * <li><code>minutes()</code>
+ * <li><code>hours()</code>
+ * <li><code>days()</code>
+ * </ul>
+ * <p>
+ * Values can be added and subtracted using the <code>add(Duration)</code> and
+ * <code>subtract(Duration)</code> methods, each of which returns a new immutable
+ * <code>Duration</code> object.
+ * <p>
+ * <code>String</code> values can be converted to <code>Duration</code> objects using the static
+ * <code>valueOf</code> factory methods. The <code>String</code> format is the opposite of the one
+ * created by <code>toString()</code>, which converts a <code>Duration</code> object to a readable
+ * form, such as "3.2 hours" or "32.5 minutes". Valid units are: milliseconds, seconds, minutes
+ * hours and days. Correct English plural forms are used in creating <code>String</code> values and
+ * are parsed as well. The <code>Locale</code> is respected and "," will be used instead of "." in
+ * the Eurozone.
+ * <p>
+ * The benchmark method will "benchmark" a <code>Runnable</code> or an {@link ICode} implementing
+ * object, returning a <code>Duration</code> object that represents the amount of time elapsed in
+ * running the code.
+ * <p>
+ * Finally, the <code>sleep</code> method will sleep for the value of a <code>Duration</code>.
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+public class Duration extends AbstractTimeValue
+{
+	private static final long serialVersionUID = 1L;
+
+	/** Constant for maximum duration. */
+	public static final Duration MAXIMUM = milliseconds(Long.MAX_VALUE);
+
+	/** Constant for no duration. */
+	public static final Duration NONE = milliseconds(0);
+
+	/** Constant for one day. */
+	public static final Duration ONE_DAY = days(1);
+
+	/** Constant for one hour. */
+	public static final Duration ONE_HOUR = hours(1);
+
+	/** Constant for on minute. */
+	public static final Duration ONE_MINUTE = minutes(1);
+
+	/** Constant for one second. */
+	public static final Duration ONE_SECOND = seconds(1);
+
+	/** Constant for one week. */
+	public static final Duration ONE_WEEK = days(7);
+
+	/** pattern to match strings */
+	private static final Pattern pattern = Pattern.compile(
+		"([0-9]+([.,][0-9]+)?)\\s+(millisecond|second|minute|hour|day)s?", Pattern.CASE_INSENSITIVE);
+
+	/**
+	 * Benchmark the given command.
+	 * 
+	 * @param code
+	 *            an <code>ICode</code>
+	 * @param log
+	 *            optional logger to use with errors and exceptions
+	 * @return the <code>Time</code> value it took to run the code
+	 */
+	public static Duration benchmark(final ICode code, final Logger log)
+	{
+		// Get time before running code
+		final Time start = Time.now();
+
+		// Run the code
+		code.run(log);
+
+		// Return the difference
+		return Time.now().subtract(start);
+	}
+
+	/**
+	 * Benchmark the given command.
+	 * 
+	 * @param code
+	 *            a <code>Runnable</code>
+	 * @return the <code>Time</code> value it took to run the code
+	 */
+	public static Duration benchmark(final Runnable code)
+	{
+		// Get time before running code
+		final Time start = Time.now();
+
+		// Run code
+		code.run();
+
+		// Return the difference
+		return Time.now().subtract(start);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on days.
+	 * 
+	 * @param days
+	 *            days <code>double</code> value
+	 * @return the <code>Duration</code> based on days
+	 */
+	public static Duration days(final double days)
+	{
+		return hours(24.0 * days);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on days.
+	 * 
+	 * @param days
+	 *            days <code>int</code> value
+	 * @return the <code>Duration</code> based on days
+	 */
+	public static Duration days(final int days)
+	{
+		return hours(24 * days);
+	}
+
+	/**
+	 * Calculates the amount of time elapsed since start time.
+	 * 
+	 * @param start
+	 *            the start <code>Time</code>
+	 * @return the elapsed period as a <code>Duration</code>
+	 * @throws IllegalStateException
+	 *             if start <code>Time</code> is in the future
+	 */
+	public static Duration elapsed(final Time start)
+	{
+		return start.elapsedSince();
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on hours.
+	 * 
+	 * @param hours
+	 *            hours <code>double</code> value
+	 * @return the <code>Duration</code> based on hours
+	 */
+	public static Duration hours(final double hours)
+	{
+		return minutes(60.0 * hours);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on hours.
+	 * 
+	 * @param hours
+	 *            hours <code>int</code> value
+	 * @return the <code>Duration</code> based on hours
+	 */
+	public static Duration hours(final int hours)
+	{
+		return minutes(60 * hours);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on milliseconds.
+	 * 
+	 * @param milliseconds
+	 *            milliseconds <code>double</code> value
+	 * @return the <code>Duration</code> based on milliseconds
+	 */
+	public static Duration milliseconds(final double milliseconds)
+	{
+		return milliseconds(Math.round(milliseconds));
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on milliseconds.
+	 * 
+	 * @param milliseconds
+	 *            milliseconds <code>long</code> value
+	 * @return the <code>Duration</code> based on milliseconds
+	 */
+	public static Duration milliseconds(final long milliseconds)
+	{
+		return new Duration(milliseconds);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on minutes.
+	 * 
+	 * @param minutes
+	 *            minutes <code>double</code> value
+	 * @return the <code>Duration</code> based on minutes
+	 */
+	public static Duration minutes(final double minutes)
+	{
+		return seconds(60.0 * minutes);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on minutes.
+	 * 
+	 * @param minutes
+	 *            minutes <code>int</code> value
+	 * @return the <code>Duration</code> based on minutes
+	 */
+	public static Duration minutes(final int minutes)
+	{
+		return seconds(60 * minutes);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on seconds.
+	 * 
+	 * @param seconds
+	 *            seconds <code>double</code> value
+	 * @return the <code>Duration</code> based on seconds
+	 */
+	public static Duration seconds(final double seconds)
+	{
+		return milliseconds(seconds * 1000.0);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> based on seconds.
+	 * 
+	 * @param seconds
+	 *            seconds <code>int</code> value
+	 * @return the <code>Duration</code> based on seconds
+	 */
+	public static Duration seconds(final int seconds)
+	{
+		return milliseconds(seconds * 1000L);
+	}
+
+	/**
+	 * Retrieves the given <code>long</code> as a <code>Duration</code>.
+	 * 
+	 * @param time
+	 *            the duration <code>long</code> value in milliseconds
+	 * @return the <code>Duration</code> value
+	 */
+	public static Duration valueOf(final long time)
+	{
+		return new Duration(time);
+	}
+
+	/**
+	 * Converts the given <code>String</code> to a new <code>Duration</code> object. The string can
+	 * take the form of a floating point number followed by a number of milliseconds, seconds,
+	 * minutes, hours or days. For example "6 hours" or "3.4 days". Parsing is case-insensitive.
+	 * 
+	 * @param string
+	 *            a <code>String</code> to parse
+	 * @return the <code>Duration</code> value of the given <code>String</code>
+	 * @throws StringValueConversionException
+	 */
+	public static Duration valueOf(final String string) throws StringValueConversionException
+	{
+		return valueOf(string, Locale.getDefault(Locale.Category.FORMAT));
+	}
+
+	/**
+	 * Converts the given <code>String</code> to a new <code>Duration</code> object. The string can
+	 * take the form of a floating point number followed by a number of milliseconds, seconds,
+	 * minutes, hours or days. For example "6 hours" or "3.4 days". Parsing is case-insensitive.
+	 * 
+	 * @param string
+	 *            a <code>String</code> to parse
+	 * @param locale
+	 *            the <code>Locale</code> used for parsing
+	 * @return the <code>Duration</code> value of the given <code>String</code>
+	 * @throws StringValueConversionException
+	 */
+	public static Duration valueOf(final String string, final Locale locale)
+		throws StringValueConversionException
+	{
+		final Matcher matcher = pattern.matcher(string);
+
+		if (matcher.matches())
+		{
+			final double value = StringValue.valueOf(matcher.group(1), locale).toDouble();
+			final String units = matcher.group(3);
+
+			if (units.equalsIgnoreCase("millisecond"))
+			{
+				return milliseconds(value);
+			}
+			else if (units.equalsIgnoreCase("second"))
+			{
+				return seconds(value);
+			}
+			else if (units.equalsIgnoreCase("minute"))
+			{
+				return minutes(value);
+			}
+			else if (units.equalsIgnoreCase("hour"))
+			{
+				return hours(value);
+			}
+			else if (units.equalsIgnoreCase("day"))
+			{
+				return days(value);
+			}
+			else
+			{
+				throw new StringValueConversionException("Unrecognized units: " + string);
+			}
+		}
+		else
+		{
+			throw new StringValueConversionException("Unable to parse duration: " + string);
+		}
+	}
+
+	/**
+	 * Private constructor forces use of static factory methods.
+	 * 
+	 * @param milliseconds
+	 *            number of milliseconds in this <code>Duration</code>
+	 */
+	protected Duration(final long milliseconds)
+	{
+		super(milliseconds);
+	}
+
+	/**
+	 * Adds a given <code>Duration</code> to this <code>Duration</code>.
+	 * 
+	 * @param duration
+	 *            the <code>Duration</code> to add
+	 * @return the sum of the <code>Duration</code>s
+	 */
+	public Duration add(final Duration duration)
+	{
+		return valueOf(getMilliseconds() + duration.getMilliseconds());
+	}
+
+	/**
+	 * Retrieves the number of days of the current <code>Duration</code>.
+	 * 
+	 * @return number of days of the current <code>Duration</code>
+	 */
+	public final double days()
+	{
+		return hours() / 24.0;
+	}
+
+	/**
+	 * Retrieves the number of hours of the current <code>Duration</code>.
+	 * 
+	 * @return number of hours of the current <code>Duration</code>
+	 */
+	public final double hours()
+	{
+		return minutes() / 60.0;
+	}
+
+	/**
+	 * Retrieves the number of minutes of the current <code>Duration</code>.
+	 * 
+	 * @return number of minutes of the current <code>Duration</code>
+	 */
+	public final double minutes()
+	{
+		return seconds() / 60.0;
+	}
+
+	/**
+	 * Retrieves the number of seconds of the current <code>Duration</code>.
+	 * 
+	 * @return number of seconds of the current <code>Duration</code>
+	 */
+	public final double seconds()
+	{
+		return getMilliseconds() / 1000.0;
+	}
+
+	/**
+	 * Sleeps for the current <code>Duration</code>.
+	 */
+	public final void sleep()
+	{
+		if (getMilliseconds() > 0)
+		{
+			try
+			{
+				Thread.sleep(getMilliseconds());
+			}
+			catch (InterruptedException e)
+			{
+				// Ignored
+			}
+		}
+	}
+
+	/**
+	 * Subtracts a given <code>Duration</code> from this <code>Duration</code>.
+	 * 
+	 * @param that
+	 *            the <code>Duration</code> to subtract
+	 * @return this <code>Duration</code> minus that <code>Duration</code>
+	 */
+	public Duration subtract(final Duration that)
+	{
+		return valueOf(getMilliseconds() - that.getMilliseconds());
+	}
+
+	/**
+	 * Wait for this duration on the given monitor
+	 * 
+	 * @param object
+	 *            The monitor to wait on
+	 */
+	public void wait(final Object object)
+	{
+		try
+		{
+			object.wait(getMilliseconds());
+		}
+		catch (InterruptedException e)
+		{
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * Retrieves the <code>String</code> representation of this <code>Duration</code> in days,
+	 * hours, minutes, seconds or milliseconds, as appropriate. Uses the default <code>Locale</code>
+	 * .
+	 * 
+	 * @return a <code>String</code> representation
+	 */
+	@Override
+	public String toString()
+	{
+		return toString(Locale.getDefault(Category.FORMAT));
+	}
+
+	/**
+	 * Retrieves the <code>String</code> representation of this <code>Duration</code> in days,
+	 * hours, minutes, seconds or milliseconds, as appropriate.
+	 * 
+	 * @param locale
+	 *            a <code>Locale</code>
+	 * @return a <code>String</code> representation
+	 */
+	public String toString(final Locale locale)
+	{
+		if (getMilliseconds() >= 0)
+		{
+			if (days() >= 1.0)
+			{
+				return unitString(days(), "day", locale);
+			}
+
+			if (hours() >= 1.0)
+			{
+				return unitString(hours(), "hour", locale);
+			}
+
+			if (minutes() >= 1.0)
+			{
+				return unitString(minutes(), "minute", locale);
+			}
+
+			if (seconds() >= 1.0)
+			{
+				return unitString(seconds(), "second", locale);
+			}
+
+			return unitString(getMilliseconds(), "millisecond", locale);
+		}
+		else
+		{
+			return "N/A";
+		}
+	}
+
+	/**
+	 * Converts a value to a unit-suffixed value, taking care of English singular/plural suffix.
+	 * 
+	 * @param value
+	 *            a <code>double</code> value to format
+	 * @param units
+	 *            the units to apply singular or plural suffix to
+	 * @param locale
+	 *            the <code>Locale</code>
+	 * @return a <code>String</code> representation
+	 */
+	private String unitString(final double value, final String units, final Locale locale)
+	{
+		return StringValue.valueOf(value, locale) + " " + units + ((value > 1.0) ? "s" : "");
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/ITimeFrameSource.java b/wicket-util/src/main/java/org/apache/wicket/util/time/ITimeFrameSource.java
new file mode 100755
index 0000000..62eb48b
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/ITimeFrameSource.java
@@ -0,0 +1,40 @@
+/*
+ * 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 java.io.Serializable;
+
+/**
+ * An <code>ITimeFrameSource</code> produces <code>TimeFrame</code> values. The value returned by an
+ * <code>ITimeFrameSource</code> source may vary over time.
+ * 
+ * @see TimeFrame
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+public interface ITimeFrameSource extends Serializable
+{
+	/**
+	 * Retrieves the current <code>TimeFrame</code> value.
+	 * 
+	 * @return the current <code>TimeFrame</code> value
+	 */
+	TimeFrame getTimeFrame();
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/Time.java b/wicket-util/src/main/java/org/apache/wicket/util/time/Time.java
new file mode 100755
index 0000000..2d8597a
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/Time.java
@@ -0,0 +1,614 @@
+/*
+ * 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 java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * An immutable <code>Time</code> class that represents a specific point in time. The underlying
+ * representation is a <code>long</code> value which holds a number of milliseconds since January 1,
+ * 1970, 0:00 GMT. To represent a duration of time, such as "6 seconds", use the
+ * <code>Duration</code> class. To represent a time period with a start and end time, use the
+ * <code>TimeFrame</code> class. To represent a time of day, use the <code>TimeOfDay</code> class.
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+public final class Time extends AbstractTime
+{
+	private static final long serialVersionUID = 1L;
+
+	/** the beginning of UNIX time: January 1, 1970, 0:00 GMT. */
+	public static final Time START_OF_UNIX_TIME = millis(0);
+
+	/** parser in 'yyyy.MM.dd' format. */
+	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd",
+		Locale.ENGLISH);
+
+	/** parser in 'yyyy.MM.dd-h.mma' format. */
+	private static final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy.MM.dd-h.mma",
+		Locale.ENGLISH);
+
+	/** required for rfc1123 date format */
+	private static final String[] DAYS =
+		{"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+	/** required for rfc1123 date format */
+	private static final String[] MONTHS =
+		{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
+
+	/** time zone for greenwich mean time */
+	public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+	
+	/**
+	 * Retrieves a <code>Time</code> instance based on the current time.
+	 * 
+	 * @return the current <code>Time</code>
+	 */
+	public static Time now()
+	{
+		return millis(System.currentTimeMillis());
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance based on the given milliseconds.
+	 * 
+	 * @param time
+	 *            the <code>Time</code> value in milliseconds since START_OF_UNIX_TIME
+	 * @return a corresponding immutable <code>Time</code> object
+	 */
+	public static Time millis(final long time)
+	{
+		return new Time(time);
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd' format.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use when parsing date <code>String</code>
+	 * @param string
+	 *            the <code>String</code> to parse
+	 * @return the time
+	 * @throws ParseException
+	 */
+	public static Time parseDate(final Calendar calendar, final String string)
+		throws ParseException
+	{
+		synchronized (dateFormat)
+		{
+			synchronized (calendar)
+			{
+				dateFormat.setCalendar(calendar);
+
+				return valueOf(dateFormat.parse(string));
+			}
+		}
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd' format using a local time
+	 * <code>Calendar</code>.
+	 * 
+	 * @param string
+	 *            the <code>String</code> to parse
+	 * @return the time
+	 * @throws ParseException
+	 */
+	public static Time parseDate(final String string) throws ParseException
+	{
+		return parseDate(localtime, string);
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd-h.mma' format.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use when parsing the <code>String</code>
+	 * @param string
+	 *            the <code>String</code> to parse
+	 * @return an immutable UNIX <code>Time</code> value
+	 * @throws ParseException
+	 */
+	public static Time valueOf(final Calendar calendar, final String string) throws ParseException
+	{
+		synchronized (dateTimeFormat)
+		{
+			synchronized (calendar)
+			{
+				dateTimeFormat.setCalendar(calendar);
+
+				return valueOf(dateTimeFormat.parse(string));
+			}
+		}
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance based on the given <code>Calendar</code> and
+	 * {@link TimeOfDay} objects.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use
+	 * @param timeOfDay
+	 *            the time of day
+	 * @return a <code>Time</code> value for the time of day today
+	 */
+	public static Time valueOf(final Calendar calendar, final TimeOfDay timeOfDay)
+	{
+		synchronized (calendar)
+		{
+			// Set time to midnight today
+			calendar.setTimeInMillis(System.currentTimeMillis());
+			calendar.set(Calendar.HOUR_OF_DAY, 0); // WICKET-2349
+			calendar.set(Calendar.MINUTE, 0);
+			calendar.set(Calendar.SECOND, 0);
+			calendar.set(Calendar.MILLISECOND, 0); // WICKET-1670
+
+			// Add time of day milliseconds to midnight
+			return millis(calendar.getTimeInMillis() + timeOfDay.getMilliseconds());
+		}
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance based on the given <code>Date</code> object.
+	 * 
+	 * @param date
+	 *            a <code>java.util.Date</code> object
+	 * @return a corresponding immutable <code>Time</code> object
+	 */
+	public static Time valueOf(final Date date)
+	{
+		return new Time(date.getTime());
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance by parsing 'yyyy.MM.dd-h.mma' format.
+	 * 
+	 * @param string
+	 *            the <code>String</code> to parse
+	 * @return the <code>Time</code> instance
+	 * @throws ParseException
+	 */
+	public static Time valueOf(final String string) throws ParseException
+	{
+		return valueOf(localtime, string);
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance by parsing 'pattern' format.
+	 * 
+	 * @param string
+	 *            input
+	 * @param pattern
+	 *            the pattern to parse
+	 * @return a <code>Time</code> instance that resulted from parsing the given <code>String</code>
+	 * @throws ParseException
+	 */
+	public static Time valueOf(final String string, final String pattern) throws ParseException
+	{
+		final SimpleDateFormat dateTimeFormat = new SimpleDateFormat(pattern, Locale.ENGLISH);
+		dateTimeFormat.setCalendar(localtime);
+		return valueOf(dateTimeFormat.parse(string));
+	}
+
+	/**
+	 * Retrieves a <code>Time</code> instance based on the given {@link TimeOfDay} object.
+	 * 
+	 * @param timeOfDay
+	 *            the time of day in local time
+	 * @return a <code>Time</code> value for the time of day today
+	 */
+	public static Time valueOf(final TimeOfDay timeOfDay)
+	{
+		return valueOf(localtime, timeOfDay);
+	}
+
+	/**
+	 * Private constructor forces use of static factory methods.
+	 * 
+	 * @param time
+	 *            the <code>Time</code> value in milliseconds since START_OF_UNIX_TIME
+	 */
+	private Time(final long time)
+	{
+		super(time);
+	}
+
+	/**
+	 * Adds the given <code>Duration</code> to this <code>Time</code> object, moving the time into
+	 * the future.
+	 * 
+	 * @param duration
+	 *            the <code>Duration</code> to add
+	 * @return this <code>Time</code> + <code>Duration</code>
+	 */
+	public Time add(final Duration duration)
+	{
+		return millis(getMilliseconds() + duration.getMilliseconds());
+	}
+
+	/**
+	 * Calculates the amount of time that has elapsed since this <code>Time</code> value.
+	 * 
+	 * @return the amount of time that has elapsed since this <code>Time</code> value
+	 * @throws IllegalStateException
+	 *             thrown if this <code>Time</code> value is in the future
+	 */
+	public Duration elapsedSince()
+	{
+		final Time now = now();
+		if (this.greaterThan(now))
+		{
+			throw new IllegalStateException("This time is in the future");
+		}
+		return now.subtract(this);
+	}
+
+	/**
+	 * Retrieves the <code>Duration</code> from now to this <code>Time</code> value. If this
+	 * <code>Time</code> value is in the past, then the <code>Duration</code> returned will be
+	 * negative. Otherwise, it will be the number of milliseconds from now to this <code>Time</code>
+	 * .
+	 * 
+	 * @return the <code>Duration</code> from now to this <code>Time</code> value
+	 */
+	public Duration fromNow()
+	{
+		return subtract(now());
+	}
+
+	/**
+	 * Retrieves the value of a field from the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use
+	 * @param field
+	 *            the <code>Calendar</code> field to get
+	 * @return the field's value for this point in time on the given <code>Calendar</code>
+	 */
+	public int get(final Calendar calendar, final int field)
+	{
+		synchronized (calendar)
+		{
+			calendar.setTimeInMillis(getMilliseconds());
+
+			return calendar.get(field);
+		}
+	}
+
+	/**
+	 * Retrieves the value of a field.
+	 * 
+	 * @param field
+	 *            the <code>Calendar</code> field to get
+	 * @return the field's value (in local time)
+	 */
+	public int get(final int field)
+	{
+		return get(localtime, field);
+	}
+
+	/**
+	 * Retrieves the day of month field of the current <code>Calendar</code>.
+	 * 
+	 * @return the day of month field value
+	 */
+	public int getDayOfMonth()
+	{
+		return getDayOfMonth(localtime);
+	}
+
+	/**
+	 * Retrieves the day of month field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to get the field value from
+	 * @return the day of month field value
+	 */
+	public int getDayOfMonth(final Calendar calendar)
+	{
+		return get(calendar, Calendar.DAY_OF_MONTH);
+	}
+
+	/**
+	 * Retrieves the hour field of the current <code>Calendar</code>.
+	 * 
+	 * @return the hour field value
+	 */
+	public int getHour()
+	{
+		return getHour(localtime);
+	}
+
+	/**
+	 * Retrieves the hour field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to get the field value from
+	 * @return the hour field value
+	 */
+	public int getHour(final Calendar calendar)
+	{
+		return get(calendar, Calendar.HOUR_OF_DAY);
+	}
+
+	/**
+	 * Retrieves the minute field of the current <code>Calendar</code>.
+	 * 
+	 * @return the minute field value
+	 */
+	public int getMinute()
+	{
+		return getMinute(localtime);
+	}
+
+	/**
+	 * Retrieves the minute field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> from which to get the field value
+	 * @return the minute field value
+	 */
+	public int getMinute(final Calendar calendar)
+	{
+		return get(calendar, Calendar.MINUTE);
+	}
+
+	/**
+	 * Retrieves the month field of the current <code>Calendar</code>.
+	 * 
+	 * @return the month field value
+	 */
+	public int getMonth()
+	{
+		return getMonth(localtime);
+	}
+
+	/**
+	 * Retrieves the month field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> from which to get the field value
+	 * @return the month field value
+	 */
+	public int getMonth(final Calendar calendar)
+	{
+		return get(calendar, Calendar.MONTH);
+	}
+
+	/**
+	 * Retrieves the seconds field of the current <code>Calendar</code>.
+	 * 
+	 * @return the seconds field value
+	 */
+	public int getSecond()
+	{
+		return getSecond(localtime);
+	}
+
+	/**
+	 * Retrieves the seconds field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> from which to get the field value
+	 * @return the seconds field value
+	 */
+	public int getSecond(final Calendar calendar)
+	{
+		return get(calendar, Calendar.SECOND);
+	}
+
+	/**
+	 * Retrieves the year field of the current <code>Calendar</code>.
+	 * 
+	 * @return the year field value
+	 */
+	public int getYear()
+	{
+		return getYear(localtime);
+	}
+
+	/**
+	 * Retrieves the year field of the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> from which to get the field value
+	 * @return the year field value
+	 */
+	public int getYear(final Calendar calendar)
+	{
+		return get(calendar, Calendar.YEAR);
+	}
+
+	/**
+	 * Subtracts the given <code>Duration</code> from this <code>Time</code> object, moving the time
+	 * into the past.
+	 * 
+	 * @param duration
+	 *            the <code>Duration</code> to subtract
+	 * @return this duration of time
+	 */
+	public Time subtract(final Duration duration)
+	{
+		return millis(getMilliseconds() - duration.getMilliseconds());
+	}
+
+	/**
+	 * Subtract time from this and returns the difference as a <code>Duration</code> object.
+	 * 
+	 * @param that
+	 *            the time to subtract
+	 * @return the <code>Duration</code> between this and that time
+	 */
+	public Duration subtract(final Time that)
+	{
+		return Duration.milliseconds(getMilliseconds() - that.getMilliseconds());
+	}
+
+	/**
+	 * Retrieves a <code>Date</code> object for this <code>Time</code> object. A new
+	 * <code>Date</code> object is always returned rather than attempting to cache a date since
+	 * <code>Date</code> is mutable.
+	 * 
+	 * @return this immutable <code>Time</code> object as a mutable <code>java.util.Date</code>
+	 *         object
+	 */
+	public Date toDate()
+	{
+		return new Date(getMilliseconds());
+	}
+
+	/**
+	 * Converts this <code>Time</code> value to a date <code>String</code> using the date formatter
+	 * 'yyyy.MM.dd'.
+	 * 
+	 * @return the date string
+	 */
+	public String toDateString()
+	{
+		return toDateString(localtime);
+	}
+
+	/**
+	 * Converts this <code>Time</code> value to a date <code>String</code> using the formatter
+	 * 'yyyy.MM.dd'.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use in the conversion
+	 * @return the date <code>String</code>
+	 */
+	public String toDateString(final Calendar calendar)
+	{
+		synchronized (dateFormat)
+		{
+			synchronized (calendar)
+			{
+				dateFormat.setCalendar(calendar);
+
+				return dateFormat.format(new Date(getMilliseconds())).toLowerCase(Locale.ROOT);
+			}
+		}
+	}
+
+	/**
+	 * Converts this <code>Time</code> value to a <code>String</code> suitable for use in a file
+	 * system name.
+	 * 
+	 * @return this <code>Time</code> value as a formatted <code>String</code>
+	 */
+	@Override
+	public String toString()
+	{
+		return toDateString() + "-" + toTimeString();
+	}
+
+	/**
+	 * Converts this <code>Time</code> object to a <code>String</code> using the given
+	 * <code>Calendar</code> and format.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use in the conversion
+	 * @param format
+	 *            the format to use
+	 * @return this <code>Time</code> value as a formatted <code>String</code>
+	 */
+	public String toString(final Calendar calendar, final String format)
+	{
+		final SimpleDateFormat dateTimeFormat = new SimpleDateFormat(format, Locale.ENGLISH);
+		dateTimeFormat.setCalendar(calendar == null ? localtime : calendar);
+		return dateTimeFormat.format(new Date(getMilliseconds()));
+	}
+
+	/**
+	 * Converts this <code>Time</code> value to a <code>String</code> using the given format.
+	 * 
+	 * @param format
+	 *            the format to use
+	 * @return this <code>Time</code> value as a formatted string
+	 */
+	public String toString(final String format)
+	{
+		return toString(null, format);
+	}
+
+	/**
+	 * Returns this time stamp in RFC1123 string format. Contrary to
+	 * {@link java.text.SimpleDateFormat} this is thread-safe. Taken from the source code of jetty
+	 * 7.3.0, credits + thanks to Greg Wilkins!
+	 * 
+	 * @return timestamp string in RFC1123 format
+	 */
+	public String toRfc1123TimestampString()
+	{
+		final Calendar cal = GregorianCalendar.getInstance(GMT);
+		final StringBuilder buf = new StringBuilder(32);
+
+		cal.setTimeInMillis(getMilliseconds());
+
+		int day_of_week = cal.get(Calendar.DAY_OF_WEEK);
+		int day_of_month = cal.get(Calendar.DAY_OF_MONTH);
+		int month = cal.get(Calendar.MONTH);
+		int year = cal.get(Calendar.YEAR);
+		int century = year / 100;
+		year = year % 100;
+
+		int hours = cal.get(Calendar.HOUR_OF_DAY);
+		int minutes = cal.get(Calendar.MINUTE);
+		int seconds = cal.get(Calendar.SECOND);
+
+		buf.append(DAYS[day_of_week]);
+		buf.append(',');
+		buf.append(' ');
+		appendTwoDigits(buf, day_of_month);
+
+		buf.append(' ');
+		buf.append(MONTHS[month]);
+		buf.append(' ');
+		appendTwoDigits(buf, century);
+		appendTwoDigits(buf, year);
+
+		buf.append(' ');
+		appendTwoDigits(buf, hours);
+		buf.append(':');
+		appendTwoDigits(buf, minutes);
+		buf.append(':');
+		appendTwoDigits(buf, seconds);
+		buf.append(" GMT");
+
+		return buf.toString();
+	}
+
+	/**
+	 * helper method for {@link #toRfc1123TimestampString()}
+	 * 
+	 * @param str
+	 * @param number
+	 */
+	private static void appendTwoDigits(StringBuilder str, int number)
+	{
+		str.append((char)(number / 10 + '0'));
+		str.append((char)(number % 10 + '0'));
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/TimeFrame.java b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeFrame.java
new file mode 100755
index 0000000..fc553fd
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeFrame.java
@@ -0,0 +1,260 @@
+/*
+ * 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
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+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 + "]";
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/TimeMap.java b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeMap.java
new file mode 100755
index 0000000..90dd09c
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeMap.java
@@ -0,0 +1,110 @@
+/*
+ * 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 java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class maps <code>ITimeFrame</code>s to <code>Object</code>s. Since values are stored using
+ * <code>ITimeFrameSource</code> implementing objects, the value returned by the source may vary
+ * over time. For example, one implementation of <code>ITimeFrameSource</code> might return the
+ * start and end time of lunch on any given day.
+ * <p>
+ * To associate an object with a dynamic <code>TimeFrame</code> (via <code>ITimeFrameSource</code>),
+ * call <code>put(ITimeFrameSource, Object)</code>. You can later retrieve the first object for a
+ * point in time with <code>get(Time)</code>. The <code>get</code> method is provided for
+ * convenience and is equivalent to <code>get(Time.now())</code>.
+ * <p>
+ * This class is not thread-safe.
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+public final class TimeMap implements Serializable
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <code>Map</code> from <code>ITimeFrameSource</code> implementing objects to
+	 * <code>Object</code> values.
+	 */
+	private final Map<ITimeFrameSource, Object> sources = new ConcurrentHashMap<>();
+
+	/**
+	 * Retrieves an <code>Object</code> for the current <code>Time</code> value.
+	 * 
+	 * @return <code>Object</code> for the current <code>Time</code> value
+	 */
+	public Object get()
+	{
+		return get(Time.now());
+	}
+
+	/**
+	 * Retrieves an <code>Object</code> for the given <code>Time</code> value.
+	 * 
+	 * @param time
+	 *            the <code>Time</code> value
+	 * @return gets an <code>Object</code> for the given <code>Time</code> value or
+	 *         <code>null</code> if none exists
+	 */
+	public Object get(final Time time)
+	{
+		for (ITimeFrameSource source : sources.keySet())
+		{
+			final TimeFrame current = source.getTimeFrame();
+			if (current.contains(time))
+			{
+				return sources.get(current);
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Associates an <code>Object</code> with a dynamic <code>TimeFrame</code>.
+	 * 
+	 * @param source
+	 *            a source that can produce a <code>TimeFrame</code> with which to compare a
+	 *            <code>Time</code> value
+	 * @param o
+	 *            the <code>Object</code> to be returned for the given dynamic
+	 *            <code>TimeFrame</code>
+	 */
+	public void put(final ITimeFrameSource source, final Object o)
+	{
+		final TimeFrame timeframe = source.getTimeFrame();
+
+		for (ITimeFrameSource tfs : sources.keySet())
+		{
+			final TimeFrame current = tfs.getTimeFrame();
+
+			if (timeframe.overlaps(current))
+			{
+				throw new IllegalArgumentException("Timeframe " + timeframe +
+					" overlaps timeframe " + current);
+			}
+		}
+
+		sources.put(source, o);
+	}
+}
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/time/TimeOfDay.java b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeOfDay.java
new file mode 100755
index 0000000..183de92
--- /dev/null
+++ b/wicket-util/src/main/java/org/apache/wicket/util/time/TimeOfDay.java
@@ -0,0 +1,419 @@
+/*
+ * 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 java.text.ParseException;
+import java.util.Calendar;
+import org.apache.wicket.util.lang.EnumeratedType;
+
+
+/**
+ * An immutable time of day value represented as milliseconds since the most recent midnight.
+ * <p>
+ * Values can be constructed using various factory methods:
+ * <ul>
+ * <li><code>valueOf(long)</code> where <code>long</code> is milliseconds since midnight
+ * <li><code>valueOf(String)</code> where the <code>String</code> is in 'h.mma' format
+ * <li><code>valueOf(Calendar, String)</code> where the <code>String</code> is in 'h.mma' format
+ * <li><code>valueOf(Duration)</code> where <code>Duration</code> is time since midnight
+ * <li><code>valueOf(Time)</code> where <code>Time</code> is some point in time today
+ * <li><code>valueOf(Calendar, Time)</code> where <code>Time</code> is some point in time today
+ * <li><code>militaryTime(int hour, int minute, int second)</code> for 24-hour time
+ * <li><code>time(int hour, int minute, Meridian)</code> where <code>Meridian</code> is AM or PM
+ * <li><code>time(int hour, int minute, int second, Meridian)</code> where <code>Meridian</code> is
+ * AM or PM
+ * <li><code>now()</code> to construct the current time of day
+ * <li><code>now(Calendar)</code> to construct the current time of day using a given
+ * <code>Calendar</code>
+ * </ul>
+ * <p>
+ * If an attempt is made to construct an illegal time of day value (one that is greater than 24
+ * hours worth of milliseconds), an <code>IllegalArgumentException</code> will be thrown.
+ * <p>
+ * Military hours, minutes and seconds of the time of day can be retrieved by calling the
+ * <code>hour</code>, <code>minute</code>, and <code>second</code> methods.
+ * <p>
+ * The next occurrence of a given <code>TimeOfDay</code> can be retrieved by calling
+ * <code>next()</code> or <code>next(Calendar)</code>.
+ * 
+ * @author Jonathan Locke
+ * @since 1.2.6
+ * 
+ * @deprecated Since Wicket 9 this class is obsolete and no more used. It will be removed in Wicket 10
+ */
+public final class TimeOfDay extends AbstractTime
+{
+	private static final long serialVersionUID = 1L;
+
+	/** Constant for AM time. */
+	public static final Meridian AM = new Meridian("AM");
+
+	/** Constant for midnight. */
+	public static final TimeOfDay MIDNIGHT = time(12, 0, AM);
+
+	/** Constant for PM time. */
+	public static final Meridian PM = new Meridian("PM");
+
+	/** Constant for noon. */
+	public static final TimeOfDay NOON = time(12, 0, PM);
+
+	/** Typesafe AM/PM enumeration. */
+	public static final class Meridian extends EnumeratedType
+	{
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * Constructor.
+		 * 
+		 * @param name
+		 *            the meridian name (value)
+		 */
+		Meridian(final String name)
+		{
+			super(name);
+		}
+	}
+
+	/**
+	 * Retrieves a <code>TimeOfDay</code> value on a 24-hour clock.
+	 * 
+	 * @param hour
+	 *            the hour (0-23)
+	 * @param minute
+	 *            the minute (0-59)
+	 * @param second
+	 *            the second (0-59)
+	 * @return the time of day
+	 */
+	public static TimeOfDay militaryTime(final int hour, final int minute, final int second)
+	{
+		if ((hour > 23) || (hour < 0))
+		{
+			throw new IllegalArgumentException("Hour " + hour + " is not valid");
+		}
+
+		if ((minute > 59) || (minute < 0))
+		{
+			throw new IllegalArgumentException("Minute " + minute + " is not valid");
+		}
+
+		if ((second > 59) || (second < 0))
+		{
+			throw new IllegalArgumentException("Second " + second + " is not valid");
+		}
+
+		return valueOf(Duration.hours(hour)
+			.add(Duration.minutes(minute))
+			.add(Duration.seconds(second)));
+	}
+
+	/**
+	 * Retrieves the <code>TimeOfDay</code> representing 'now'.
+	 * 
+	 * @return the time of day it is now
+	 */
+	public static TimeOfDay now()
+	{
+		return valueOf(Time.now());
+	}
+
+	/**
+	 * Retrieves the <code>TimeOfDay</code> representing 'now' on the given <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use
+	 * @return the time of day it is now on the given <code>Calendar</code>
+	 */
+	public static TimeOfDay now(final Calendar calendar)
+	{
+		return valueOf(calendar, Time.now());
+	}
+
+	/**
+	 * Retrieves a <code>TimeOfDay</code> on a 12-hour clock.
+	 * 
+	 * @param hour
+	 *            the hour (1-12)
+	 * @param minute
+	 *            the minute (0-59)
+	 * @param second
+	 *            the second (0-59)
+	 * @param meridian
+	 *            AM or PM
+	 * @return the <code>TimeOfDay</code> value
+	 */
+	public static TimeOfDay time(final int hour, final int minute, final int second,
+		final Meridian meridian)
+	{
+		if (meridian == PM)
+		{
+			if (hour == 12)
+			{
+				return militaryTime(12, minute, second);
+			}
+			else
+			{
+				return militaryTime(hour + 12, minute, second);
+			}
+		}
+		else
+		{
+			if (hour == 12)
+			{
+				return militaryTime(0, minute, second);
+			}
+			else
+			{
+				return militaryTime(hour, minute, second);
+			}
+		}
+	}
+
+	/**
+	 * Retrieves a <code>TimeOfDay</code> on a 12-hour clock.
+	 * 
+	 * @param hour
+	 *            the hour (1-12)
+	 * @param minute
+	 *            the minute (0-59)
+	 * @param meridian
+	 *            AM of PM
+	 * @return the <code>TimeOfDay</code> value
+	 */
+	public static TimeOfDay time(final int hour, final int minute, final Meridian meridian)
+	{
+		return time(hour, minute, 0, meridian);
+	}
+
+	/**
+	 * Converts a time <code>String</code> and <code>Calendar</code> to a <code>TimeOfDay</code>
+	 * instance.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use when parsing time <code>String</code>
+	 * @param time
+	 *            a <code>String</code> in 'h.mma' format
+	 * @return the <code>TimeOfDay</code> on the given <code>Calendar</code>
+	 * @throws ParseException
+	 */
+	public static TimeOfDay valueOf(final Calendar calendar, final String time)
+		throws ParseException
+	{
+		synchronized (timeFormat)
+		{
+			synchronized (calendar)
+			{
+				timeFormat.setCalendar(calendar);
+				return new TimeOfDay(timeFormat.parse(time).getTime());
+			}
+		}
+	}
+
+	/**
+	 * Converts a <code>Time</code> instance and <code>Calendar</code> to a <code>TimeOfDay</code>
+	 * instance.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use
+	 * @param time
+	 *            a <code>Time</code> instance
+	 * @return the <code>TimeOfDay</code> on the given <code>Calendar</code>
+	 */
+	public static TimeOfDay valueOf(final Calendar calendar, final Time time)
+	{
+		return militaryTime(time.getHour(calendar), time.getMinute(calendar),
+			time.getSecond(calendar));
+	}
+
+	/**
+	 * Converts a <code>Duration</code> instance to a <code>TimeOfDay</code> instance.
+	 * 
+	 * @param duration
+	 *            the <code>Duration</code> to use
+	 * @return the <code>TimeOfDay</code> of the given <code>Duration</code>
+	 */
+	public static TimeOfDay valueOf(final Duration duration)
+	{
+		return new TimeOfDay(duration.getMilliseconds());
+	}
+
+	/**
+	 * Converts a <code>long</code> value to a <code>TimeOfDay</code> instance.
+	 * 
+	 * @param time
+	 *            the time in milliseconds today
+	 * @return the <code>TimeOfDay</code>
+	 */
+	public static TimeOfDay valueOf(final long time)
+	{
+		return new TimeOfDay(time);
+	}
+
+	/**
+	 * Converts a <code>String</code> value to a <code>TimeOfDay</code> instance.
+	 * 
+	 * @param time
+	 *            a <code>String</code> in 'h.mma' format
+	 * @return the <code>TimeOfDay</code>
+	 * @throws ParseException
+	 */
+	public static TimeOfDay valueOf(final String time) throws ParseException
+	{
+		return valueOf(localtime, time);
+	}
+
+	/**
+	 * Converts a <code>String</code> value to a <code>TimeOfDay</code> instance.
+	 * 
+	 * @param time
+	 *            a <code>Time</code> to convert to <code>TimeOfDay</code>
+	 * @return the <code>TimeOfDay</code> in the current time zone
+	 */
+	public static TimeOfDay valueOf(final Time time)
+	{
+		return valueOf(AbstractTime.localtime, time);
+	}
+
+	/**
+	 * Private utility constructor forces use of static factory methods.
+	 * 
+	 * @param time
+	 *            the time today in milliseconds
+	 */
+	private TimeOfDay(final long time)
+	{
+		super(time);
+
+		// A time of day value must be less than 1 day of milliseconds
+		if (Duration.valueOf(time).greaterThan(Duration.ONE_DAY))
+		{
+			throw new IllegalArgumentException("Time " + this + " is not a time of day value");
+		}
+	}
+
+	/**
+	 * Retrieves the hour of the day.
+	 * 
+	 * @return the hour (0-23) of this <code>TimeOfDay</code>
+	 */
+	public int hour()
+	{
+		return toHours(getMilliseconds());
+	}
+
+	/**
+	 * Retrieves the minute.
+	 * 
+	 * @return the minute (0-59) of this <code>TimeOfDay</code>
+	 */
+	public int minute()
+	{
+		return toMinutes(getMilliseconds()) % 60;
+	}
+
+	/**
+	 * Retrieves the next occurrence of this <code>TimeOfDay</code> in local time.
+	 * 
+	 * @return the next occurrence of this <code>TimeOfDay</code> in local time
+	 */
+	public Time next()
+	{
+		return next(AbstractTime.localtime);
+	}
+
+	/**
+	 * Retrieves the next occurrence of this <code>TimeOfDay</code> on the given
+	 * <code>Calendar</code>.
+	 * 
+	 * @param calendar
+	 *            the <code>Calendar</code> to use
+	 * @return the next occurrence of this <code>TimeOfDay</code> on the given <code>Calendar</code>
+	 */
+	public Time next(final Calendar calendar)
+	{
+		// Get this time of day today
+		final Time timeToday = Time.valueOf(calendar, this);
+
+		// If it has already passed
+		if (timeToday.before(Time.now()))
+		{
+			// Return the time tomorrow
+			return Time.valueOf(calendar, this).add(Duration.ONE_DAY);
+		}
+		else
+		{
+			// Time hasn't happened yet today
+			return timeToday;
+		}
+	}
+
+	/**
+	 * Retrieves the second.
+	 * 
+	 * @return the second (0-59)
+	 */
+	public int second()
+	{
+		return toSeconds(getMilliseconds()) % 60;
+	}
+
+	/**
+	 * @see Object#toString()
+	 */
+	@Override
+	public String toString()
+	{
+		final int second = second();
+		return "" + hour() + ":" + minute() + (second != 0 ? ":" + second : "");
+	}
+
+	/**
+	 * Retrieves milliseconds as hours.
+	 * 
+	 * @param milliseconds
+	 *            milliseconds to convert
+	 * @return converted input
+	 */
+	private int toHours(final long milliseconds)
+	{
+		return toMinutes(milliseconds) / 60;
+	}
+
+	/**
+	 * Retrieves milliseconds as minutes.
+	 * 
+	 * @param milliseconds
+	 *            milliseconds to convert
+	 * @return converted input
+	 */
+	private int toMinutes(final long milliseconds)
+	{
+		return toSeconds(milliseconds) / 60;
+	}
+
+	/**
+	 * Retrieves milliseconds as seconds.
+	 * 
+	 * @param milliseconds
+	 *            milliseconds to convert
+	 * @return converted input
+	 */
+	private int toSeconds(final long milliseconds)
+	{
+		return (int)(milliseconds / 1000);
+	}
+}