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