001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 * 06-Jun-2008 : Added handling for general RegularTimePeriod in compareTo() 053 * method: 054 * see http://www.jfree.org/phpBB2/viewtopic.php?t=24805 (DG); 055 * 16-Sep-2008 : Deprecated DEFAULT_TIME_ZONE (DG); 056 * 057 */ 058 059 package org.jfree.data.time; 060 061 import java.io.Serializable; 062 import java.util.Calendar; 063 import java.util.Date; 064 import java.util.TimeZone; 065 066 /** 067 * Represents a millisecond. This class is immutable, which is a requirement 068 * for all {@link RegularTimePeriod} subclasses. 069 */ 070 public class Millisecond extends RegularTimePeriod implements Serializable { 071 072 /** For serialization. */ 073 static final long serialVersionUID = -5316836467277638485L; 074 075 /** A constant for the first millisecond in a second. */ 076 public static final int FIRST_MILLISECOND_IN_SECOND = 0; 077 078 /** A constant for the last millisecond in a second. */ 079 public static final int LAST_MILLISECOND_IN_SECOND = 999; 080 081 /** The day. */ 082 private Day day; 083 084 /** The hour in the day. */ 085 private byte hour; 086 087 /** The minute. */ 088 private byte minute; 089 090 /** The second. */ 091 private byte second; 092 093 /** The millisecond. */ 094 private int millisecond; 095 096 /** 097 * The pegged millisecond. 098 */ 099 private long firstMillisecond; 100 101 /** 102 * Constructs a millisecond based on the current system time. 103 */ 104 public Millisecond() { 105 this(new Date()); 106 } 107 108 /** 109 * Constructs a millisecond. 110 * 111 * @param millisecond the millisecond (0-999). 112 * @param second the second. 113 */ 114 public Millisecond(int millisecond, Second second) { 115 this.millisecond = millisecond; 116 this.second = (byte) second.getSecond(); 117 this.minute = (byte) second.getMinute().getMinute(); 118 this.hour = (byte) second.getMinute().getHourValue(); 119 this.day = second.getMinute().getDay(); 120 peg(Calendar.getInstance()); 121 } 122 123 /** 124 * Creates a new millisecond. 125 * 126 * @param millisecond the millisecond (0-999). 127 * @param second the second (0-59). 128 * @param minute the minute (0-59). 129 * @param hour the hour (0-23). 130 * @param day the day (1-31). 131 * @param month the month (1-12). 132 * @param year the year (1900-9999). 133 */ 134 public Millisecond(int millisecond, int second, int minute, int hour, 135 int day, int month, int year) { 136 137 this(millisecond, new Second(second, minute, hour, day, month, year)); 138 139 } 140 141 /** 142 * Constructs a new millisecond using the default time zone. 143 * 144 * @param time the time. 145 * 146 * @see #Millisecond(Date, TimeZone) 147 */ 148 public Millisecond(Date time) { 149 this(time, TimeZone.getDefault()); 150 } 151 152 /** 153 * Creates a millisecond. 154 * 155 * @param time the instant in time. 156 * @param zone the time zone. 157 */ 158 public Millisecond(Date time, TimeZone zone) { 159 // FIXME: need a locale as well as a timezone 160 Calendar calendar = Calendar.getInstance(zone); 161 calendar.setTime(time); 162 this.millisecond = calendar.get(Calendar.MILLISECOND); 163 this.second = (byte) calendar.get(Calendar.SECOND); 164 this.minute = (byte) calendar.get(Calendar.MINUTE); 165 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); 166 this.day = new Day(time, zone); 167 peg(calendar); 168 } 169 170 /** 171 * Returns the second. 172 * 173 * @return The second. 174 */ 175 public Second getSecond() { 176 return new Second(this.second, this.minute, this.hour, 177 this.day.getDayOfMonth(), this.day.getMonth(), 178 this.day.getYear()); 179 } 180 181 /** 182 * Returns the millisecond. 183 * 184 * @return The millisecond. 185 */ 186 public long getMillisecond() { 187 return this.millisecond; 188 } 189 190 /** 191 * Returns the first millisecond of the second. This will be determined 192 * relative to the time zone specified in the constructor, or in the 193 * calendar instance passed in the most recent call to the 194 * {@link #peg(Calendar)} method. 195 * 196 * @return The first millisecond of the second. 197 * 198 * @see #getLastMillisecond() 199 */ 200 public long getFirstMillisecond() { 201 return this.firstMillisecond; 202 } 203 204 /** 205 * Returns the last millisecond of the second. This will be 206 * determined relative to the time zone specified in the constructor, or 207 * in the calendar instance passed in the most recent call to the 208 * {@link #peg(Calendar)} method. 209 * 210 * @return The last millisecond of the second. 211 * 212 * @see #getFirstMillisecond() 213 */ 214 public long getLastMillisecond() { 215 return this.firstMillisecond; 216 } 217 218 /** 219 * Recalculates the start date/time and end date/time for this time period 220 * relative to the supplied calendar (which incorporates a time zone). 221 * 222 * @param calendar the calendar (<code>null</code> not permitted). 223 * 224 * @since 1.0.3 225 */ 226 public void peg(Calendar calendar) { 227 this.firstMillisecond = getFirstMillisecond(calendar); 228 } 229 230 /** 231 * Returns the millisecond preceding this one. 232 * 233 * @return The millisecond preceding this one. 234 */ 235 public RegularTimePeriod previous() { 236 237 RegularTimePeriod result = null; 238 239 if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) { 240 result = new Millisecond(this.millisecond - 1, getSecond()); 241 } 242 else { 243 Second previous = (Second) getSecond().previous(); 244 if (previous != null) { 245 result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous); 246 } 247 } 248 return result; 249 250 } 251 252 /** 253 * Returns the millisecond following this one. 254 * 255 * @return The millisecond following this one. 256 */ 257 public RegularTimePeriod next() { 258 259 RegularTimePeriod result = null; 260 if (this.millisecond != LAST_MILLISECOND_IN_SECOND) { 261 result = new Millisecond(this.millisecond + 1, getSecond()); 262 } 263 else { 264 Second next = (Second) getSecond().next(); 265 if (next != null) { 266 result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next); 267 } 268 } 269 return result; 270 271 } 272 273 /** 274 * Returns a serial index number for the millisecond. 275 * 276 * @return The serial index number. 277 */ 278 public long getSerialIndex() { 279 long hourIndex = this.day.getSerialIndex() * 24L + this.hour; 280 long minuteIndex = hourIndex * 60L + this.minute; 281 long secondIndex = minuteIndex * 60L + this.second; 282 return secondIndex * 1000L + this.millisecond; 283 } 284 285 /** 286 * Tests the equality of this object against an arbitrary Object. 287 * <P> 288 * This method will return true ONLY if the object is a Millisecond object 289 * representing the same millisecond as this instance. 290 * 291 * @param obj the object to compare 292 * 293 * @return <code>true</code> if milliseconds and seconds of this and object 294 * are the same. 295 */ 296 public boolean equals(Object obj) { 297 if (obj == this) { 298 return true; 299 } 300 if (!(obj instanceof Millisecond)) { 301 return false; 302 } 303 Millisecond that = (Millisecond) obj; 304 if (this.millisecond != that.millisecond) { 305 return false; 306 } 307 if (this.second != that.second) { 308 return false; 309 } 310 if (this.minute != that.minute) { 311 return false; 312 } 313 if (this.hour != that.hour) { 314 return false; 315 } 316 if (!this.day.equals(that.day)) { 317 return false; 318 } 319 return true; 320 } 321 322 /** 323 * Returns a hash code for this object instance. The approach described by 324 * Joshua Bloch in "Effective Java" has been used here: 325 * <p> 326 * <code>http://developer.java.sun.com/developer/Books/effectivejava 327 * /Chapter3.pdf</code> 328 * 329 * @return A hashcode. 330 */ 331 public int hashCode() { 332 int result = 17; 333 result = 37 * result + this.millisecond; 334 result = 37 * result + getSecond().hashCode(); 335 return result; 336 } 337 338 /** 339 * Returns an integer indicating the order of this Millisecond object 340 * relative to the specified object: 341 * 342 * negative == before, zero == same, positive == after. 343 * 344 * @param obj the object to compare 345 * 346 * @return negative == before, zero == same, positive == after. 347 */ 348 public int compareTo(Object obj) { 349 350 int result; 351 long difference; 352 353 // CASE 1 : Comparing to another Second object 354 // ------------------------------------------- 355 if (obj instanceof Millisecond) { 356 Millisecond ms = (Millisecond) obj; 357 difference = getFirstMillisecond() - ms.getFirstMillisecond(); 358 if (difference > 0) { 359 result = 1; 360 } 361 else { 362 if (difference < 0) { 363 result = -1; 364 } 365 else { 366 result = 0; 367 } 368 } 369 } 370 371 // CASE 2 : Comparing to another TimePeriod object 372 // ----------------------------------------------- 373 else if (obj instanceof RegularTimePeriod) { 374 RegularTimePeriod rtp = (RegularTimePeriod) obj; 375 final long thisVal = this.getFirstMillisecond(); 376 final long anotherVal = rtp.getFirstMillisecond(); 377 result = (thisVal < anotherVal ? -1 378 : (thisVal == anotherVal ? 0 : 1)); 379 } 380 381 // CASE 3 : Comparing to a non-TimePeriod object 382 // --------------------------------------------- 383 else { 384 // consider time periods to be ordered after general objects 385 result = 1; 386 } 387 388 return result; 389 390 } 391 392 /** 393 * Returns the first millisecond of the time period. 394 * 395 * @param calendar the calendar (<code>null</code> not permitted). 396 * 397 * @return The first millisecond of the time period. 398 * 399 * @throws NullPointerException if <code>calendar</code> is 400 * <code>null</code>. 401 */ 402 public long getFirstMillisecond(Calendar calendar) { 403 int year = this.day.getYear(); 404 int month = this.day.getMonth() - 1; 405 int day = this.day.getDayOfMonth(); 406 calendar.clear(); 407 calendar.set(year, month, day, this.hour, this.minute, this.second); 408 calendar.set(Calendar.MILLISECOND, this.millisecond); 409 //return calendar.getTimeInMillis(); // this won't work for JDK 1.3 410 return calendar.getTime().getTime(); 411 } 412 413 /** 414 * Returns the last millisecond of the time period. 415 * 416 * @param calendar the calendar (<code>null</code> not permitted). 417 * 418 * @return The last millisecond of the time period. 419 * 420 * @throws NullPointerException if <code>calendar</code> is 421 * <code>null</code>. 422 */ 423 public long getLastMillisecond(Calendar calendar) { 424 return getFirstMillisecond(calendar); 425 } 426 427 }