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