diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index b0ee23761d..8cf85f81e9 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -27,6 +27,8 @@ package hudson; import hudson.model.Slave; import hudson.security.*; + +import java.text.SimpleDateFormat; import java.util.function.Predicate; import jenkins.util.SystemProperties; import hudson.cli.CLICommand; @@ -219,10 +221,36 @@ public class Functions { return Util.XS_DATETIME_FORMATTER.format(cal.getTime()); } + @Restricted(NoExternalUse.class) + public static String iso8601DateTime(Date date) { + return Util.XS_DATETIME_FORMATTER.format(date); + } + + /** + * Returns a localized string for the specified date, not including time. + * @param date + * @return + */ + @Restricted(NoExternalUse.class) + public static String localDate(Date date) { + return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT).format(date); + } + public static String rfc822Date(Calendar cal) { return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime()); } + /** + * Returns a human-readable string describing the time difference between now and the specified date. + * + * @param date + * @return + */ + @Restricted(NoExternalUse.class) + public static String getTimeSpanString(Date date) { + return Util.getTimeSpanString(Math.abs(date.getTime() - new Date().getTime())); + } + /** * During Jenkins start-up, before {@link InitMilestone#PLUGINS_STARTED} the extensions lists will be empty * and they are not guaranteed to be fully populated until after {@link InitMilestone#EXTENSIONS_AUGMENTED}, diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index f0fd153ed6..58a3c2f2bc 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -43,8 +43,10 @@ import java.io.IOException; import java.net.URI; import java.net.URL; import java.net.URLEncoder; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -1019,6 +1021,13 @@ public class UpdateSite { */ private Set incompatibleParentPlugins; + /** + * Date when this plugin was released. + * @since TODO + */ + @Exported + public final Date releaseTimestamp; + @DataBoundConstructor public Plugin(String sourceId, JSONObject o) { super(sourceId, o, UpdateSite.this.url); @@ -1028,6 +1037,16 @@ public class UpdateSite { this.compatibleSinceVersion = Util.intern(get(o,"compatibleSinceVersion")); this.minimumJavaVersion = Util.intern(get(o, "minimumJavaVersion")); this.requiredCore = Util.intern(get(o,"requiredCore")); + final String releaseTimestamp = get(o, "releaseTimestamp"); + Date date = null; + if (releaseTimestamp != null) { + try { + date = Date.from(Instant.parse(releaseTimestamp)); + } catch (Exception ex) { + LOGGER.log(Level.FINE, "Failed to parse releaseTimestamp for " + title + " from " + sourceId, ex); + } + } + this.releaseTimestamp = date; this.categories = o.has("labels") ? internInPlace((String[])o.getJSONArray("labels").toArray(EMPTY_STRING_ARRAY)) : null; JSONArray ja = o.getJSONArray("dependencies"); int depCount = (int)(ja.stream().filter(IS_DEP_PREDICATE.and(IS_NOT_OPTIONAL)).count()); diff --git a/core/src/main/resources/hudson/PluginManager/_table.css b/core/src/main/resources/hudson/PluginManager/_table.css index 626282f69e..4fc5eb51a1 100644 --- a/core/src/main/resources/hudson/PluginManager/_table.css +++ b/core/src/main/resources/hudson/PluginManager/_table.css @@ -4,6 +4,10 @@ width: 15em; } +time { + white-space: nowrap; +} + #filter-container { margin: 1em; text-align: right; diff --git a/core/src/main/resources/hudson/PluginManager/table.jelly b/core/src/main/resources/hudson/PluginManager/table.jelly index f5abf90b4b..abf956d516 100644 --- a/core/src/main/resources/hudson/PluginManager/table.jelly +++ b/core/src/main/resources/hudson/PluginManager/table.jelly @@ -30,7 +30,7 @@ THE SOFTWARE. page: page name to be passed to local:tabBar --> - + @@ -52,6 +52,7 @@ THE SOFTWARE. width="32">${%Install} ${%Name} ${%Version} + ${%Released} ${%Installed} @@ -132,6 +133,13 @@ THE SOFTWARE. + + + + + diff --git a/core/src/main/resources/hudson/PluginManager/table.properties b/core/src/main/resources/hudson/PluginManager/table.properties index e2297fdde1..15e7cdeb33 100644 --- a/core/src/main/resources/hudson/PluginManager/table.properties +++ b/core/src/main/resources/hudson/PluginManager/table.properties @@ -45,4 +45,4 @@ depJavaWarning=\ and in turn loading this plugin will fail. securityWarning=\ Warning: This plugin version may not be safe to use. Please review the following security notices: - +ago={0} ago