001 package org.nakedobjects.applib.value;
002
003 import java.text.DateFormat;
004 import java.util.Calendar;
005 import java.util.Date;
006 import java.util.TimeZone;
007
008 import org.nakedobjects.applib.annotation.Value;
009 import org.nakedobjects.applib.clock.Clock;
010
011
012 /**
013 * Value object representing a time value.
014 *
015 * <p>
016 * TODO: other methods to implement:
017 * <ul>
018 * <li>comparison methods</li>
019 * <li>sameHourAs() hour ==hour sameMinuteAs() minutes = minutes sameTimeAs(hour, min) hour == hour & minutes ==
020 * minutes</li>
021 * <li>withinNextTimePeriod(int hours, int minutes); withinTimePeriod(Date d, int hours, int minutes)</li>
022 * <li>withinPreviousTimePeriod(int hours, int minutes); d.hour >= this.hour >= d.hour + hours & d.minutes >=
023 * this.minutes >= d.minutes + minutes</li>
024 * </ul>
025 */
026 @Value(semanticsProviderName = "org.nakedobjects.metamodel.value.TimeValueSemanticsProvider")
027 public class Time extends Magnitude {
028 private static final long serialVersionUID = 1L;
029 public static final int MINUTE = 60;
030 public static final int HOUR = 60 * MINUTE;
031 public static final int DAY = 24 * HOUR;
032 private static final DateFormat SHORT_FORMAT = DateFormat.getTimeInstance(DateFormat.SHORT);
033 private static final long zero;
034 private static final TimeZone UtcTimeZone;
035 static {
036 UtcTimeZone = TimeZone.getTimeZone("Etc/UTC");
037
038 final Calendar cal = Calendar.getInstance();
039 cal.setTimeZone(UtcTimeZone);
040 // set to 1-Jan-1970 00:00:00 (the epoch)
041 cal.set(Calendar.MILLISECOND, 0);
042 cal.set(Calendar.SECOND, 0);
043 cal.set(Calendar.MINUTE, 0);
044 cal.set(Calendar.HOUR_OF_DAY, 0);
045 cal.clear(Calendar.AM_PM);
046 cal.clear(Calendar.HOUR);
047 cal.set(Calendar.DAY_OF_MONTH, 1);
048 cal.set(Calendar.MONTH, 0);
049 cal.set(Calendar.YEAR, 1970);
050 zero = cal.getTime().getTime();
051
052 SHORT_FORMAT.setTimeZone(UtcTimeZone);
053 }
054
055 static long getZero() {
056 return zero / 1000;
057 }
058
059 private final Date date;
060
061 /**
062 * Create a Time object set to the current time.
063 */
064 public Time() {
065 final Calendar cal = createCalendar();
066 cal.setTimeZone(TimeZone.getDefault());
067 cal.setTime(new Date(Clock.getTime()));
068
069 date = time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
070 }
071
072 /**
073 * Create a Time object for storing a time with the time set to the specified hours and minutes.
074 */
075 public Time(final int hour, final int minute) {
076 date = time(hour, minute);
077 }
078
079 private Date time(final int hour, final int minute) {
080 checkTime(hour, minute, 0);
081
082 final Calendar cal = createCalendar();
083 cal.setTimeZone(UtcTimeZone);
084 cal.set(Calendar.HOUR_OF_DAY, hour);
085 cal.set(Calendar.MINUTE, minute);
086
087 cal.set(Calendar.MILLISECOND, 0);
088 cal.set(Calendar.SECOND, 0);
089 cal.set(Calendar.DAY_OF_MONTH, 1);
090 cal.set(Calendar.MONTH, 0);
091 cal.set(Calendar.YEAR, 1970);
092 return cal.getTime();
093 }
094
095 /**
096 * Create a Time object for storing a time with the time set to the specified time of the Java Date
097 * object.
098 */
099 public Time(final Date date) {
100 final Calendar localTime = createCalendar();
101 // localTime.setTimeZone(TimeZone.getDefault());
102 localTime.setTimeZone(UtcTimeZone);
103 localTime.setTime(date);
104
105 this.date = time(localTime.get(Calendar.HOUR_OF_DAY), localTime.get(Calendar.MINUTE));
106 }
107
108 /**
109 * Add the specified hours and minutes to this time value, returned as a new Time object.
110 */
111 public Time add(final int hours, final int minutes) {
112 final Calendar cal = Calendar.getInstance();
113 cal.setTime(date);
114 cal.add(Calendar.MINUTE, minutes);
115 cal.add(Calendar.HOUR_OF_DAY, hours);
116 return createTime(cal.getTime());
117 }
118
119 private void checkTime(final int hour, final int minute, final int second) {
120 if ((hour < 0) || (hour > 23)) {
121 throw new IllegalArgumentException("Hour must be in the range 0 - 23 inclusive");
122 }
123
124 if ((minute < 0) || (minute > 59)) {
125 throw new IllegalArgumentException("Minute must be in the range 0 - 59 inclusive");
126 }
127
128 if ((second < 0) || (second > 59)) {
129 throw new IllegalArgumentException("Second must be in the range 0 - 59 inclusive");
130 }
131 }
132
133 /**
134 * Returns a Calendar object with the irrelevant field (determined by this objects type) set to zero.
135 */
136 private Calendar createCalendar() {
137 final Calendar cal = Calendar.getInstance();
138 cal.setTimeZone(UtcTimeZone);
139
140 // clear all aspects of the time that are not used
141 cal.set(Calendar.MILLISECOND, 0);
142 cal.set(Calendar.SECOND, 0);
143 cal.set(Calendar.DAY_OF_MONTH, 1);
144 cal.clear(Calendar.AM_PM);
145 cal.clear(Calendar.HOUR);
146 cal.set(Calendar.MONTH, 0);
147 cal.set(Calendar.YEAR, 1970);
148
149 return cal;
150 }
151
152 protected Time createTime(final int hours, final int minutes) {
153 return new Time(hours, minutes);
154 }
155
156 protected Time createTime(final Date date) {
157 return new Time(date);
158 }
159
160 public java.util.Date dateValue() {
161 return (date == null) ? null : date;
162 }
163
164 @Override
165 public boolean equals(final Object obj) {
166 if (this == obj) {
167 return true;
168 }
169 if (!(obj instanceof Time)) {
170 return false;
171 }
172 final Time object = (Time) obj;
173 return object.date.equals(date);
174 }
175
176 public int getHour() {
177 final Calendar c = Calendar.getInstance();
178 c.setTimeZone(UtcTimeZone);
179 c.setTime(date);
180 return c.get(Calendar.HOUR_OF_DAY);
181 }
182
183 public int getMinute() {
184 final Calendar c = Calendar.getInstance();
185 c.setTimeZone(UtcTimeZone);
186 c.setTime(date);
187 return c.get(Calendar.MINUTE);
188 }
189
190 /**
191 * returns true if the time of this object has the same value as the specified time
192 */
193 @Override
194 public boolean isEqualTo(final Magnitude time) {
195 if (time instanceof Time) {
196 return (date == null) ? false : (date.equals(((Time) time).date));
197 } else {
198 throw new IllegalArgumentException("Parameter must be of type Time");
199 }
200 }
201
202 /**
203 * returns true if the time of this object is earlier than the specified time
204 */
205 @Override
206 public boolean isLessThan(final Magnitude time) {
207 if (time instanceof Time) {
208 return (date != null) && date.before(((Time) time).date);
209 } else {
210 throw new IllegalArgumentException("Parameter must be of type Time");
211 }
212 }
213
214 /**
215 * The number of seconds since midnight.
216 */
217 public long longValue() {
218 return date.getTime() / 1000;
219 }
220
221 public String titleString() {
222 return (date == null) ? "" : SHORT_FORMAT.format(date);
223 }
224
225 @Override
226 public String toString() {
227 return getHour() + ":" + getMinute();
228
229 }
230
231 public boolean sameHourAs(final Time time) {
232 return getHour() == time.getHour();
233 }
234
235 public boolean sameMinuteAs(final Time time) {
236 return getMinute() == time.getMinute();
237 }
238
239 public Time onTheHour() {
240 return createTime(getHour(), 0);
241 }
242 }
243 // Copyright (c) Naked Objects Group Ltd.