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 * Minute.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 * 18-Dec-2001 : Changed order of parameters in constructor (DG);
039 * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG);
040 * 14-Feb-2002 : Fixed bug in Minute(Date) constructor, and changed the range
041 * to start from zero instead of one (DG);
042 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to
043 * evaluate with reference to a particular time zone (DG);
044 * 13-Mar-2002 : Added parseMinute() method (DG);
045 * 19-Mar-2002 : Changed API, the minute is now defined in relation to an
046 * Hour (DG);
047 * 10-Sep-2002 : Added getSerialIndex() method (DG);
048 * 07-Oct-2002 : Fixed errors reported by Checkstyle (DG);
049 * 10-Jan-2003 : Changed base class and method names (DG);
050 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented
051 * Serializable (DG);
052 * 21-Oct-2003 : Added hashCode() method, and new constructor for
053 * convenience (DG);
054 * 30-Sep-2004 : Replaced getTime().getTime() with getTimeInMillis() (DG);
055 * 04-Nov-2004 : Reverted change of 30-Sep-2004, because it won't work for
056 * JDK 1.3 (DG);
057 * ------------- JFREECHART 1.0.x ---------------------------------------------
058 * 05-Oct-2006 : Updated API docs (DG);
059 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG);
060 * 11-Dec-2006 : Fix for previous() - bug 1611872 (DG);
061 *
062 */
063
064 package org.jfree.data.time;
065
066 import java.io.Serializable;
067 import java.util.Calendar;
068 import java.util.Date;
069 import java.util.TimeZone;
070
071 /**
072 * Represents a minute. This class is immutable, which is a requirement for
073 * all {@link RegularTimePeriod} subclasses.
074 */
075 public class Minute extends RegularTimePeriod implements Serializable {
076
077 /** For serialization. */
078 private static final long serialVersionUID = 2144572840034842871L;
079
080 /** Useful constant for the first minute in a day. */
081 public static final int FIRST_MINUTE_IN_HOUR = 0;
082
083 /** Useful constant for the last minute in a day. */
084 public static final int LAST_MINUTE_IN_HOUR = 59;
085
086 /** The day. */
087 private Day day;
088
089 /** The hour in which the minute falls. */
090 private byte hour;
091
092 /** The minute. */
093 private byte minute;
094
095 /** The first millisecond. */
096 private long firstMillisecond;
097
098 /** The last millisecond. */
099 private long lastMillisecond;
100
101 /**
102 * Constructs a new Minute, based on the system date/time.
103 */
104 public Minute() {
105 this(new Date());
106 }
107
108 /**
109 * Constructs a new Minute.
110 *
111 * @param minute the minute (0 to 59).
112 * @param hour the hour (<code>null</code> not permitted).
113 */
114 public Minute(int minute, Hour hour) {
115 if (hour == null) {
116 throw new IllegalArgumentException("Null 'hour' argument.");
117 }
118 this.minute = (byte) minute;
119 this.hour = (byte) hour.getHour();
120 this.day = hour.getDay();
121 peg(Calendar.getInstance());
122 }
123
124 /**
125 * Constructs a new Minute, based on the supplied date/time.
126 *
127 * @param time the time (<code>null</code> not permitted).
128 */
129 public Minute(Date time) {
130 // defer argument checking
131 this(time, RegularTimePeriod.DEFAULT_TIME_ZONE);
132 }
133
134 /**
135 * Constructs a new Minute, based on the supplied date/time and timezone.
136 *
137 * @param time the time (<code>null</code> not permitted).
138 * @param zone the time zone (<code>null</code> not permitted).
139 */
140 public Minute(Date time, TimeZone zone) {
141 if (time == null) {
142 throw new IllegalArgumentException("Null 'time' argument.");
143 }
144 if (zone == null) {
145 throw new IllegalArgumentException("Null 'zone' argument.");
146 }
147 Calendar calendar = Calendar.getInstance(zone);
148 calendar.setTime(time);
149 int min = calendar.get(Calendar.MINUTE);
150 this.minute = (byte) min;
151 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY);
152 this.day = new Day(time, zone);
153 peg(calendar);
154 }
155
156 /**
157 * Creates a new minute.
158 *
159 * @param minute the minute (0-59).
160 * @param hour the hour (0-23).
161 * @param day the day (1-31).
162 * @param month the month (1-12).
163 * @param year the year (1900-9999).
164 */
165 public Minute(int minute,
166 int hour,
167 int day,
168 int month,
169 int year) {
170 this(minute, new Hour(hour, new Day(day, month, year)));
171 }
172
173 /**
174 * Returns the day.
175 *
176 * @return The day.
177 *
178 * @since 1.0.3
179 */
180 public Day getDay() {
181 return this.day;
182 }
183
184 /**
185 * Returns the hour.
186 *
187 * @return The hour (never <code>null</code>).
188 */
189 public Hour getHour() {
190 return new Hour(this.hour, this.day);
191 }
192
193 /**
194 * Returns the hour.
195 *
196 * @return The hour.
197 *
198 * @since 1.0.3
199 */
200 public int getHourValue() {
201 return this.hour;
202 }
203
204 /**
205 * Returns the minute.
206 *
207 * @return The minute.
208 */
209 public int getMinute() {
210 return this.minute;
211 }
212
213 /**
214 * Returns the first millisecond of the minute. This will be determined
215 * relative to the time zone specified in the constructor, or in the
216 * calendar instance passed in the most recent call to the
217 * {@link #peg(Calendar)} method.
218 *
219 * @return The first millisecond of the minute.
220 *
221 * @see #getLastMillisecond()
222 */
223 public long getFirstMillisecond() {
224 return this.firstMillisecond;
225 }
226
227 /**
228 * Returns the last millisecond of the minute. This will be
229 * determined relative to the time zone specified in the constructor, or
230 * in the calendar instance passed in the most recent call to the
231 * {@link #peg(Calendar)} method.
232 *
233 * @return The last millisecond of the minute.
234 *
235 * @see #getFirstMillisecond()
236 */
237 public long getLastMillisecond() {
238 return this.lastMillisecond;
239 }
240
241 /**
242 * Recalculates the start date/time and end date/time for this time period
243 * relative to the supplied calendar (which incorporates a time zone).
244 *
245 * @param calendar the calendar (<code>null</code> not permitted).
246 *
247 * @since 1.0.3
248 */
249 public void peg(Calendar calendar) {
250 this.firstMillisecond = getFirstMillisecond(calendar);
251 this.lastMillisecond = getLastMillisecond(calendar);
252 }
253
254 /**
255 * Returns the minute preceding this one.
256 *
257 * @return The minute preceding this one.
258 */
259 public RegularTimePeriod previous() {
260 Minute result;
261 if (this.minute != FIRST_MINUTE_IN_HOUR) {
262 result = new Minute(this.minute - 1, getHour());
263 }
264 else {
265 Hour h = (Hour) getHour().previous();
266 if (h != null) {
267 result = new Minute(LAST_MINUTE_IN_HOUR, h);
268 }
269 else {
270 result = null;
271 }
272 }
273 return result;
274 }
275
276 /**
277 * Returns the minute following this one.
278 *
279 * @return The minute following this one.
280 */
281 public RegularTimePeriod next() {
282
283 Minute result;
284 if (this.minute != LAST_MINUTE_IN_HOUR) {
285 result = new Minute(this.minute + 1, getHour());
286 }
287 else { // we are at the last minute in the hour...
288 Hour nextHour = (Hour) getHour().next();
289 if (nextHour != null) {
290 result = new Minute(FIRST_MINUTE_IN_HOUR, nextHour);
291 }
292 else {
293 result = null;
294 }
295 }
296 return result;
297
298 }
299
300 /**
301 * Returns a serial index number for the minute.
302 *
303 * @return The serial index number.
304 */
305 public long getSerialIndex() {
306 long hourIndex = this.day.getSerialIndex() * 24L + this.hour;
307 return hourIndex * 60L + this.minute;
308 }
309
310 /**
311 * Returns the first millisecond of the minute.
312 *
313 * @param calendar the calendar which defines the timezone
314 * (<code>null</code> not permitted).
315 *
316 * @return The first millisecond.
317 *
318 * @throws NullPointerException if <code>calendar</code> is
319 * <code>null</code>.
320 */
321 public long getFirstMillisecond(Calendar calendar) {
322
323 int year = this.day.getYear();
324 int month = this.day.getMonth() - 1;
325 int day = this.day.getDayOfMonth();
326
327 calendar.clear();
328 calendar.set(year, month, day, this.hour, this.minute, 0);
329 calendar.set(Calendar.MILLISECOND, 0);
330
331 //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
332 return calendar.getTime().getTime();
333
334 }
335
336 /**
337 * Returns the last millisecond of the minute.
338 *
339 * @param calendar the calendar / timezone (<code>null</code> not
340 * permitted).
341 *
342 * @return The last millisecond.
343 *
344 * @throws NullPointerException if <code>calendar</code> is
345 * <code>null</code>.
346 */
347 public long getLastMillisecond(Calendar calendar) {
348
349 int year = this.day.getYear();
350 int month = this.day.getMonth() - 1;
351 int day = this.day.getDayOfMonth();
352
353 calendar.clear();
354 calendar.set(year, month, day, this.hour, this.minute, 59);
355 calendar.set(Calendar.MILLISECOND, 999);
356
357 //return calendar.getTimeInMillis(); // this won't work for JDK 1.3
358 return calendar.getTime().getTime();
359
360 }
361
362 /**
363 * Tests the equality of this object against an arbitrary Object.
364 * <P>
365 * This method will return true ONLY if the object is a Minute object
366 * representing the same minute as this instance.
367 *
368 * @param obj the object to compare (<code>null</code> permitted).
369 *
370 * @return <code>true</code> if the minute and hour value of this and the
371 * object are the same.
372 */
373 public boolean equals(Object obj) {
374 if (obj == this) {
375 return true;
376 }
377 if (!(obj instanceof Minute)) {
378 return false;
379 }
380 Minute that = (Minute) obj;
381 if (this.minute != that.minute) {
382 return false;
383 }
384 if (this.hour != that.hour) {
385 return false;
386 }
387 return true;
388 }
389
390 /**
391 * Returns a hash code for this object instance. The approach described
392 * by Joshua Bloch in "Effective Java" has been used here:
393 * <p>
394 * <code>http://developer.java.sun.com/developer/Books/effectivejava
395 * /Chapter3.pdf</code>
396 *
397 * @return A hash code.
398 */
399 public int hashCode() {
400 int result = 17;
401 result = 37 * result + this.minute;
402 result = 37 * result + this.hour;
403 result = 37 * result + this.day.hashCode();
404 return result;
405 }
406
407 /**
408 * Returns an integer indicating the order of this Minute object relative
409 * to the specified object:
410 *
411 * negative == before, zero == same, positive == after.
412 *
413 * @param o1 object to compare.
414 *
415 * @return negative == before, zero == same, positive == after.
416 */
417 public int compareTo(Object o1) {
418
419 int result;
420
421 // CASE 1 : Comparing to another Minute object
422 // -------------------------------------------
423 if (o1 instanceof Minute) {
424 Minute m = (Minute) o1;
425 result = getHour().compareTo(m.getHour());
426 if (result == 0) {
427 result = this.minute - m.getMinute();
428 }
429 }
430
431 // CASE 2 : Comparing to another TimePeriod object
432 // -----------------------------------------------
433 else if (o1 instanceof RegularTimePeriod) {
434 // more difficult case - evaluate later...
435 result = 0;
436 }
437
438 // CASE 3 : Comparing to a non-TimePeriod object
439 // ---------------------------------------------
440 else {
441 // consider time periods to be ordered after general objects
442 result = 1;
443 }
444
445 return result;
446
447 }
448
449 /**
450 * Creates a Minute instance by parsing a string. The string is assumed to
451 * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing
452 * whitespace.
453 *
454 * @param s the minute string to parse.
455 *
456 * @return <code>null</code>, if the string is not parseable, the minute
457 * otherwise.
458 */
459 public static Minute parseMinute(String s) {
460
461 Minute result = null;
462 s = s.trim();
463
464 String daystr = s.substring(0, Math.min(10, s.length()));
465 Day day = Day.parseDay(daystr);
466 if (day != null) {
467 String hmstr = s.substring(
468 Math.min(daystr.length() + 1, s.length()), s.length()
469 );
470 hmstr = hmstr.trim();
471
472 String hourstr = hmstr.substring(0, Math.min(2, hmstr.length()));
473 int hour = Integer.parseInt(hourstr);
474
475 if ((hour >= 0) && (hour <= 23)) {
476 String minstr = hmstr.substring(
477 Math.min(hourstr.length() + 1, hmstr.length()),
478 hmstr.length()
479 );
480 int minute = Integer.parseInt(minstr);
481 if ((minute >= 0) && (minute <= 59)) {
482 result = new Minute(minute, new Hour(hour, day));
483 }
484 }
485 }
486
487 return result;
488
489 }
490
491 }