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 * Millisecond.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 org.jfree.chart.internal.Args; 040 041import java.io.Serializable; 042import java.util.Calendar; 043import java.util.Date; 044import java.util.Locale; 045import java.util.TimeZone; 046 047/** 048 * Represents a millisecond. This class is immutable, which is a requirement 049 * for all {@link RegularTimePeriod} subclasses. 050 */ 051public class Millisecond extends RegularTimePeriod implements Serializable { 052 053 /** For serialization. */ 054 static final long serialVersionUID = -5316836467277638485L; 055 056 /** A constant for the first millisecond in a second. */ 057 public static final int FIRST_MILLISECOND_IN_SECOND = 0; 058 059 /** A constant for the last millisecond in a second. */ 060 public static final int LAST_MILLISECOND_IN_SECOND = 999; 061 062 /** The day. */ 063 private Day day; 064 065 /** The hour in the day. */ 066 private byte hour; 067 068 /** The minute. */ 069 private byte minute; 070 071 /** The second. */ 072 private byte second; 073 074 /** The millisecond. */ 075 private int millisecond; 076 077 /** 078 * The pegged millisecond. 079 */ 080 private long firstMillisecond; 081 082 /** 083 * Constructs a millisecond based on the current system time. 084 * The time zone and locale are determined by the calendar 085 * returned by {@link RegularTimePeriod#getCalendarInstance()}. 086 */ 087 public Millisecond() { 088 this(new Date()); 089 } 090 091 /** 092 * Constructs a millisecond. 093 * The time zone and locale are determined by the calendar 094 * returned by {@link RegularTimePeriod#getCalendarInstance()}. 095 * 096 * @param millisecond the millisecond (0-999). 097 * @param second the second ({@code null} not permitted). 098 */ 099 public Millisecond(int millisecond, Second second) { 100 Args.nullNotPermitted(second, "second"); 101 this.millisecond = millisecond; 102 this.second = (byte) second.getSecond(); 103 this.minute = (byte) second.getMinute().getMinute(); 104 this.hour = (byte) second.getMinute().getHourValue(); 105 this.day = second.getMinute().getDay(); 106 peg(getCalendarInstance()); 107 } 108 109 /** 110 * Creates a new millisecond. 111 * The time zone and locale are determined by the calendar 112 * returned by {@link RegularTimePeriod#getCalendarInstance()}. 113 * 114 * @param millisecond the millisecond (0-999). 115 * @param second the second (0-59). 116 * @param minute the minute (0-59). 117 * @param hour the hour (0-23). 118 * @param day the day (1-31). 119 * @param month the month (1-12). 120 * @param year the year (1900-9999). 121 */ 122 public Millisecond(int millisecond, int second, int minute, int hour, 123 int day, int month, int year) { 124 125 this(millisecond, new Second(second, minute, hour, day, month, year)); 126 127 } 128 129 /** 130 * Constructs a new millisecond. 131 * The time zone and locale are determined by the calendar 132 * returned by {@link RegularTimePeriod#getCalendarInstance()}. 133 * 134 * @param time the time. 135 * 136 * @see #Millisecond(Date, TimeZone, Locale) 137 */ 138 public Millisecond(Date time) { 139 this(time, getCalendarInstance()); 140 } 141 142 /** 143 * Creates a millisecond. 144 * 145 * @param time the date-time ({@code null} not permitted). 146 * @param zone the time zone ({@code null} not permitted). 147 * @param locale the locale ({@code null} not permitted). 148 * 149 * @since 1.0.13 150 */ 151 public Millisecond(Date time, TimeZone zone, Locale locale) { 152 Args.nullNotPermitted(time, "time"); 153 Args.nullNotPermitted(zone, "zone"); 154 Args.nullNotPermitted(locale, "locale"); 155 Calendar calendar = Calendar.getInstance(zone, locale); 156 calendar.setTime(time); 157 this.millisecond = calendar.get(Calendar.MILLISECOND); 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, locale); 162 peg(calendar); 163 } 164 165 /** 166 * Constructs a new instance, based on a particular date/time. 167 * The time zone and locale are determined by the {@code calendar} 168 * parameter. 169 * 170 * @param time the date/time ({@code null} not permitted). 171 * @param calendar the calendar to use for calculations ({@code null} not permitted). 172 */ 173 public Millisecond(Date time, Calendar calendar) { 174 Args.nullNotPermitted(time, "time"); 175 Args.nullNotPermitted(calendar, "calendar"); 176 calendar.setTime(time); 177 this.millisecond = calendar.get(Calendar.MILLISECOND); 178 this.second = (byte) calendar.get(Calendar.SECOND); 179 this.minute = (byte) calendar.get(Calendar.MINUTE); 180 this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); 181 this.day = new Day(time, calendar); 182 peg(calendar); 183 } 184 185 /** 186 * Returns the second. 187 * 188 * @return The second. 189 */ 190 public Second getSecond() { 191 return new Second(this.second, this.minute, this.hour, 192 this.day.getDayOfMonth(), this.day.getMonth(), 193 this.day.getYear()); 194 } 195 196 /** 197 * Returns the millisecond. 198 * 199 * @return The millisecond. 200 */ 201 public long getMillisecond() { 202 return this.millisecond; 203 } 204 205 /** 206 * Returns the first millisecond of the second. This will be determined 207 * relative to the time zone specified in the constructor, or in the 208 * calendar instance passed in the most recent call to the 209 * {@link #peg(Calendar)} method. 210 * 211 * @return The first millisecond of the second. 212 * 213 * @see #getLastMillisecond() 214 */ 215 @Override 216 public long getFirstMillisecond() { 217 return this.firstMillisecond; 218 } 219 220 /** 221 * Returns the last millisecond of the second. This will be 222 * determined relative to the time zone specified in the constructor, or 223 * in the calendar instance passed in the most recent call to the 224 * {@link #peg(Calendar)} method. 225 * 226 * @return The last millisecond of the second. 227 * 228 * @see #getFirstMillisecond() 229 */ 230 @Override 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} not permitted). 240 * 241 * @since 1.0.3 242 */ 243 @Override 244 public void peg(Calendar calendar) { 245 this.firstMillisecond = getFirstMillisecond(calendar); 246 } 247 248 /** 249 * Returns the millisecond preceding this one. 250 * No matter what time zone and locale this instance was created with, 251 * the returned instance will use the default calendar for time 252 * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. 253 * 254 * @return The millisecond preceding this one. 255 */ 256 @Override 257 public RegularTimePeriod previous() { 258 RegularTimePeriod result = null; 259 if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) { 260 result = new Millisecond(this.millisecond - 1, getSecond()); 261 } 262 else { 263 Second previous = (Second) getSecond().previous(); 264 if (previous != null) { 265 result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous); 266 } 267 } 268 return result; 269 } 270 271 /** 272 * Returns the millisecond following this one. 273 * No matter what time zone and locale this instance was created with, 274 * the returned instance will use the default calendar for time 275 * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. 276 * 277 * @return The millisecond following this one. 278 */ 279 @Override 280 public RegularTimePeriod next() { 281 RegularTimePeriod result = null; 282 if (this.millisecond != LAST_MILLISECOND_IN_SECOND) { 283 result = new Millisecond(this.millisecond + 1, getSecond()); 284 } 285 else { 286 Second next = (Second) getSecond().next(); 287 if (next != null) { 288 result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next); 289 } 290 } 291 return result; 292 } 293 294 /** 295 * Returns a serial index number for the millisecond. 296 * 297 * @return The serial index number. 298 */ 299 @Override 300 public long getSerialIndex() { 301 long hourIndex = this.day.getSerialIndex() * 24L + this.hour; 302 long minuteIndex = hourIndex * 60L + this.minute; 303 long secondIndex = minuteIndex * 60L + this.second; 304 return secondIndex * 1000L + this.millisecond; 305 } 306 307 /** 308 * Tests the equality of this object against an arbitrary Object. 309 * <P> 310 * This method will return true ONLY if the object is a Millisecond object 311 * representing the same millisecond as this instance. 312 * 313 * @param obj the object to compare 314 * 315 * @return {@code true} if milliseconds and seconds of this and object 316 * are the same. 317 */ 318 @Override 319 public boolean equals(Object obj) { 320 if (obj == this) { 321 return true; 322 } 323 if (!(obj instanceof Millisecond)) { 324 return false; 325 } 326 Millisecond that = (Millisecond) obj; 327 if (this.millisecond != that.millisecond) { 328 return false; 329 } 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} 351 * 352 * @return A hashcode. 353 */ 354 @Override 355 public int hashCode() { 356 int result = 17; 357 result = 37 * result + this.millisecond; 358 result = 37 * result + getSecond().hashCode(); 359 return result; 360 } 361 362 /** 363 * Returns an integer indicating the order of this Millisecond object 364 * relative to the specified object: 365 * 366 * negative == before, zero == same, positive == after. 367 * 368 * @param obj the object to compare 369 * 370 * @return negative == before, zero == same, positive == after. 371 */ 372 @Override 373 public int compareTo(Object obj) { 374 int result; 375 long difference; 376 377 // CASE 1 : Comparing to another Second object 378 // ------------------------------------------- 379 if (obj instanceof Millisecond) { 380 Millisecond ms = (Millisecond) obj; 381 difference = getFirstMillisecond() - ms.getFirstMillisecond(); 382 if (difference > 0) { 383 result = 1; 384 } 385 else { 386 if (difference < 0) { 387 result = -1; 388 } 389 else { 390 result = 0; 391 } 392 } 393 } 394 395 // CASE 2 : Comparing to another TimePeriod object 396 // ----------------------------------------------- 397 else if (obj instanceof RegularTimePeriod) { 398 RegularTimePeriod rtp = (RegularTimePeriod) obj; 399 final long thisVal = this.getFirstMillisecond(); 400 final long anotherVal = rtp.getFirstMillisecond(); 401 result = (thisVal < anotherVal ? -1 402 : (thisVal == anotherVal ? 0 : 1)); 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 * Returns the first millisecond of the time period. 417 * 418 * @param calendar the calendar ({@code null} not permitted). 419 * 420 * @return The first millisecond of the time period. 421 * 422 * @throws NullPointerException if {@code calendar} is 423 * {@code null}. 424 */ 425 @Override 426 public long getFirstMillisecond(Calendar calendar) { 427 int year = this.day.getYear(); 428 int month = this.day.getMonth() - 1; 429 int d = this.day.getDayOfMonth(); 430 calendar.clear(); 431 calendar.set(year, month, d, this.hour, this.minute, this.second); 432 calendar.set(Calendar.MILLISECOND, this.millisecond); 433 return calendar.getTimeInMillis(); 434 } 435 436 /** 437 * Returns the last millisecond of the time period. 438 * 439 * @param calendar the calendar ({@code null} not permitted). 440 * 441 * @return The last millisecond of the time period. 442 * 443 * @throws NullPointerException if {@code calendar} is 444 * {@code null}. 445 */ 446 @Override 447 public long getLastMillisecond(Calendar calendar) { 448 return getFirstMillisecond(calendar); 449 } 450 451}