001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, 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-2022, 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.internal.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 * @since 1.0.13 145 */ 146 public Second(Date time, TimeZone zone, Locale locale) { 147 this(time, Calendar.getInstance(zone, locale)); 148 } 149 150 /** 151 * Constructs a new instance, based on a particular date/time. 152 * The time zone and locale are determined by the {@code calendar} 153 * parameter. 154 * 155 * @param time the date/time ({@code null} not permitted). 156 * @param calendar the calendar to use for calculations ({@code null} not permitted). 157 */ 158 public Second(Date time, Calendar calendar) { 159 calendar.setTime(time); 160 this.second = (byte) calendar.get(Calendar.SECOND); 161 this.minute = (byte) calendar.get(Calendar.MINUTE); 162 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); 163 this.day = new Day(time, calendar); 164 peg(calendar); 165 } 166 167 /** 168 * Returns the second within the minute. 169 * 170 * @return The second (0 - 59). 171 */ 172 public int getSecond() { 173 return this.second; 174 } 175 176 /** 177 * Returns the minute. 178 * 179 * @return The minute (never {@code null}). 180 */ 181 public Minute getMinute() { 182 return new Minute(this.minute, new Hour(this.hour, this.day)); 183 } 184 185 /** 186 * Returns the first millisecond of the second. This will be determined 187 * relative to the time zone specified in the constructor, or in the 188 * calendar instance passed in the most recent call to the 189 * {@link #peg(Calendar)} method. 190 * 191 * @return The first millisecond of the second. 192 * 193 * @see #getLastMillisecond() 194 */ 195 @Override 196 public long getFirstMillisecond() { 197 return this.firstMillisecond; 198 } 199 200 /** 201 * Returns the last millisecond of the second. This will be 202 * determined relative to the time zone specified in the constructor, or 203 * in the calendar instance passed in the most recent call to the 204 * {@link #peg(Calendar)} method. 205 * 206 * @return The last millisecond of the second. 207 * 208 * @see #getFirstMillisecond() 209 */ 210 @Override 211 public long getLastMillisecond() { 212 return this.firstMillisecond + 999L; 213 } 214 215 /** 216 * Recalculates the start date/time and end date/time for this time period 217 * relative to the supplied calendar (which incorporates a time zone). 218 * 219 * @param calendar the calendar ({@code null} not permitted). 220 * 221 * @since 1.0.3 222 */ 223 @Override 224 public void peg(Calendar calendar) { 225 this.firstMillisecond = getFirstMillisecond(calendar); 226 } 227 228 /** 229 * Returns the second preceding this one. 230 * No matter what time zone and locale this instance was created with, 231 * the returned instance will use the default calendar for time 232 * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. 233 * 234 * @return The second preceding this one. 235 */ 236 @Override 237 public RegularTimePeriod previous() { 238 Second result = null; 239 if (this.second != FIRST_SECOND_IN_MINUTE) { 240 result = new Second(this.second - 1, getMinute()); 241 } 242 else { 243 Minute previous = (Minute) getMinute().previous(); 244 if (previous != null) { 245 result = new Second(LAST_SECOND_IN_MINUTE, previous); 246 } 247 } 248 return result; 249 } 250 251 /** 252 * Returns the second following this one. 253 * No matter what time zone and locale this instance was created with, 254 * the returned instance will use the default calendar for time 255 * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. 256 * 257 * @return The second following this one. 258 */ 259 @Override 260 public RegularTimePeriod next() { 261 Second result = null; 262 if (this.second != LAST_SECOND_IN_MINUTE) { 263 result = new Second(this.second + 1, getMinute()); 264 } 265 else { 266 Minute next = (Minute) getMinute().next(); 267 if (next != null) { 268 result = new Second(FIRST_SECOND_IN_MINUTE, next); 269 } 270 } 271 return result; 272 } 273 274 /** 275 * Returns a serial index number for the minute. 276 * 277 * @return The serial index number. 278 */ 279 @Override 280 public long getSerialIndex() { 281 long hourIndex = this.day.getSerialIndex() * 24L + this.hour; 282 long minuteIndex = hourIndex * 60L + this.minute; 283 return minuteIndex * 60L + this.second; 284 } 285 286 /** 287 * Returns the first millisecond of the minute. 288 * 289 * @param calendar the calendar/timezone ({@code null} not permitted). 290 * 291 * @return The first millisecond. 292 * 293 * @throws NullPointerException if {@code calendar} is {@code null}. 294 */ 295 @Override 296 public long getFirstMillisecond(Calendar calendar) { 297 int year = this.day.getYear(); 298 int month = this.day.getMonth() - 1; 299 int d = this.day.getDayOfMonth(); 300 calendar.clear(); 301 calendar.set(year, month, d, this.hour, this.minute, this.second); 302 calendar.set(Calendar.MILLISECOND, 0); 303 return calendar.getTimeInMillis(); 304 } 305 306 /** 307 * Returns the last millisecond of the second. 308 * 309 * @param calendar the calendar/timezone ({@code null} not permitted). 310 * 311 * @return The last millisecond. 312 * 313 * @throws NullPointerException if {@code calendar} is {@code null}. 314 */ 315 @Override 316 public long getLastMillisecond(Calendar calendar) { 317 return getFirstMillisecond(calendar) + 999L; 318 } 319 320 /** 321 * Tests the equality of this object against an arbitrary Object. 322 * <P> 323 * This method will return true ONLY if the object is a Second object 324 * representing the same second as this instance. 325 * 326 * @param obj the object to compare ({@code null} permitted). 327 * 328 * @return {@code true} if second and minute of this and the object 329 * are the same. 330 */ 331 @Override 332 public boolean equals(Object obj) { 333 if (obj == this) { 334 return true; 335 } 336 if (!(obj instanceof Second)) { 337 return false; 338 } 339 Second that = (Second) obj; 340 if (this.second != that.second) { 341 return false; 342 } 343 if (this.minute != that.minute) { 344 return false; 345 } 346 if (this.hour != that.hour) { 347 return false; 348 } 349 if (!this.day.equals(that.day)) { 350 return false; 351 } 352 return true; 353 } 354 355 /** 356 * Returns a hash code for this object instance. The approach described by 357 * Joshua Bloch in "Effective Java" has been used here: 358 * <p> 359 * {@code http://developer.java.sun.com/developer/Books/effectivejava 360 * /Chapter3.pdf} 361 * 362 * @return A hash code. 363 */ 364 @Override 365 public int hashCode() { 366 int result = 17; 367 result = 37 * result + this.second; 368 result = 37 * result + this.minute; 369 result = 37 * result + this.hour; 370 result = 37 * result + this.day.hashCode(); 371 return result; 372 } 373 374 /** 375 * Returns an integer indicating the order of this Second object relative 376 * to the specified 377 * object: negative == before, zero == same, positive == after. 378 * 379 * @param o1 the object to compare. 380 * 381 * @return negative == before, zero == same, positive == after. 382 */ 383 @Override 384 public int compareTo(Object o1) { 385 int result; 386 387 // CASE 1 : Comparing to another Second object 388 // ------------------------------------------- 389 if (o1 instanceof Second) { 390 Second s = (Second) o1; 391 if (this.firstMillisecond < s.firstMillisecond) { 392 return -1; 393 } 394 else if (this.firstMillisecond > s.firstMillisecond) { 395 return 1; 396 } 397 else { 398 return 0; 399 } 400 } 401 402 // CASE 2 : Comparing to another TimePeriod object 403 // ----------------------------------------------- 404 else if (o1 instanceof RegularTimePeriod) { 405 // more difficult case - evaluate later... 406 result = 0; 407 } 408 409 // CASE 3 : Comparing to a non-TimePeriod object 410 // --------------------------------------------- 411 else { 412 // consider time periods to be ordered after general objects 413 result = 1; 414 } 415 416 return result; 417 } 418 419 /** 420 * Creates a new instance by parsing a string. The string is assumed to 421 * be in the format "YYYY-MM-DD HH:MM:SS", perhaps with leading or trailing 422 * whitespace. 423 * 424 * @param s the string to parse. 425 * 426 * @return The second, or {@code null} if the string is not parseable. 427 */ 428 public static Second parseSecond(String s) { 429 Second result = null; 430 s = s.trim(); 431 String daystr = s.substring(0, Math.min(10, s.length())); 432 Day day = Day.parseDay(daystr); 433 if (day != null) { 434 String hmsstr = s.substring(Math.min(daystr.length() + 1, 435 s.length()), s.length()); 436 hmsstr = hmsstr.trim(); 437 438 int l = hmsstr.length(); 439 String hourstr = hmsstr.substring(0, Math.min(2, l)); 440 String minstr = hmsstr.substring(Math.min(3, l), Math.min(5, l)); 441 String secstr = hmsstr.substring(Math.min(6, l), Math.min(8, l)); 442 int hour = Integer.parseInt(hourstr); 443 444 if ((hour >= 0) && (hour <= 23)) { 445 446 int minute = Integer.parseInt(minstr); 447 if ((minute >= 0) && (minute <= 59)) { 448 449 Minute m = new Minute(minute, new Hour(hour, day)); 450 int second = Integer.parseInt(secstr); 451 if ((second >= 0) && (second <= 59)) { 452 result = new Second(second, m); 453 } 454 } 455 } 456 } 457 return result; 458 } 459 460}