001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2006, 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 * Second.java 029 * ----------- 030 * (C) Copyright 2001-2006, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: Second.java,v 1.6.2.3 2006/10/06 14:00:15 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 11-Oct-2001 : Version 1 (DG); 040 * 18-Dec-2001 : Changed order of parameters in constructor (DG); 041 * 19-Dec-2001 : Added a new constructor as suggested by Paul English (DG); 042 * 14-Feb-2002 : Fixed bug in Second(Date) constructor, and changed start of 043 * range to zero from one (DG); 044 * 26-Feb-2002 : Changed getStart(), getMiddle() and getEnd() methods to 045 * evaluate with reference to a particular time zone (DG); 046 * 13-Mar-2002 : Added parseSecond() method (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 * 05-Mar-2003 : Fixed bug in getLastMillisecond() picked up in JUnit 051 * tests (DG); 052 * 13-Mar-2003 : Moved to com.jrefinery.data.time package and implemented 053 * Serializable (DG); 054 * 21-Oct-2003 : Added hashCode() method (DG); 055 * ------------- JFREECHART 1.0.x --------------------------------------------- 056 * 05-Oct-2006 : Updated API docs (DG); 057 * 06-Oct-2006 : Refactored to cache first and last millisecond values (DG); 058 * 059 */ 060 061 package org.jfree.data.time; 062 063 import java.io.Serializable; 064 import java.util.Calendar; 065 import java.util.Date; 066 import java.util.TimeZone; 067 068 /** 069 * Represents a second in a particular day. This class is immutable, which is 070 * a requirement for all {@link RegularTimePeriod} subclasses. 071 */ 072 public class Second extends RegularTimePeriod implements Serializable { 073 074 /** For serialization. */ 075 private static final long serialVersionUID = -6536564190712383466L; 076 077 /** Useful constant for the first second in a minute. */ 078 public static final int FIRST_SECOND_IN_MINUTE = 0; 079 080 /** Useful constant for the last second in a minute. */ 081 public static final int LAST_SECOND_IN_MINUTE = 59; 082 083 /** The day. */ 084 private Day day; 085 086 /** The hour of the day. */ 087 private byte hour; 088 089 /** The minute. */ 090 private byte minute; 091 092 /** The second. */ 093 private byte second; 094 095 /** 096 * The first millisecond. We don't store the last millisecond, because it 097 * is always firstMillisecond + 999L. 098 */ 099 private long firstMillisecond; 100 101 /** 102 * Constructs a new Second, based on the system date/time. 103 */ 104 public Second() { 105 this(new Date()); 106 } 107 108 /** 109 * Constructs a new Second. 110 * 111 * @param second the second (0 to 24*60*60-1). 112 * @param minute the minute (<code>null</code> not permitted). 113 */ 114 public Second(int second, Minute minute) { 115 if (minute == null) { 116 throw new IllegalArgumentException("Null 'minute' argument."); 117 } 118 this.day = minute.getDay(); 119 this.hour = (byte) minute.getHourValue(); 120 this.minute = (byte) minute.getMinute(); 121 this.second = (byte) second; 122 peg(Calendar.getInstance()); 123 } 124 125 /** 126 * Creates a new second. 127 * 128 * @param second the second (0-59). 129 * @param minute the minute (0-59). 130 * @param hour the hour (0-23). 131 * @param day the day (1-31). 132 * @param month the month (1-12). 133 * @param year the year (1900-9999). 134 */ 135 public Second(int second, int minute, int hour, 136 int day, int month, int year) { 137 this(second, new Minute(minute, hour, day, month, year)); 138 } 139 140 /** 141 * Constructs a second. 142 * 143 * @param time the time. 144 */ 145 public Second(Date time) { 146 this(time, RegularTimePeriod.DEFAULT_TIME_ZONE); 147 } 148 149 /** 150 * Creates a new second based on the supplied time and time zone. 151 * 152 * @param time the instant in time. 153 * @param zone the time zone. 154 */ 155 public Second(Date time, final TimeZone zone) { 156 Calendar calendar = Calendar.getInstance(zone); 157 calendar.setTime(time); 158 this.second = (byte) calendar.get(Calendar.SECOND); 159 this.minute = (byte) calendar.get(Calendar.MINUTE); 160 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); 161 this.day = new Day(time, zone); 162 peg(calendar); 163 } 164 165 /** 166 * Returns the second within the minute. 167 * 168 * @return The second (0 - 59). 169 */ 170 public int getSecond() { 171 return this.second; 172 } 173 174 /** 175 * Returns the minute. 176 * 177 * @return The minute (never <code>null</code>). 178 */ 179 public Minute getMinute() { 180 return new Minute(this.minute, new Hour(this.hour, this.day)); 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 + 999L; 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 second preceding this one. 225 * 226 * @return The second preceding this one. 227 */ 228 public RegularTimePeriod previous() { 229 230 Second result = null; 231 if (this.second != FIRST_SECOND_IN_MINUTE) { 232 result = new Second(this.second - 1, getMinute()); 233 } 234 else { 235 Minute previous = (Minute) getMinute().previous(); 236 if (previous != null) { 237 result = new Second(LAST_SECOND_IN_MINUTE, previous); 238 } 239 } 240 return result; 241 242 } 243 244 /** 245 * Returns the second following this one. 246 * 247 * @return The second following this one. 248 */ 249 public RegularTimePeriod next() { 250 251 Second result = null; 252 if (this.second != LAST_SECOND_IN_MINUTE) { 253 result = new Second(this.second + 1, getMinute()); 254 } 255 else { 256 Minute next = (Minute) getMinute().next(); 257 if (next != null) { 258 result = new Second(FIRST_SECOND_IN_MINUTE, next); 259 } 260 } 261 return result; 262 263 } 264 265 /** 266 * Returns a serial index number for the minute. 267 * 268 * @return The serial index number. 269 */ 270 public long getSerialIndex() { 271 long hourIndex = this.day.getSerialIndex() * 24L + this.hour; 272 long minuteIndex = hourIndex * 60L + this.minute; 273 return minuteIndex * 60L + this.second; 274 } 275 276 /** 277 * Returns the first millisecond of the minute. 278 * 279 * @param calendar the calendar/timezone (<code>null</code> not permitted). 280 * 281 * @return The first millisecond. 282 * 283 * @throws NullPointerException if <code>calendar</code> is 284 * <code>null</code>. 285 */ 286 public long getFirstMillisecond(Calendar calendar) { 287 int year = this.day.getYear(); 288 int month = this.day.getMonth() - 1; 289 int day = this.day.getDayOfMonth(); 290 calendar.clear(); 291 calendar.set(year, month, day, this.hour, this.minute, this.second); 292 calendar.set(Calendar.MILLISECOND, 0); 293 //return calendar.getTimeInMillis(); // this won't work for JDK 1.3 294 return calendar.getTime().getTime(); 295 } 296 297 /** 298 * Returns the last millisecond of the second. 299 * 300 * @param calendar the calendar/timezone (<code>null</code> not permitted). 301 * 302 * @return The last millisecond. 303 * 304 * @throws NullPointerException if <code>calendar</code> is 305 * <code>null</code>. 306 */ 307 public long getLastMillisecond(Calendar calendar) { 308 return getFirstMillisecond(calendar) + 999L; 309 } 310 311 /** 312 * Tests the equality of this object against an arbitrary Object. 313 * <P> 314 * This method will return true ONLY if the object is a Second object 315 * representing the same second as this instance. 316 * 317 * @param obj the object to compare (<code>null</code> permitted). 318 * 319 * @return <code>true</code> if second and minute of this and the object 320 * are the same. 321 */ 322 public boolean equals(Object obj) { 323 if (obj == this) { 324 return true; 325 } 326 if (!(obj instanceof Second)) { 327 return false; 328 } 329 Second that = (Second) obj; 330 if (this.second != that.second) { 331 return false; 332 } 333 if (this.minute != that.minute) { 334 return false; 335 } 336 if (this.hour != that.hour) { 337 return false; 338 } 339 if (!this.day.equals(that.day)) { 340 return false; 341 } 342 return true; 343 } 344 345 /** 346 * Returns a hash code for this object instance. The approach described by 347 * Joshua Bloch in "Effective Java" has been used here: 348 * <p> 349 * <code>http://developer.java.sun.com/developer/Books/effectivejava 350 * /Chapter3.pdf</code> 351 * 352 * @return A hash code. 353 */ 354 public int hashCode() { 355 int result = 17; 356 result = 37 * result + this.second; 357 result = 37 * result + this.minute; 358 result = 37 * result + this.hour; 359 result = 37 * result + this.day.hashCode(); 360 return result; 361 } 362 363 /** 364 * Returns an integer indicating the order of this Second object relative 365 * to the specified 366 * object: negative == before, zero == same, positive == after. 367 * 368 * @param o1 the object to compare. 369 * 370 * @return negative == before, zero == same, positive == after. 371 */ 372 public int compareTo(Object o1) { 373 374 int result; 375 376 // CASE 1 : Comparing to another Second object 377 // ------------------------------------------- 378 if (o1 instanceof Second) { 379 Second s = (Second) o1; 380 if (this.firstMillisecond < s.firstMillisecond) { 381 return -1; 382 } 383 else if (this.firstMillisecond > s.firstMillisecond) { 384 return 1; 385 } 386 else { 387 return 0; 388 } 389 } 390 391 // CASE 2 : Comparing to another TimePeriod object 392 // ----------------------------------------------- 393 else if (o1 instanceof RegularTimePeriod) { 394 // more difficult case - evaluate later... 395 result = 0; 396 } 397 398 // CASE 3 : Comparing to a non-TimePeriod object 399 // --------------------------------------------- 400 else { 401 // consider time periods to be ordered after general objects 402 result = 1; 403 } 404 405 return result; 406 407 } 408 409 /** 410 * Creates a new instance by parsing a string. The string is assumed to 411 * be in the format "YYYY-MM-DD HH:MM:SS", perhaps with leading or trailing 412 * whitespace. 413 * 414 * @param s the string to parse. 415 * 416 * @return The second, or <code>null</code> if the string is not parseable. 417 */ 418 public static Second parseSecond(String s) { 419 420 Second result = null; 421 s = s.trim(); 422 423 String daystr = s.substring(0, Math.min(10, s.length())); 424 Day day = Day.parseDay(daystr); 425 if (day != null) { 426 String hmsstr = s.substring( 427 Math.min(daystr.length() + 1, s.length()), s.length() 428 ); 429 hmsstr = hmsstr.trim(); 430 431 int l = hmsstr.length(); 432 String hourstr = hmsstr.substring(0, Math.min(2, l)); 433 String minstr = hmsstr.substring(Math.min(3, l), Math.min(5, l)); 434 String secstr = hmsstr.substring(Math.min(6, l), Math.min(8, l)); 435 int hour = Integer.parseInt(hourstr); 436 437 if ((hour >= 0) && (hour <= 23)) { 438 439 int minute = Integer.parseInt(minstr); 440 if ((minute >= 0) && (minute <= 59)) { 441 442 Minute m = new Minute(minute, new Hour(hour, day)); 443 int second = Integer.parseInt(secstr); 444 if ((second >= 0) && (second <= 59)) { 445 result = new Second(second, m); 446 } 447 } 448 } 449 } 450 451 return result; 452 453 } 454 455 }