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