diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java
index 5d68b2f97..00ba91289 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Zip.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java
@@ -25,6 +25,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -55,6 +56,7 @@ import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.types.resources.Union;
import org.apache.tools.ant.types.resources.ZipResource;
import org.apache.tools.ant.types.resources.selectors.ResourceSelector;
+import org.apache.tools.ant.util.DateUtils;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.GlobPatternMapper;
@@ -122,6 +124,9 @@ public class Zip extends MatchingTask {
}
};
+ private String fixedModTime = null; // User-provided.
+ protected long modTimeMillis = 0; // Calculated.
+
/**
* If this flag is true, execute() will run most operations twice,
* the first time with {@link #skipWriting skipWriting} set to
@@ -584,6 +589,27 @@ public class Zip extends MatchingTask {
return zip64Mode;
}
+ /**
+ * Set all stored file modification times to {@code time}.
+ * @param time Milliseconds since 1970-01-01 00:00, or
+ * YYYY-MM-DD{T/ }HH:MM[:SS[.SSS]][ ][±ZZ[[:]ZZ]]
, or
+ * MM/DD/YYYY HH:MM[:SS] {AM/PM}
, where {a/b} indicates
+ * that you must choose one of a or b, and [c] indicates that you
+ * may use or omit c. ±ZZZZ is the timezone offset, and may be
+ * literally "Z" to mean GMT.
+ */
+ public void setModificationtime(String time) {
+ fixedModTime = time;
+ }
+
+ /**
+ * The file modification time previously provided to
+ * {@link #setModificationtime(String)} or {@code null} if unset.
+ */
+ public String getModificationtime() {
+ return fixedModTime;
+ }
+
/**
* validate and build
* @throws BuildException on error
@@ -836,6 +862,17 @@ public class Zip extends MatchingTask {
+ archiveType + " file to create!");
}
+ if (fixedModTime != null) {
+ try {
+ modTimeMillis = DateUtils.parseLenientDateTime(fixedModTime).getTime();
+ } catch (ParseException pe) {
+ throw new BuildException("Failed to parse date string " + fixedModTime + ".");
+ }
+ if (roundUp) {
+ modTimeMillis += ROUNDUP_MILLIS;
+ }
+ }
+
if (zipFile.exists() && !zipFile.isFile()) {
throw new BuildException(zipFile + " is not a file.");
}
@@ -1716,7 +1753,9 @@ public class Zip extends MatchingTask {
// ZIPs store time with a granularity of 2 seconds, round up
final int millisToAdd = roundUp ? ROUNDUP_MILLIS : 0;
- if (dir != null && dir.isExists()) {
+ if (fixedModTime != null) {
+ ze.setTime(modTimeMillis);
+ } else if (dir != null && dir.isExists()) {
ze.setTime(dir.getLastModified() + millisToAdd);
} else {
ze.setTime(System.currentTimeMillis() + millisToAdd);
@@ -1803,7 +1842,7 @@ public class Zip extends MatchingTask {
if (!skipWriting) {
final ZipEntry ze = new ZipEntry(vPath);
- ze.setTime(lastModified);
+ ze.setTime(fixedModTime != null ? modTimeMillis : lastModified);
ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);
/*
diff --git a/src/main/org/apache/tools/ant/util/DateUtils.java b/src/main/org/apache/tools/ant/util/DateUtils.java
index 9ce737b0a..37874eed4 100644
--- a/src/main/org/apache/tools/ant/util/DateUtils.java
+++ b/src/main/org/apache/tools/ant/util/DateUtils.java
@@ -26,6 +26,10 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.tools.ant.taskdefs.Touch;
/**
* Helper methods to deal with date/time formatting with a specific
@@ -298,4 +302,50 @@ public final class DateUtils {
return parseIso8601Date(datestr);
}
}
+
+ final private static ThreadLocal iso8601WithTimeZone =
+ new ThreadLocal() {
+ @Override protected DateFormat initialValue() {
+ // An arbitrary easy-to-read format to normalize to.
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
+ }
+ };
+ final private static Pattern iso8601normalizer = Pattern.compile(
+ "^(\\d{4,}-\\d{2}-\\d{2})[Tt ]" + // yyyy-MM-dd
+ "(\\d{2}:\\d{2}(:\\d{2}(\\.\\d{3})?)?) ?" + // HH:mm:ss.SSS
+ "(?:Z|([+-]\\d{2})(?::?(\\d{2}))?)?$"); // Z
+
+ /**
+ * Parse a lenient ISO 8601, ms since epoch, or {@code }-style date.
+ * That is:
+ *
+ * - Milliseconds since 1970-01-01 00:00
+ * YYYY-MM-DD{T| }HH:MM[:SS[.SSS]][ ][±ZZ[[:]ZZ]]
+ * MM/DD/YYYY HH:MM[:SS] {AM|PM}
+ * where {a|b} indicates that you must choose one of a or b, and [c]
+ * indicates that you may use or omit c. ±ZZZZ is the timezone offset, and
+ * may be literally "Z" to mean GMT.
+ */
+ public static Date parseLenientDateTime(String dateStr) throws ParseException {
+ try {
+ return new Date(Long.parseLong(dateStr));
+ } catch (NumberFormatException nfe) {}
+
+ try {
+ return Touch.DEFAULT_DF_FACTORY.getPrimaryFormat().parse(dateStr);
+ } catch (ParseException pe) {}
+
+ try {
+ return Touch.DEFAULT_DF_FACTORY.getFallbackFormat().parse(dateStr);
+ } catch (ParseException pe) {}
+
+ Matcher m = iso8601normalizer.matcher(dateStr);
+ if (!m.find()) throw new ParseException(dateStr, 0);
+ String normISO = m.group(1) + " "
+ + (m.group(3) == null ? m.group(2) + ":00" : m.group(2))
+ + (m.group(4) == null ? ".000 " : " ")
+ + (m.group(5) == null ? "+00" : m.group(5))
+ + (m.group(6) == null ? "00" : m.group(6));
+ return iso8601WithTimeZone.get().parse(normISO);
+ }
}