001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * RelativeDateFormat.java 029 * ----------------------- 030 * (C) Copyright 2006, 2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: RelativeDateFormat.java,v 1.1.2.3 2007/03/05 13:47:40 mungady Exp $ 036 * 037 * Changes: 038 * -------- 039 * 01-Nov-2006 : Version 1 (DG); 040 * 23-Nov-2006 : Added argument checks, updated equals(), added clone() and 041 * hashCode() (DG); 042 * 043 */ 044 package org.jfree.chart.util; 045 046 import java.text.DateFormat; 047 import java.text.DecimalFormat; 048 import java.text.FieldPosition; 049 import java.text.NumberFormat; 050 import java.text.ParsePosition; 051 import java.util.Calendar; 052 import java.util.Date; 053 import java.util.GregorianCalendar; 054 055 /** 056 * A formatter that formats dates to show the elapsed time relative to some 057 * base date. 058 * 059 * @since 1.0.3 060 */ 061 public class RelativeDateFormat extends DateFormat { 062 063 /** The base milliseconds for the elapsed time calculation. */ 064 private long baseMillis; 065 066 /** 067 * A flag that controls whether or not a zero day count is displayed. 068 */ 069 private boolean showZeroDays; 070 071 /** 072 * A formatter for the day count (most likely not critical until the 073 * day count exceeds 999). 074 */ 075 private NumberFormat dayFormatter; 076 077 /** 078 * A string appended after the day count. 079 */ 080 private String daySuffix; 081 082 /** 083 * A string appended after the hours. 084 */ 085 private String hourSuffix; 086 087 /** 088 * A string appended after the minutes. 089 */ 090 private String minuteSuffix; 091 092 /** 093 * A formatter for the seconds (and milliseconds). 094 */ 095 private NumberFormat secondFormatter; 096 097 /** 098 * A string appended after the seconds. 099 */ 100 private String secondSuffix; 101 102 /** 103 * A constant for the number of milliseconds in one hour. 104 */ 105 private static long MILLISECONDS_IN_ONE_HOUR = 60 * 60 * 1000L; 106 107 /** 108 * A constant for the number of milliseconds in one day. 109 */ 110 private static long MILLISECONDS_IN_ONE_DAY = 24 * MILLISECONDS_IN_ONE_HOUR; 111 112 /** 113 * Creates a new instance. 114 */ 115 public RelativeDateFormat() { 116 this(0L); 117 } 118 119 /** 120 * Creates a new instance. 121 * 122 * @param time the date/time (<code>null</code> not permitted). 123 */ 124 public RelativeDateFormat(Date time) { 125 this(time.getTime()); 126 } 127 128 /** 129 * Creates a new instance. 130 * 131 * @param baseMillis the time zone (<code>null</code> not permitted). 132 */ 133 public RelativeDateFormat(long baseMillis) { 134 super(); 135 this.baseMillis = baseMillis; 136 this.showZeroDays = false; 137 this.dayFormatter = NumberFormat.getInstance(); 138 this.daySuffix = "d"; 139 this.hourSuffix = "h"; 140 this.minuteSuffix = "m"; 141 this.secondFormatter = NumberFormat.getNumberInstance(); 142 this.secondFormatter.setMaximumFractionDigits(3); 143 this.secondFormatter.setMinimumFractionDigits(3); 144 this.secondSuffix = "s"; 145 146 // we don't use the calendar or numberFormat fields, but equals(Object) 147 // is failing without them being non-null 148 this.calendar = new GregorianCalendar(); 149 this.numberFormat = new DecimalFormat("0"); 150 } 151 152 /** 153 * Returns the base date/time used to calculate the elapsed time for 154 * display. 155 * 156 * @return The base date/time in milliseconds since 1-Jan-1970. 157 * 158 * @see #setBaseMillis(long) 159 */ 160 public long getBaseMillis() { 161 return this.baseMillis; 162 } 163 164 /** 165 * Sets the base date/time used to calculate the elapsed time for display. 166 * This should be specified in milliseconds using the same encoding as 167 * <code>java.util.Date</code>. 168 * 169 * @param baseMillis the base date/time in milliseconds. 170 * 171 * @see #getBaseMillis() 172 */ 173 public void setBaseMillis(long baseMillis) { 174 this.baseMillis = baseMillis; 175 } 176 177 /** 178 * Returns the flag that controls whether or not zero day counts are 179 * shown in the formatted output. 180 * 181 * @return The flag. 182 * 183 * @see #setShowZeroDays(boolean) 184 */ 185 public boolean getShowZeroDays() { 186 return this.showZeroDays; 187 } 188 189 /** 190 * Sets the flag that controls whether or not zero day counts are shown 191 * in the formatted output. 192 * 193 * @param show the flag. 194 * 195 * @see #getShowZeroDays() 196 */ 197 public void setShowZeroDays(boolean show) { 198 this.showZeroDays = show; 199 } 200 201 /** 202 * Returns the string that is appended to the day count. 203 * 204 * @return The string. 205 * 206 * @see #setDaySuffix(String) 207 */ 208 public String getDaySuffix() { 209 return this.daySuffix; 210 } 211 212 /** 213 * Sets the string that is appended to the day count. 214 * 215 * @param suffix the suffix (<code>null</code> not permitted). 216 * 217 * @see #getDaySuffix() 218 */ 219 public void setDaySuffix(String suffix) { 220 if (suffix == null) { 221 throw new IllegalArgumentException("Null 'suffix' argument."); 222 } 223 this.daySuffix = suffix; 224 } 225 226 /** 227 * Returns the string that is appended to the hour count. 228 * 229 * @return The string. 230 * 231 * @see #setHourSuffix(String) 232 */ 233 public String getHourSuffix() { 234 return this.hourSuffix; 235 } 236 237 /** 238 * Sets the string that is appended to the hour count. 239 * 240 * @param suffix the suffix (<code>null</code> not permitted). 241 * 242 * @see #getHourSuffix() 243 */ 244 public void setHourSuffix(String suffix) { 245 if (suffix == null) { 246 throw new IllegalArgumentException("Null 'suffix' argument."); 247 } 248 this.hourSuffix = suffix; 249 } 250 251 /** 252 * Returns the string that is appended to the minute count. 253 * 254 * @return The string. 255 * 256 * @see #setMinuteSuffix(String) 257 */ 258 public String getMinuteSuffix() { 259 return this.minuteSuffix; 260 } 261 262 /** 263 * Sets the string that is appended to the minute count. 264 * 265 * @param suffix the suffix (<code>null</code> not permitted). 266 * 267 * @see #getMinuteSuffix() 268 */ 269 public void setMinuteSuffix(String suffix) { 270 if (suffix == null) { 271 throw new IllegalArgumentException("Null 'suffix' argument."); 272 } 273 this.minuteSuffix = suffix; 274 } 275 276 /** 277 * Returns the string that is appended to the second count. 278 * 279 * @return The string. 280 * 281 * @see #setSecondSuffix(String) 282 */ 283 public String getSecondSuffix() { 284 return this.secondSuffix; 285 } 286 287 /** 288 * Sets the string that is appended to the second count. 289 * 290 * @param suffix the suffix (<code>null</code> not permitted). 291 * 292 * @see #getSecondSuffix() 293 */ 294 public void setSecondSuffix(String suffix) { 295 if (suffix == null) { 296 throw new IllegalArgumentException("Null 'suffix' argument."); 297 } 298 this.secondSuffix = suffix; 299 } 300 301 /** 302 * Sets the formatter for the seconds and milliseconds. 303 * 304 * @param formatter the formatter (<code>null</code> not permitted). 305 */ 306 public void setSecondFormatter(NumberFormat formatter) { 307 if (formatter == null) { 308 throw new IllegalArgumentException("Null 'formatter' argument."); 309 } 310 this.secondFormatter = formatter; 311 } 312 313 /** 314 * Formats the given date as the amount of elapsed time (relative to the 315 * base date specified in the constructor). 316 * 317 * @param date the date. 318 * @param toAppendTo the string buffer. 319 * @param fieldPosition the field position. 320 * 321 * @return The formatted date. 322 */ 323 public StringBuffer format(Date date, StringBuffer toAppendTo, 324 FieldPosition fieldPosition) { 325 long currentMillis = date.getTime(); 326 long elapsed = currentMillis - this.baseMillis; 327 328 long days = elapsed / MILLISECONDS_IN_ONE_DAY; 329 elapsed = elapsed - (days * MILLISECONDS_IN_ONE_DAY); 330 long hours = elapsed / MILLISECONDS_IN_ONE_HOUR; 331 elapsed = elapsed - (hours * MILLISECONDS_IN_ONE_HOUR); 332 long minutes = elapsed / 60000L; 333 elapsed = elapsed - (minutes * 60000L); 334 double seconds = elapsed / 1000.0; 335 if (days != 0 || this.showZeroDays) { 336 toAppendTo.append(this.dayFormatter.format(days) + getDaySuffix()); 337 } 338 toAppendTo.append(String.valueOf(hours) + getHourSuffix()); 339 toAppendTo.append(String.valueOf(minutes) + getMinuteSuffix()); 340 toAppendTo.append(this.secondFormatter.format(seconds) 341 + getSecondSuffix()); 342 return toAppendTo; 343 } 344 345 /** 346 * Parses the given string (not implemented). 347 * 348 * @param source the date string. 349 * @param pos the parse position. 350 * 351 * @return <code>null</code>, as this method has not been implemented. 352 */ 353 public Date parse(String source, ParsePosition pos) { 354 return null; 355 } 356 357 /** 358 * Tests this formatter for equality with an arbitrary object. 359 * 360 * @param obj the object (<code>null</code> permitted). 361 * 362 * @return A boolean. 363 */ 364 public boolean equals(Object obj) { 365 if (obj == this) { 366 return true; 367 } 368 if (!(obj instanceof RelativeDateFormat)) { 369 return false; 370 } 371 if (!super.equals(obj)) { 372 return false; 373 } 374 RelativeDateFormat that = (RelativeDateFormat) obj; 375 if (this.baseMillis != that.baseMillis) { 376 return false; 377 } 378 if (this.showZeroDays != that.showZeroDays) { 379 return false; 380 } 381 if (!this.daySuffix.equals(that.daySuffix)) { 382 return false; 383 } 384 if (!this.hourSuffix.equals(that.hourSuffix)) { 385 return false; 386 } 387 if (!this.minuteSuffix.equals(that.minuteSuffix)) { 388 return false; 389 } 390 if (!this.secondSuffix.equals(that.secondSuffix)) { 391 return false; 392 } 393 if (!this.secondFormatter.equals(that.secondFormatter)) { 394 return false; 395 } 396 return true; 397 } 398 399 /** 400 * Returns a hash code for this instance. 401 * 402 * @return A hash code. 403 */ 404 public int hashCode() { 405 int result = 193; 406 result = 37 * result 407 + (int) (this.baseMillis ^ (this.baseMillis >>> 32)); 408 result = 37 * result + this.daySuffix.hashCode(); 409 result = 37 * result + this.hourSuffix.hashCode(); 410 result = 37 * result + this.minuteSuffix.hashCode(); 411 result = 37 * result + this.secondSuffix.hashCode(); 412 result = 37 * result + this.secondFormatter.hashCode(); 413 return result; 414 } 415 416 /** 417 * Returns a clone of this instance. 418 * 419 * @return A clone. 420 */ 421 public Object clone() { 422 RelativeDateFormat clone = (RelativeDateFormat) super.clone(); 423 clone.dayFormatter = (NumberFormat) this.dayFormatter.clone(); 424 clone.secondFormatter = (NumberFormat) this.secondFormatter.clone(); 425 return clone; 426 } 427 428 /** 429 * Some test code. 430 * 431 * @param args ignored. 432 */ 433 public static void main(String[] args) { 434 GregorianCalendar c0 = new GregorianCalendar(2006, 10, 1, 0, 0, 0); 435 GregorianCalendar c1 = new GregorianCalendar(2006, 10, 1, 11, 37, 43); 436 c1.set(Calendar.MILLISECOND, 123); 437 438 System.out.println("Default: "); 439 RelativeDateFormat rdf = new RelativeDateFormat(c0.getTimeInMillis()); 440 System.out.println(rdf.format(c1.getTime())); 441 System.out.println(); 442 443 System.out.println("Hide milliseconds: "); 444 rdf.setSecondFormatter(new DecimalFormat("0")); 445 System.out.println(rdf.format(c1.getTime())); 446 System.out.println(); 447 448 System.out.println("Show zero day output: "); 449 rdf.setShowZeroDays(true); 450 System.out.println(rdf.format(c1.getTime())); 451 System.out.println(); 452 453 System.out.println("Alternative suffixes: "); 454 rdf.setShowZeroDays(false); 455 rdf.setDaySuffix(":"); 456 rdf.setHourSuffix(":"); 457 rdf.setMinuteSuffix(":"); 458 rdf.setSecondSuffix(""); 459 System.out.println(rdf.format(c1.getTime())); 460 System.out.println(); 461 } 462 }