diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java
index 35e71eaa4e..be6b6ac3b8 100644
--- a/core/src/main/java/hudson/Util.java
+++ b/core/src/main/java/hudson/Util.java
@@ -49,6 +49,14 @@ import java.util.regex.Pattern;
*/
public class Util {
+ // Constant number of milliseconds in various time units.
+ private static final long ONE_SECOND_MS = 1000;
+ private static final long ONE_MINUTE_MS = 60 * ONE_SECOND_MS;
+ private static final long ONE_HOUR_MS = 60 * ONE_MINUTE_MS;
+ private static final long ONE_DAY_MS = 24 * ONE_HOUR_MS;
+ private static final long ONE_MONTH_MS = 30 * ONE_DAY_MS;
+ private static final long ONE_YEAR_MS = 365 * ONE_DAY_MS;
+
/**
* Creates a filtered sublist.
*/
@@ -70,7 +78,7 @@ public class Util {
* Replaces the occurrence of '$key' by properties.get('key').
*
*
- * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
+ * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
*
*/
public static String replaceMacro(String s, Map properties) {
@@ -341,7 +349,7 @@ public class Util {
throw new IOException2("MD5 not installed",e); // impossible
}
}
-
+
public static String getDigestOf(String text) {
try {
return getDigestOf(new ByteArrayInputStream(text.getBytes("UTF-8")));
@@ -372,33 +380,64 @@ public class Util {
* number of milliseconds.
*/
public static String getTimeSpanString(long duration) {
- duration /= 1000;
- if(duration<60)
- return Messages.Util_second(duration);
- duration /= 60;
- if(duration<60)
- return Messages.Util_minute(duration);
- duration /= 60;
- if(duration<24)
- return Messages.Util_hour(duration);
- duration /= 24;
- if(duration<30)
- return Messages.Util_day(duration);
- duration /= 30;
- if(duration<12)
- return Messages.Util_month(duration);
- duration /= 12;
- return Messages.Util_year(duration);
+ // Break the duration up in to units.
+ long years = duration / ONE_YEAR_MS;
+ duration %= ONE_YEAR_MS;
+ long months = duration / ONE_MONTH_MS;
+ duration %= ONE_MONTH_MS;
+ long days = duration / ONE_DAY_MS;
+ duration %= ONE_DAY_MS;
+ long hours = duration / ONE_HOUR_MS;
+ duration %= ONE_HOUR_MS;
+ long minutes = duration / ONE_MINUTE_MS;
+ duration %= ONE_MINUTE_MS;
+ long seconds = duration / ONE_SECOND_MS;
+
+ if (years > 0)
+ return makeTimeSpanString(years, "year", months, "month");
+ else if (months > 0)
+ return makeTimeSpanString(months, "month", days, "day");
+ else if (days > 0)
+ return makeTimeSpanString(days, "day", hours, "hour");
+ else if (hours > 0)
+ return makeTimeSpanString(hours, "hour", minutes, "minute");
+ else if (minutes > 0)
+ return makeTimeSpanString(minutes, "minute", seconds, "second");
+ else
+ // Durations less than a minute are only expressed in seconds (no ms).
+ return combine(seconds, "second");
}
+
+ /**
+ * Create a string representation of a time duration. If the quanity of
+ * the most significant unit is big (>=10), then we use only that most
+ * significant unit in the string represenation. If the quantity of the
+ * most significant unit is small (a single-digit value), then we also
+ * use a secondary, smaller unit for increased precision.
+ * So 13 minutes and 43 seconds returns just "13 minutes", but 3 minutes
+ * and 43 seconds is "3 minutes 43 seconds".
+ */
+ private static String makeTimeSpanString(long bigUnit,
+ String bigLabel,
+ long smallUnit,
+ String smallLabel) {
+ String text = combine(bigUnit, bigLabel);
+ if (bigUnit < 10)
+ text += ' ' + combine(smallUnit, smallLabel);
+ return text;
+ }
+
+
/**
* Get a human readable string representing strings like "xxx days ago",
- * which should be used to point to the occurence of an event in the past.
+ * which should be used to point to the occurence of an event in the past.
*/
public static String getPastTimeString(long duration) {
return Messages.Util_pastTime(getTimeSpanString(duration));
}
+
/**
* Combines number and unit, with a plural suffix if needed.
*/
@@ -624,7 +663,7 @@ public class Util {
/**
* Wraps with the error icon and the CSS class to render error message.
- * @since 1.173
+ * @since 1.173
*/
public static String wrapToErrorSpan(String s) {
s = ");
assertEquals("/a/foo/can/B/you-believe_aa~it?", Util.replaceMacro("/$A/foo/can/$B/you-believe_$AA~it?",m));
}
+
+
+ public void testTimeSpanString() {
+ // Check that amounts less than 365 days are not rounded up to a whole year.
+ // In the previous implementation there were 360 days in a year.
+ // We)