001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util; 017 018import static java.util.concurrent.TimeUnit.MILLISECONDS; 019 020import java.text.NumberFormat; 021import java.text.ParseException; 022import java.text.SimpleDateFormat; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Date; 026import java.util.List; 027import java.util.Map; 028import java.util.TimeZone; 029 030import org.apache.commons.lang3.StringUtils; 031import org.kuali.common.util.base.Exceptions; 032 033import com.google.common.base.Stopwatch; 034import com.google.common.collect.Iterables; 035 036/** 037 * Format time, bytes, counts, dates, and transfer rates into human friendly form 038 * 039 * @author Jeff Caddel 040 * @since May 27, 2010 6:46:17 PM 041 */ 042public class FormatUtils { 043 044 public static final double SECOND = 1000; 045 public static final double MINUTE = 60 * SECOND; 046 public static final double HOUR = 60 * MINUTE; 047 public static final double DAY = 24 * HOUR; 048 public static final double YEAR = 365 * DAY; 049 050 /** 051 * This is the format produced by the {@code toString()} method of {@code java.util.Date} instance 052 */ 053 public static final String JAVA_UTIL_DATE_TO_STRING_FORMAT = "EEE MMM d HH:mm:ss zzz y"; 054 private static final SimpleDateFormat JAVA_UTIL_DATE_TO_STRING_FORMATTER = new SimpleDateFormat(JAVA_UTIL_DATE_TO_STRING_FORMAT); 055 056 private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"; 057 private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT); 058 059 private static final List<String> TIME_TOKENS = Arrays.asList("ms", "s", "m", "h", "d", "y"); 060 private static final List<Long> TIME_MULTIPLIERS = getTimeMultipliers(); 061 062 private static final List<String> SIZE_TOKENS = Arrays.asList("b", "k", "m", "g", "t", "p", "e"); 063 private static final int BASE = 1024; 064 065 private static NumberFormat largeSizeFormatter = NumberFormat.getInstance(); 066 private static NumberFormat sizeFormatter = NumberFormat.getInstance(); 067 private static NumberFormat timeFormatter = NumberFormat.getInstance(); 068 private static NumberFormat rateFormatter = NumberFormat.getInstance(); 069 private static NumberFormat countFormatter = NumberFormat.getInstance(); 070 private static NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(); 071 private static NumberFormat integerFormatter = NumberFormat.getInstance(); 072 073 static { 074 integerFormatter.setGroupingUsed(false); 075 integerFormatter.setMaximumFractionDigits(0); 076 integerFormatter.setMinimumFractionDigits(0); 077 sizeFormatter.setGroupingUsed(false); 078 sizeFormatter.setMaximumFractionDigits(1); 079 sizeFormatter.setMinimumFractionDigits(1); 080 largeSizeFormatter.setGroupingUsed(false); 081 largeSizeFormatter.setMaximumFractionDigits(3); 082 largeSizeFormatter.setMinimumFractionDigits(3); 083 timeFormatter.setGroupingUsed(false); 084 timeFormatter.setMaximumFractionDigits(3); 085 timeFormatter.setMinimumFractionDigits(3); 086 rateFormatter.setGroupingUsed(false); 087 rateFormatter.setMaximumFractionDigits(3); 088 rateFormatter.setMinimumFractionDigits(3); 089 countFormatter.setGroupingUsed(true); 090 countFormatter.setMaximumFractionDigits(0); 091 countFormatter.setMinimumFractionDigits(0); 092 } 093 094 public static String getCurrency(double number) { 095 return currencyFormatter.format(number); 096 } 097 098 /** 099 * Parse bytes from a size string that ends with a unit of measure. If no unit of measure is provided, bytes is assumed. Unit of measure is case insensitive. 100 * 101 * <pre> 102 * 1 == 1 byte 103 * 1b == 1 byte 104 * 1k == 1 kilobyte == 1024 bytes == 1,024 bytes 105 * 1m == 1 megabyte == 1024^2 bytes == 1,048,576 bytes 106 * 1g == 1 gigabyte == 1024^3 bytes == 1,073,741,824 bytes 107 * 1t == 1 terabyte == 1024^4 bytes == 1,099,511,627,776 bytes 108 * 1p == 1 petabyte == 1024^5 bytes == 1,125,899,906,842,624 bytes 109 * 1e == 1 exabyte == 1024^6 bytes == 1,152,921,504,606,846,976 bytes 110 * </pre> 111 */ 112 public static long getBytes(String size) { 113 return getBytes(size, SIZE_TOKENS, BASE); 114 } 115 116 public static long getBytes(String size, List<String> tokens, int base) { 117 Assert.notBlank(size); 118 for (int i = 0; i < tokens.size(); i++) { 119 String token = tokens.get(i); 120 long multiplier = (long) Math.pow(base, i); 121 if (StringUtils.endsWithIgnoreCase(size, token)) { 122 return getByteValue(size, token, multiplier); 123 } 124 } 125 // Assume bytes 126 return getByteValue(size, "", 1); 127 } 128 129 protected static long getByteValue(String time, String suffix, long multiplier) { 130 int len = StringUtils.length(time); 131 String substring = StringUtils.substring(time, 0, len - suffix.length()); 132 Double value = new Double(substring); 133 value = value * multiplier; 134 return value.longValue(); 135 } 136 137 /** 138 * Parse milliseconds from a time string that ends with a unit of measure. If no unit of measure is provided, milliseconds is assumed. Unit of measure is case insensitive. 139 * 140 * <pre> 141 * 1 == 1 millisecond 142 * 1ms == 1 millisecond 143 * 1s == 1 second == 1000 milliseconds 144 * 1m == 1 minute == 60,000 milliseconds 145 * 1h == 1 hour == 3,600,000 milliseconds 146 * 1d == 1 day == 86,400,000 milliseconds 147 * 1y == 1 year == 31,536,000,000 milliseconds 148 * </pre> 149 */ 150 public static long getMillis(String time) { 151 return getMillis(time, TIME_TOKENS, TIME_MULTIPLIERS); 152 } 153 154 /** 155 * Parse milliseconds from a time string that ends with a unit of measure. If no unit of measure is provided, milliseconds is assumed. Unit of measure is case insensitive. 156 * 157 * <pre> 158 * 1 == 1 millisecond 159 * 1ms == 1 millisecond 160 * 1s == 1 second == 1000 milliseconds 161 * 1m == 1 minute == 60,000 milliseconds 162 * 1h == 1 hour == 3,600,000 milliseconds 163 * 1d == 1 day == 86,400,000 milliseconds 164 * 1y == 1 year == 31,536,000,000 milliseconds 165 * </pre> 166 */ 167 public static int getMillisAsInt(String time) { 168 Long millis = getMillis(time); 169 if (millis <= Integer.MAX_VALUE) { 170 return millis.intValue(); 171 } else { 172 throw Exceptions.illegalArgument("[%s] converts to [%s]. maximum allowable integer value is [%s]", time, millis, Integer.MAX_VALUE); 173 } 174 } 175 176 public static long getMillis(String time, List<String> tokens, List<Long> multipliers) { 177 Assert.notBlank(time); 178 Assert.isTrue(tokens.size() == multipliers.size()); 179 for (int i = 0; i < tokens.size(); i++) { 180 String token = tokens.get(i); 181 long multiplier = multipliers.get(i); 182 if (StringUtils.endsWithIgnoreCase(time, token)) { 183 return getTimeValue(time, token, multiplier); 184 } 185 } 186 // Assume milliseconds 187 return getTimeValue(time, "", 1); 188 } 189 190 protected static long getTimeValue(String time, String suffix, long multiplier) { 191 int len = StringUtils.length(time); 192 String substring = StringUtils.substring(time, 0, len - suffix.length()); 193 Double value = new Double(substring); 194 value = value * multiplier; 195 return value.longValue(); 196 } 197 198 /** 199 * Parse a date from the string. The string must be in the same format returned by the getDate() methods 200 */ 201 public static Date parseDate(String date) { 202 try { 203 synchronized (DATE_FORMATTER) { 204 return DATE_FORMATTER.parse(date); 205 } 206 } catch (ParseException e) { 207 throw new IllegalArgumentException("Can't parse [" + date + "]", e); 208 } 209 } 210 211 /** 212 * Return a formatted date 213 */ 214 public static String dateAsString(long millis) { 215 return dateAsString(new Date(millis)); 216 } 217 218 /** 219 * Return a formatted date 220 */ 221 public static String dateAsString(Date date) { 222 synchronized (JAVA_UTIL_DATE_TO_STRING_FORMATTER) { 223 return JAVA_UTIL_DATE_TO_STRING_FORMATTER.format(date); 224 } 225 } 226 227 /** 228 * Return a formatted date 229 */ 230 public static String dateAsString(long millis, String timezone) { 231 return dateAsString(new Date(millis), timezone); 232 } 233 234 /** 235 * Return a formatted date 236 */ 237 public static String dateAsString(Date date, String timezone) { 238 SimpleDateFormat sdf = new SimpleDateFormat(JAVA_UTIL_DATE_TO_STRING_FORMAT); 239 sdf.setTimeZone(TimeZone.getTimeZone(timezone)); 240 return sdf.format(date); 241 } 242 243 /** 244 * Return a formatted date 245 */ 246 public static String getDate(long millis) { 247 return getDate(new Date(millis)); 248 } 249 250 /** 251 * Return a formatted date 252 */ 253 public static String getDate(Date date) { 254 synchronized (DATE_FORMATTER) { 255 return DATE_FORMATTER.format(date); 256 } 257 } 258 259 /** 260 * 261 */ 262 public static String getThroughputInSeconds(long millis, long count, String label) { 263 double seconds = millis / SECOND; 264 double countPerSecond = count / seconds; 265 synchronized (countFormatter) { 266 return countFormatter.format(countPerSecond) + " " + label; 267 } 268 } 269 270 /** 271 * Given a number of bytes and the number of milliseconds it took to transfer that number of bytes, return bytes/s, KB/s, MB/s, GB/s, TB/s, PB/s, or EB/s as appropriate 272 */ 273 public static String getRate(long millis, long bytes) { 274 return getRate(millis, bytes, rateFormatter); 275 } 276 277 /** 278 * Given a number of bytes and the number of milliseconds it took to transfer that number of bytes, return bytes/s, KB/s, MB/s, GB/s, TB/s, PB/s, or EB/s as appropriate 279 */ 280 public static String getRate(long millis, long bytes, NumberFormat rateFormatter) { 281 double seconds = millis / SECOND; 282 double bytesPerSecond = bytes / seconds; 283 Size bandwidthLevel = getSizeEnum(bytesPerSecond); 284 double transferRate = bytesPerSecond / bandwidthLevel.getValue(); 285 synchronized (rateFormatter) { 286 return rateFormatter.format(transferRate) + " " + bandwidthLevel.getRateLabel(); 287 } 288 } 289 290 /** 291 * Return a formatted <code>count</code> representing the number of keys in the map 292 */ 293 public static String getCount(Map<?, ?> map) { 294 return getCount(map.keySet()); 295 } 296 297 /** 298 * Return a formatted <code>count</code> representing the number of elements in the iterable 299 */ 300 public static String getCount(Iterable<?> iterable) { 301 return getCount(Iterables.size(iterable)); 302 } 303 304 /** 305 * Return a formatted <code>count</code> 306 */ 307 public static String getCount(long count) { 308 synchronized (countFormatter) { 309 return countFormatter.format(count); 310 } 311 } 312 313 /** 314 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are 315 * exactly 365 days per year. 316 */ 317 public static String getTime(long millis) { 318 return getTime(millis, timeFormatter); 319 } 320 321 /** 322 * Given a stopwatch, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are 323 * exactly 365 days per year. 324 */ 325 public static String getTime(Stopwatch stopwatch) { 326 return getTime(stopwatch.elapsed(MILLISECONDS)); 327 } 328 329 /** 330 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are 331 * exactly 365 days per year. 332 */ 333 public static String getTime(long millis, NumberFormat formatter) { 334 long abs = Math.abs(millis); 335 synchronized (formatter) { 336 if (abs < SECOND) { 337 return millis + "ms"; 338 } else if (abs < MINUTE) { 339 return formatter.format(millis / SECOND) + "s"; 340 } else if (abs < HOUR) { 341 return formatter.format(millis / MINUTE) + "m"; 342 } else if (abs < DAY) { 343 return formatter.format(millis / HOUR) + "h"; 344 } else if (abs < YEAR) { 345 return formatter.format(millis / DAY) + "d"; 346 } else { 347 return formatter.format(millis / YEAR) + "y"; 348 } 349 } 350 } 351 352 /** 353 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate. 354 */ 355 public static String getSize(long bytes) { 356 return getSize(bytes, (Size) null); 357 } 358 359 /** 360 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate. 361 */ 362 public static String getIntegerSize(long bytes) { 363 return getIntegerSize(bytes, null); 364 } 365 366 /** 367 * Given a number of bytes return a string formatted into the unit of measure indicated 368 */ 369 public static String getIntegerSize(long bytes, final Size unitOfMeasure) { 370 Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure; 371 StringBuilder sb = new StringBuilder(); 372 synchronized (integerFormatter) { 373 sb.append(integerFormatter.format(bytes / (double) uom.getValue())); 374 } 375 sb.append(uom.getSizeLabel()); 376 return sb.toString(); 377 } 378 379 /** 380 * Given a number of bytes return a string formatted into the unit of measure indicated 381 */ 382 public static String getSize(long bytes, NumberFormat formatter) { 383 return getSize(bytes, null, formatter); 384 } 385 386 /** 387 * Given a number of bytes return a string formatted into the unit of measure indicated 388 */ 389 public static String getSize(long bytes, final Size unitOfMeasure, NumberFormat formatter) { 390 Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure; 391 StringBuilder sb = new StringBuilder(); 392 synchronized (formatter) { 393 sb.append(formatter.format(bytes / (double) uom.getValue())); 394 } 395 sb.append(uom.getSizeLabel()); 396 return sb.toString(); 397 } 398 399 /** 400 * Given a number of bytes return a string formatted into the unit of measure indicated 401 */ 402 public static String getSize(long bytes, Size unitOfMeasure) { 403 Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure; 404 StringBuilder sb = new StringBuilder(); 405 sb.append(getFormattedSize(bytes, uom)); 406 sb.append(uom.getSizeLabel()); 407 return sb.toString(); 408 } 409 410 public static String getFormattedSize(long bytes, Size size) { 411 switch (size) { 412 case BYTE: 413 return bytes + ""; 414 case KB: 415 case MB: 416 case GB: 417 synchronized (sizeFormatter) { 418 return sizeFormatter.format(bytes / (double) size.getValue()); 419 } 420 default: 421 synchronized (largeSizeFormatter) { 422 return largeSizeFormatter.format(bytes / (double) size.getValue()); 423 } 424 } 425 } 426 427 public static Size getSizeEnum(double bytes) { 428 bytes = Math.abs(bytes); 429 if (bytes < Size.KB.getValue()) { 430 return Size.BYTE; 431 } else if (bytes < Size.MB.getValue()) { 432 return Size.KB; 433 } else if (bytes < Size.GB.getValue()) { 434 return Size.MB; 435 } else if (bytes < Size.TB.getValue()) { 436 return Size.GB; 437 } else if (bytes < Size.PB.getValue()) { 438 return Size.TB; 439 } else if (bytes < Size.EB.getValue()) { 440 return Size.PB; 441 } else { 442 return Size.EB; 443 } 444 } 445 446 protected static final List<Long> getTimeMultipliers() { 447 List<Long> m = new ArrayList<Long>(); 448 m.add(1L); 449 m.add(new Double(SECOND).longValue()); 450 m.add(new Double(MINUTE).longValue()); 451 m.add(new Double(HOUR).longValue()); 452 m.add(new Double(DAY).longValue()); 453 m.add(new Double(YEAR).longValue()); 454 return m; 455 } 456}