001/** 002 * Copyright 2010-2013 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; 027 028import org.apache.commons.lang3.StringUtils; 029import org.kuali.common.util.base.Exceptions; 030 031import com.google.common.base.Stopwatch; 032 033/** 034 * Format time, bytes, counts, dates, and transfer rates into human friendly form 035 * 036 * @author Jeff Caddel 037 * @since May 27, 2010 6:46:17 PM 038 */ 039public class FormatUtils { 040 041 public static final double SECOND = 1000; 042 public static final double MINUTE = 60 * SECOND; 043 public static final double HOUR = 60 * MINUTE; 044 public static final double DAY = 24 * HOUR; 045 public static final double YEAR = 365 * DAY; 046 047 private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"; 048 private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT); 049 050 private static final List<String> TIME_TOKENS = Arrays.asList("ms", "s", "m", "h", "d", "y"); 051 private static final List<Long> TIME_MULTIPLIERS = getTimeMultipliers(); 052 053 private static final List<String> SIZE_TOKENS = Arrays.asList("b", "k", "m", "g", "t", "p", "e"); 054 private static final int BASE = 1024; 055 056 private static NumberFormat largeSizeFormatter = NumberFormat.getInstance(); 057 private static NumberFormat sizeFormatter = NumberFormat.getInstance(); 058 private static NumberFormat timeFormatter = NumberFormat.getInstance(); 059 private static NumberFormat rateFormatter = NumberFormat.getInstance(); 060 private static NumberFormat countFormatter = NumberFormat.getInstance(); 061 private static NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(); 062 private static NumberFormat integerFormatter = NumberFormat.getInstance(); 063 064 static { 065 integerFormatter.setGroupingUsed(false); 066 integerFormatter.setMaximumFractionDigits(0); 067 integerFormatter.setMinimumFractionDigits(0); 068 sizeFormatter.setGroupingUsed(false); 069 sizeFormatter.setMaximumFractionDigits(1); 070 sizeFormatter.setMinimumFractionDigits(1); 071 largeSizeFormatter.setGroupingUsed(false); 072 largeSizeFormatter.setMaximumFractionDigits(3); 073 largeSizeFormatter.setMinimumFractionDigits(3); 074 timeFormatter.setGroupingUsed(false); 075 timeFormatter.setMaximumFractionDigits(3); 076 timeFormatter.setMinimumFractionDigits(3); 077 rateFormatter.setGroupingUsed(false); 078 rateFormatter.setMaximumFractionDigits(3); 079 rateFormatter.setMinimumFractionDigits(3); 080 countFormatter.setGroupingUsed(true); 081 countFormatter.setMaximumFractionDigits(0); 082 countFormatter.setMinimumFractionDigits(0); 083 } 084 085 public static String getCurrency(double number) { 086 return currencyFormatter.format(number); 087 } 088 089 /** 090 * 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. 091 * 092 * <pre> 093 * 1 == 1 byte 094 * 1b == 1 byte 095 * 1k == 1 kilobyte == 1024 bytes == 1,024 bytes 096 * 1m == 1 megabyte == 1024^2 bytes == 1,048,576 bytes 097 * 1g == 1 gigabyte == 1024^3 bytes == 1,073,741,824 bytes 098 * 1t == 1 terabyte == 1024^4 bytes == 1,099,511,627,776 bytes 099 * 1p == 1 petabyte == 1024^5 bytes == 1,125,899,906,842,624 bytes 100 * 1e == 1 exabyte == 1024^6 bytes == 1,152,921,504,606,846,976 bytes 101 * </pre> 102 */ 103 public static long getBytes(String size) { 104 return getBytes(size, SIZE_TOKENS, BASE); 105 } 106 107 public static long getBytes(String size, List<String> tokens, int base) { 108 Assert.notBlank(size); 109 for (int i = 0; i < tokens.size(); i++) { 110 String token = tokens.get(i); 111 long multiplier = (long) Math.pow(base, i); 112 if (StringUtils.endsWithIgnoreCase(size, token)) { 113 return getByteValue(size, token, multiplier); 114 } 115 } 116 // Assume bytes 117 return getByteValue(size, "", 1); 118 } 119 120 protected static long getByteValue(String time, String suffix, long multiplier) { 121 int len = StringUtils.length(time); 122 String substring = StringUtils.substring(time, 0, len - suffix.length()); 123 Double value = new Double(substring); 124 value = value * multiplier; 125 return value.longValue(); 126 } 127 128 /** 129 * 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. 130 * 131 * <pre> 132 * 1 == 1 millisecond 133 * 1ms == 1 millisecond 134 * 1s == 1 second == 1000 milliseconds 135 * 1m == 1 minute == 60,000 milliseconds 136 * 1h == 1 hour == 3,600,000 milliseconds 137 * 1d == 1 day == 86,400,000 milliseconds 138 * 1y == 1 year == 31,536,000,000 milliseconds 139 * </pre> 140 */ 141 public static long getMillis(String time) { 142 return getMillis(time, TIME_TOKENS, TIME_MULTIPLIERS); 143 } 144 145 /** 146 * 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. 147 * 148 * <pre> 149 * 1 == 1 millisecond 150 * 1ms == 1 millisecond 151 * 1s == 1 second == 1000 milliseconds 152 * 1m == 1 minute == 60,000 milliseconds 153 * 1h == 1 hour == 3,600,000 milliseconds 154 * 1d == 1 day == 86,400,000 milliseconds 155 * 1y == 1 year == 31,536,000,000 milliseconds 156 * </pre> 157 */ 158 public static int getMillisAsInt(String time) { 159 Long millis = getMillis(time); 160 if (millis <= Integer.MAX_VALUE) { 161 return millis.intValue(); 162 } else { 163 throw Exceptions.illegalArg("[%s] converts to [%s]. maximum allowable integer value is [%s]", time, millis, Integer.MAX_VALUE); 164 } 165 } 166 167 public static long getMillis(String time, List<String> tokens, List<Long> multipliers) { 168 Assert.notBlank(time); 169 Assert.isTrue(tokens.size() == multipliers.size()); 170 for (int i = 0; i < tokens.size(); i++) { 171 String token = tokens.get(i); 172 long multiplier = multipliers.get(i); 173 if (StringUtils.endsWithIgnoreCase(time, token)) { 174 return getTimeValue(time, token, multiplier); 175 } 176 } 177 // Assume milliseconds 178 return getTimeValue(time, "", 1); 179 } 180 181 protected static long getTimeValue(String time, String suffix, long multiplier) { 182 int len = StringUtils.length(time); 183 String substring = StringUtils.substring(time, 0, len - suffix.length()); 184 Double value = new Double(substring); 185 value = value * multiplier; 186 return value.longValue(); 187 } 188 189 /** 190 * Parse a date from the string. The string must be in the same format returned by the getDate() methods 191 */ 192 public static Date parseDate(String date) { 193 try { 194 synchronized (DATE_FORMATTER) { 195 return DATE_FORMATTER.parse(date); 196 } 197 } catch (ParseException e) { 198 throw new IllegalArgumentException("Can't parse [" + date + "]", e); 199 } 200 } 201 202 /** 203 * Return a formatted date 204 */ 205 public static String getDate(long millis) { 206 return getDate(new Date(millis)); 207 } 208 209 /** 210 * Return a formatted date 211 */ 212 public static String getDate(Date date) { 213 synchronized (DATE_FORMATTER) { 214 return DATE_FORMATTER.format(date); 215 } 216 } 217 218 /** 219 * 220 */ 221 public static String getThroughputInSeconds(long millis, long count, String label) { 222 double seconds = millis / SECOND; 223 double countPerSecond = count / seconds; 224 synchronized (countFormatter) { 225 return countFormatter.format(countPerSecond) + " " + label; 226 } 227 } 228 229 /** 230 * 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 231 */ 232 public static String getRate(long millis, long bytes) { 233 double seconds = millis / SECOND; 234 double bytesPerSecond = bytes / seconds; 235 Size bandwidthLevel = getSizeEnum(bytesPerSecond); 236 double transferRate = bytesPerSecond / bandwidthLevel.getValue(); 237 synchronized (rateFormatter) { 238 return rateFormatter.format(transferRate) + " " + bandwidthLevel.getRateLabel(); 239 } 240 } 241 242 /** 243 * Return a formatted <code>count</code> 244 */ 245 public static String getCount(long count) { 246 synchronized (countFormatter) { 247 return countFormatter.format(count); 248 } 249 } 250 251 /** 252 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are 253 * exactly 365 days per year. 254 */ 255 public static String getTime(long millis) { 256 return getTime(millis, timeFormatter); 257 } 258 259 /** 260 * 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 261 * exactly 365 days per year. 262 */ 263 public static String getTime(Stopwatch stopwatch) { 264 return getTime(stopwatch.elapsed(MILLISECONDS)); 265 } 266 267 /** 268 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are 269 * exactly 365 days per year. 270 */ 271 public static String getTime(long millis, NumberFormat formatter) { 272 long abs = Math.abs(millis); 273 synchronized (formatter) { 274 if (abs < SECOND) { 275 return millis + "ms"; 276 } else if (abs < MINUTE) { 277 return formatter.format(millis / SECOND) + "s"; 278 } else if (abs < HOUR) { 279 return formatter.format(millis / MINUTE) + "m"; 280 } else if (abs < DAY) { 281 return formatter.format(millis / HOUR) + "h"; 282 } else if (abs < YEAR) { 283 return formatter.format(millis / DAY) + "d"; 284 } else { 285 return formatter.format(millis / YEAR) + "y"; 286 } 287 } 288 } 289 290 /** 291 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate. 292 */ 293 public static String getSize(long bytes) { 294 return getSize(bytes, null); 295 } 296 297 /** 298 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate. 299 */ 300 public static String getIntegerSize(long bytes) { 301 return getIntegerSize(bytes, null); 302 } 303 304 /** 305 * Given a number of bytes return a string formatted into the unit of measure indicated 306 */ 307 public static String getIntegerSize(long bytes, final Size unitOfMeasure) { 308 Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure; 309 StringBuilder sb = new StringBuilder(); 310 synchronized (integerFormatter) { 311 sb.append(integerFormatter.format(bytes / (double) uom.getValue())); 312 } 313 sb.append(uom.getSizeLabel()); 314 return sb.toString(); 315 } 316 317 /** 318 * Given a number of bytes return a string formatted into the unit of measure indicated 319 */ 320 public static String getSize(long bytes, Size unitOfMeasure) { 321 Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure; 322 StringBuilder sb = new StringBuilder(); 323 sb.append(getFormattedSize(bytes, uom)); 324 sb.append(uom.getSizeLabel()); 325 return sb.toString(); 326 } 327 328 public static String getFormattedSize(long bytes, Size size) { 329 switch (size) { 330 case BYTE: 331 return bytes + ""; 332 case KB: 333 case MB: 334 case GB: 335 synchronized (sizeFormatter) { 336 return sizeFormatter.format(bytes / (double) size.getValue()); 337 } 338 default: 339 synchronized (largeSizeFormatter) { 340 return largeSizeFormatter.format(bytes / (double) size.getValue()); 341 } 342 } 343 } 344 345 public static Size getSizeEnum(double bytes) { 346 bytes = Math.abs(bytes); 347 if (bytes < Size.KB.getValue()) { 348 return Size.BYTE; 349 } else if (bytes < Size.MB.getValue()) { 350 return Size.KB; 351 } else if (bytes < Size.GB.getValue()) { 352 return Size.MB; 353 } else if (bytes < Size.TB.getValue()) { 354 return Size.GB; 355 } else if (bytes < Size.PB.getValue()) { 356 return Size.TB; 357 } else if (bytes < Size.EB.getValue()) { 358 return Size.PB; 359 } else { 360 return Size.EB; 361 } 362 } 363 364 protected static final List<Long> getTimeMultipliers() { 365 List<Long> m = new ArrayList<Long>(); 366 m.add(1L); 367 m.add(new Double(SECOND).longValue()); 368 m.add(new Double(MINUTE).longValue()); 369 m.add(new Double(HOUR).longValue()); 370 m.add(new Double(DAY).longValue()); 371 m.add(new Double(YEAR).longValue()); 372 return m; 373 } 374}