001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ----------------
028 * Millisecond.java
029 * ----------------
030 * (C) Copyright 2001-2007, by Object Refinery Limited.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * Changes
036 * -------
037 * 11-Oct-2001 : Version 1 (DG);
038 * 19-Dec-2001 : Added new constructors as suggested by Paul English (DG);
039 * 26-Feb-2002 : Added new getStart() and getEnd() methods (DG);
040 * 29-Mar-2002 : Fixed bug in getStart(), getEnd() and compareTo() methods (DG);
041 * 10-Sep-2002 : Added getSerialIndex() method (DG);
042 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
043 * 10-Jan-2003 : Changed base class and method names (DG);
044 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented
045 * Serializable (DG);
046 * 21-Oct-2003 : Added hashCode() method (DG);
047 * ------------- JFREECHART 1.0.x ---------------------------------------------
048 * 05-Oct-2006 : Updated API docs (DG);
049 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
050 * 04-Apr-2007 : In Millisecond(Date, TimeZone), peg milliseconds to the
051 * specified zone (DG);
052 *
053 */
054
055 package org.jfree.data.time;
056
057 import java.io.Serializable;
058 import java.util.Calendar;
059 import java.util.Date;
060 import java.util.TimeZone;
061
062 /**
063 * Represents a millisecond. This class is immutable, which is a requirement
064 * for all {@link RegularTimePeriod} subclasses.
065 */
066 public class Millisecond extends RegularTimePeriod implements Serializable {
067
068 /** For serialization. */
069 static final long serialVersionUID = -5316836467277638485L;
070
071 /** A constant for the first millisecond in a second. */
072 public static final int FIRST_MILLISECOND_IN_SECOND = 0;
073
074 /** A constant for the last millisecond in a second. */
075 public static final int LAST_MILLISECOND_IN_SECOND = 999;
076
077 /** The day. */
078 private Day day;
079
080 /** The hour in the day. */
081 private byte hour;
082
083 /** The minute. */
084 private byte minute;
085
086 /** The second. */
087 private byte second;
088
089 /** The millisecond. */
090 private int millisecond;
091
092 /**
093 * The pegged millisecond.
094 */
095 private long firstMillisecond;
096
097 /**
098 * Constructs a millisecond based on the current system time.
099 */
100 public Millisecond() {
101 this(new Date());
102 }
103
104 /**
105 * Constructs a millisecond.
106 *
107 * @param millisecond the millisecond (0-999).
108 * @param second the second.
109 */
110 public Millisecond(int millisecond, Second second) {
111 this.millisecond = millisecond;
112 this.second = (byte) second.getSecond();
113 this.minute = (byte) second.getMinute().getMinute();
114 this.hour = (byte) second.getMinute().getHourValue();
115 this.day = second.getMinute().getDay();
116 peg(Calendar.getInstance());
117 }
118
119 /**
120 * Creates a new millisecond.
121 *
122 * @param millisecond the millisecond (0-999).
123 * @param second the second (0-59).
124 * @param minute the minute (0-59).
125 * @param hour the hour (0-23).
126 * @param day the day (1-31).
127 * @param month the month (1-12).
128 * @param year the year (1900-9999).
129 */
130 public Millisecond(int millisecond, int second, int minute, int hour,
131 int day, int month, int year) {
132
133 this(millisecond, new Second(second, minute, hour, day, month, year));
134
135 }
136
137 /**
138 * Constructs a millisecond.
139 *
140 * @param time the time.
141 */
142 public Millisecond(Date time) {
143 this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
144 }
145
146 /**
147 * Creates a millisecond.
148 *
149 * @param time the instant in time.
150 * @param zone the time zone.
151 */
152 public Millisecond(Date time, TimeZone zone) {
153 Calendar calendar = Calendar.getInstance(zone);
154 calendar.setTime(time);
155 this.millisecond = calendar.get(Calendar.MILLISECOND);
156 this.second = (byte) calendar.get(Calendar.SECOND);
157 this.minute = (byte) calendar.get(Calendar.MINUTE);
158 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
159 this.day = new Day(time, zone);
160 peg(calendar);
161 }
162
163 /**
164 * Returns the second.
165 *
166 * @return The second.
167 */
168 public Second getSecond() {
169 return new Second(this.second, this.minute, this.hour,
170 this.day.getDayOfMonth(), this.day.getMonth(),
171 this.day.getYear());
172 }
173
174 /**
175 * Returns the millisecond.
176 *
177 * @return The millisecond.
178 */
179 public long getMillisecond() {
180 return this.millisecond;
181 }
182
183 /**
184 * Returns the first millisecond of the second. This will be determined
185 * relative to the time zone specified in the constructor, or in the
186 * calendar instance passed in the most recent call to the
187 * {@link #peg(Calendar)} method.
188 *
189 * @return The first millisecond of the second.
190 *
191 * @see #getLastMillisecond()
192 */
193 public long getFirstMillisecond() {
194 return this.firstMillisecond;
195 }
196
197 /**
198 * Returns the last millisecond of the second. This will be
199 * determined relative to the time zone specified in the constructor, or
200 * in the calendar instance passed in the most recent call to the
201 * {@link #peg(Calendar)} method.
202 *
203 * @return The last millisecond of the second.
204 *
205 * @see #getFirstMillisecond()
206 */
207 public long getLastMillisecond() {
208 return this.firstMillisecond;
209 }
210
211 /**
212 * Recalculates the start date/time and end date/time for this time period
213 * relative to the supplied calendar (which incorporates a time zone).
214 *
215 * @param calendar the calendar (<code>null</code> not permitted).
216 *
217 * @since 1.0.3
218 */
219 public void peg(Calendar calendar) {
220 this.firstMillisecond = getFirstMillisecond(calendar);
221 }
222
223 /**
224 * Returns the millisecond preceding this one.
225 *
226 * @return The millisecond preceding this one.
227 */
228 public RegularTimePeriod previous() {
229
230 RegularTimePeriod result = null;
231
232 if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) {
233 result = new Millisecond(this.millisecond - 1, getSecond());
234 }
235 else {
236 Second previous = (Second) getSecond().previous();
237 if (previous != null) {
238 result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous);
239 }
240 }
241 return result;
242
243 }
244
245 /**
246 * Returns the millisecond following this one.
247 *
248 * @return The millisecond following this one.
249 */
250 public RegularTimePeriod next() {
251
252 RegularTimePeriod result = null;
253 if (this.millisecond != LAST_MILLISECOND_IN_SECOND) {
254 result = new Millisecond(this.millisecond + 1, getSecond());
255 }
256 else {
257 Second next = (Second) getSecond().next();
258 if (next != null) {
259 result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next);
260 }
261 }
262 return result;
263
264 }
265
266 /**
267 * Returns a serial index number for the millisecond.
268 *
269 * @return The serial index number.
270 */
271 public long getSerialIndex() {
272 long hourIndex = this.day.getSerialIndex() * 24L + this.hour;
273 long minuteIndex = hourIndex * 60L + this.minute;
274 long secondIndex = minuteIndex * 60L + this.second;
275 return secondIndex * 1000L + this.millisecond;
276 }
277
278 /**
279 * Tests the equality of this object against an arbitrary Object.
280 * <P>
281 * This method will return true ONLY if the object is a Millisecond object
282 * representing the same millisecond as this instance.
283 *
284 * @param obj the object to compare
285 *
286 * @return <code>true</code> if milliseconds and seconds of this and object
287 * are the same.
288 */
289 public boolean equals(Object obj) {
290 if (obj == this) {
291 return true;
292 }
293 if (!(obj instanceof Millisecond)) {
294 return false;
295 }
296 Millisecond that = (Millisecond) obj;
297 if (this.millisecond != that.millisecond) {
298 return false;
299 }
300 if (this.second != that.second) {
301 return false;
302 }
303 if (this.minute != that.minute) {
304 return false;
305 }
306 if (this.hour != that.hour) {
307 return false;
308 }
309 if (!this.day.equals(that.day)) {
310 return false;
311 }
312 return true;
313 }
314
315 /**
316 * Returns a hash code for this object instance. The approach described by
317 * Joshua Bloch in "Effective Java" has been used here:
318 * <p>
319 * <code>http://developer.java.sun.com/developer/Books/effectivejava
320 * /Chapter3.pdf</code>
321 *
322 * @return A hashcode.
323 */
324 public int hashCode() {
325 int result = 17;
326 result = 37 * result + this.millisecond;
327 result = 37 * result + getSecond().hashCode();
328 return result;
329 }
330
331 /**
332 * Returns an integer indicating the order of this Millisecond object
333 * relative to the specified object:
334 *
335 * negative == before, zero == same, positive == after.
336 *
337 * @param obj the object to compare
338 *
339 * @return negative == before, zero == same, positive == after.
340 */
341 public int compareTo(Object obj) {
342
343 int result;
344 long difference;
345
346 // CASE 1 : Comparing to another Second object
347 // -------------------------------------------
348 if (obj instanceof Millisecond) {
349 Millisecond ms = (Millisecond) obj;
350 difference = getFirstMillisecond() - ms.getFirstMillisecond();
351 if (difference > 0) {
352 result = 1;
353 }
354 else {
355 if (difference < 0) {
356 result = -1;
357 }
358 else {
359 result = 0;
360 }
361 }
362 }
363
364 // CASE 2 : Comparing to another TimePeriod object
365 // -----------------------------------------------
366 else if (obj instanceof RegularTimePeriod) {
367 // more difficult case - evaluate later...
368 result = 0;
369 }
370
371 // CASE 3 : Comparing to a non-TimePeriod object
372 // ---------------------------------------------
373 else {
374 // consider time periods to be ordered after general objects
375 result = 1;
376 }
377
378 return result;
379
380 }
381
382 /**
383 * Returns the first millisecond of the time period.
384 *
385 * @param calendar the calendar (<code>null</code> not permitted).
386 *
387 * @return The first millisecond of the time period.
388 *
389 * @throws NullPointerException if <code>calendar</code> is
390 * <code>null</code>.
391 */
392 public long getFirstMillisecond(Calendar calendar) {
393 int year = this.day.getYear();
394 int month = this.day.getMonth() - 1;
395 int day = this.day.getDayOfMonth();
396 calendar.clear();
397 calendar.set(year, month, day, this.hour, this.minute, this.second);
398 calendar.set(Calendar.MILLISECOND, this.millisecond);
399 //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
400 return calendar.getTime().getTime();
401 }
402
403 /**
404 * Returns the last millisecond of the time period.
405 *
406 * @param calendar the calendar (<code>null</code> not permitted).
407 *
408 * @return The last millisecond of the time period.
409 *
410 * @throws NullPointerException if <code>calendar</code> is
411 * <code>null</code>.
412 */
413 public long getLastMillisecond(Calendar calendar) {
414 return getFirstMillisecond(calendar);
415 }
416
417 }