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.