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