You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by bo...@apache.org on 2017/09/28 10:45:41 UTC

[1/4] ant git commit: Allow faking of zip entry modification times.

Repository: ant
Updated Branches:
  refs/heads/1.9.x 7b09880b6 -> 01613e0d8


Allow faking of zip entry modification times.

Adds DateUtils.parseLenientDateTime.


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/9d4b4328
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/9d4b4328
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/9d4b4328

Branch: refs/heads/1.9.x
Commit: 9d4b432840dc5db015b8b3f049875430a5545230
Parents: 7b09880
Author: George Bateman <ge...@gmail.com>
Authored: Mon Aug 28 12:20:38 2017 +0100
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Sep 28 12:40:52 2017 +0200

----------------------------------------------------------------------
 src/main/org/apache/tools/ant/taskdefs/Zip.java | 43 ++++++++++++++++-
 .../org/apache/tools/ant/util/DateUtils.java    | 50 ++++++++++++++++++++
 2 files changed, 91 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/9d4b4328/src/main/org/apache/tools/ant/taskdefs/Zip.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java
index 5d68b2f..00ba912 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
@@ -585,6 +590,27 @@ public class Zip extends MatchingTask {
     }
 
     /**
+     * Set all stored file modification times to {@code time}.
+     * @param time Milliseconds since 1970-01-01 00:00, or
+     *        <code>YYYY-MM-DD{T/ }HH:MM[:SS[.SSS]][ ][±ZZ[[:]ZZ]]</code>, or
+     *        <code>MM/DD/YYYY HH:MM[:SS] {AM/PM}</code>, 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);
 
             /*

http://git-wip-us.apache.org/repos/asf/ant/blob/9d4b4328/src/main/org/apache/tools/ant/util/DateUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/util/DateUtils.java b/src/main/org/apache/tools/ant/util/DateUtils.java
index 9ce737b..37874ee 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<DateFormat> iso8601WithTimeZone =
+        new ThreadLocal<DateFormat>() {
+            @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 <touch>}-style date.
+     * That is:
+     * <ul>
+     * <li>Milliseconds since 1970-01-01 00:00</li>
+     * <li><code>YYYY-MM-DD{T| }HH:MM[:SS[.SSS]][ ][±ZZ[[:]ZZ]]</code></li>
+     * <li><code>MM/DD/YYYY HH:MM[:SS] {AM|PM}</code></li></ul>
+     * 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);
+    }
 }


[2/4] ant git commit: Reverse dependancy of DateUtils on Touch

Posted by bo...@apache.org.
Reverse dependancy of DateUtils on Touch

Also add test


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/a0cb1fa8
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/a0cb1fa8
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/a0cb1fa8

Branch: refs/heads/1.9.x
Commit: a0cb1fa8434e6385fb115951bf74ea8c78a6fd07
Parents: 9d4b432
Author: George Bateman <ge...@gmail.com>
Authored: Mon Sep 25 12:08:18 2017 +0100
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Sep 28 12:43:12 2017 +0200

----------------------------------------------------------------------
 .../org/apache/tools/ant/taskdefs/Touch.java    | 26 +++++-----------
 .../org/apache/tools/ant/util/DateUtils.java    | 32 +++++++++++++++++---
 src/tests/antunit/taskdefs/zip-test.xml         | 20 ++++++++++++
 3 files changed, 55 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/a0cb1fa8/src/main/org/apache/tools/ant/taskdefs/Touch.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/Touch.java b/src/main/org/apache/tools/ant/taskdefs/Touch.java
index c6d79b7..5b9722b 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Touch.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Touch.java
@@ -39,6 +39,7 @@ import org.apache.tools.ant.types.resources.FileProvider;
 import org.apache.tools.ant.types.resources.FileResource;
 import org.apache.tools.ant.types.resources.Touchable;
 import org.apache.tools.ant.types.resources.Union;
+import org.apache.tools.ant.util.DateUtils;
 import org.apache.tools.ant.util.FileNameMapper;
 import org.apache.tools.ant.util.FileUtils;
 
@@ -59,31 +60,18 @@ public class Touch extends Task {
         DateFormat getFallbackFormat();
     }
 
+    /**
+     * Provides access to DateUtils.EN_US_DATE_FORMAT_MIN (primary) and
+     * DateUtils.EN_US_DATE_FORMAT_SEC (fallback).
+     */
     public static final DateFormatFactory DEFAULT_DF_FACTORY
         = new DateFormatFactory() {
 
-        private ThreadLocal<DateFormat> primary =
-            new ThreadLocal<DateFormat>() {
-                @Override
-                protected DateFormat initialValue() {
-                    return new SimpleDateFormat("MM/dd/yyyy hh:mm a",
-                                                Locale.US);
-                }
-            };
-        private ThreadLocal<DateFormat> fallback =
-            new ThreadLocal<DateFormat>() {
-                @Override
-                protected DateFormat initialValue() {
-                    return new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a",
-                                                Locale.US);
-                }
-            };
-
         public DateFormat getPrimaryFormat() {
-            return primary.get();
+            return DateUtils.EN_US_DATE_FORMAT_MIN.get();
         }
         public DateFormat getFallbackFormat() {
-            return fallback.get();
+            return DateUtils.EN_US_DATE_FORMAT_SEC.get();
         }
     };
     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();

http://git-wip-us.apache.org/repos/asf/ant/blob/a0cb1fa8/src/main/org/apache/tools/ant/util/DateUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/util/DateUtils.java b/src/main/org/apache/tools/ant/util/DateUtils.java
index 37874ee..992c3fc 100644
--- a/src/main/org/apache/tools/ant/util/DateUtils.java
+++ b/src/main/org/apache/tools/ant/util/DateUtils.java
@@ -29,8 +29,6 @@ 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
  * defined format (<a href="http://www.w3.org/TR/NOTE-datetime">ISO8601</a>)
@@ -93,6 +91,32 @@ public final class DateUtils {
     private static final ChoiceFormat SECONDS_FORMAT =
             new ChoiceFormat(LIMITS, SECONDS_PART);
 
+    /**
+     * Provides a thread-local US-style date format. Exactly as used by
+     * {@code <touch>}, to minute precision:
+     * {@code SimpleDateFormat("MM/dd/yyyy hh:mm a", Locale.US)}
+     */
+    public static final ThreadLocal<DateFormat> EN_US_DATE_FORMAT_MIN =
+        new ThreadLocal<DateFormat>() {
+            @Override
+            protected DateFormat initialValue() {
+                return new SimpleDateFormat("MM/dd/yyyy hh:mm a", Locale.US);
+            }
+        };
+
+    /**
+     * Provides a thread-local US-style date format. Exactly as used by
+     * {@code <touch>}, to second precision:
+     * {@code SimpleDateFormat("MM/dd/yyyy hh:mm:ss a", Locale.US)}
+     */
+    public static final ThreadLocal<DateFormat> EN_US_DATE_FORMAT_SEC =
+        new ThreadLocal<DateFormat>() {
+            @Override
+            protected DateFormat initialValue() {
+                return new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a", Locale.US);
+            }
+        };
+
     static {
         MINUTE_SECONDS.setFormat(0, MINUTES_FORMAT);
         MINUTE_SECONDS.setFormat(1, SECONDS_FORMAT);
@@ -332,11 +356,11 @@ public final class DateUtils {
         } catch (NumberFormatException nfe) {}
 
         try {
-            return Touch.DEFAULT_DF_FACTORY.getPrimaryFormat().parse(dateStr);
+            return EN_US_DATE_FORMAT_MIN.get().parse(dateStr);
         } catch (ParseException pe) {}
 
         try {
-           return Touch.DEFAULT_DF_FACTORY.getFallbackFormat().parse(dateStr);
+           return EN_US_DATE_FORMAT_SEC.get().parse(dateStr);
         } catch (ParseException pe) {}
 
         Matcher m = iso8601normalizer.matcher(dateStr);

http://git-wip-us.apache.org/repos/asf/ant/blob/a0cb1fa8/src/tests/antunit/taskdefs/zip-test.xml
----------------------------------------------------------------------
diff --git a/src/tests/antunit/taskdefs/zip-test.xml b/src/tests/antunit/taskdefs/zip-test.xml
index 6a054f1..c2dfbe3 100644
--- a/src/tests/antunit/taskdefs/zip-test.xml
+++ b/src/tests/antunit/taskdefs/zip-test.xml
@@ -182,4 +182,24 @@
         <fileset dir="${basedir}" includes="zip-test.xml"/>
       </zip>
     </target>
+
+    <target name="testSetZipModTime">
+      <mkdir dir="${input}/"/>
+      <touch file="${input}/test1.txt"/>
+      <mkdir dir="${output}"/>
+      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00">
+        <fileset dir="${input}"/>
+      </zip>
+      <checksum file="${output}/test.zip" property="testSetZipModTime_hash" />
+      <delete file="${output}/test.zip" />
+
+      <touch file="${input}/test1.txt"/>
+      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00">
+        <fileset dir="${input}"/>
+      </zip>
+      <checksum file="${output}/test.zip" property="testSetZipModTime_hash" verifyproperty="testSetZipModTime_okay" />
+      <au:assertTrue>
+        <equals arg1="${testSetZipModTime_okay}" arg2="true" />
+      </au:assertTrue>
+    </target>
 </project>


[3/4] ant git commit: Fix zip-test

Posted by bo...@apache.org.
Fix zip-test


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/2c5430b4
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/2c5430b4
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/2c5430b4

Branch: refs/heads/1.9.x
Commit: 2c5430b420890ac5ac68794f95c2ac83e5a27456
Parents: a0cb1fa
Author: George Bateman <ge...@gmail.com>
Authored: Tue Sep 26 22:43:28 2017 +0100
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Sep 28 12:43:39 2017 +0200

----------------------------------------------------------------------
 src/tests/antunit/taskdefs/zip-test.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/2c5430b4/src/tests/antunit/taskdefs/zip-test.xml
----------------------------------------------------------------------
diff --git a/src/tests/antunit/taskdefs/zip-test.xml b/src/tests/antunit/taskdefs/zip-test.xml
index c2dfbe3..7ff45c1 100644
--- a/src/tests/antunit/taskdefs/zip-test.xml
+++ b/src/tests/antunit/taskdefs/zip-test.xml
@@ -185,19 +185,19 @@
 
     <target name="testSetZipModTime">
       <mkdir dir="${input}/"/>
+      <mkdir dir="${input}/subdir"/>
       <touch file="${input}/test1.txt"/>
+      <touch file="${input}/subdir/test2.txt"/>
       <mkdir dir="${output}"/>
-      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00">
-        <fileset dir="${input}"/>
-      </zip>
+      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00" />
       <checksum file="${output}/test.zip" property="testSetZipModTime_hash" />
       <delete file="${output}/test.zip" />
 
+      <sleep seconds="4" /> <!-- Necessary to ensure zips would have normally differed in timestamp -->
       <touch file="${input}/test1.txt"/>
-      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00">
-        <fileset dir="${input}"/>
-      </zip>
-      <checksum file="${output}/test.zip" property="testSetZipModTime_hash" verifyproperty="testSetZipModTime_okay" />
+      <touch file="${input}/subdir/test2.txt"/>
+      <zip destfile="${output}/test.zip" basedir="${input}" modificationtime="2016-01-01 00:00" />
+      <checksum file="${output}/test.zip" property="${testSetZipModTime_hash}" verifyproperty="testSetZipModTime_okay" />
       <au:assertTrue>
         <equals arg1="${testSetZipModTime_okay}" arg2="true" />
       </au:assertTrue>


[4/4] ant git commit: record change, add @since tags, closes #36

Posted by bo...@apache.org.
record change, add @since tags, closes #36


Project: http://git-wip-us.apache.org/repos/asf/ant/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant/commit/01613e0d
Tree: http://git-wip-us.apache.org/repos/asf/ant/tree/01613e0d
Diff: http://git-wip-us.apache.org/repos/asf/ant/diff/01613e0d

Branch: refs/heads/1.9.x
Commit: 01613e0d8275665b850145fa02ae86a5a90a965d
Parents: 2c5430b
Author: Stefan Bodewig <bo...@apache.org>
Authored: Thu Sep 28 12:31:04 2017 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu Sep 28 12:45:25 2017 +0200

----------------------------------------------------------------------
 WHATSNEW                                          | 4 ++++
 src/main/org/apache/tools/ant/taskdefs/Zip.java   | 4 +++-
 src/main/org/apache/tools/ant/util/DateUtils.java | 3 +++
 3 files changed, 10 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant/blob/01613e0d/WHATSNEW
----------------------------------------------------------------------
diff --git a/WHATSNEW b/WHATSNEW
index f8701a9..eeae3e8 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -35,6 +35,10 @@ Other changes:
    it uploads, if the preserveLastModified attribute is set to
    true for that task 
 
+ * zip and the related tasks can now set the modfication time of all
+   entries to a fixed timestamp.
+   Github Pull Request #36
+
 Changes from Ant 1.9.8 TO Ant 1.9.9
 ===================================
 

http://git-wip-us.apache.org/repos/asf/ant/blob/01613e0d/src/main/org/apache/tools/ant/taskdefs/Zip.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java
index 00ba912..d1e1065 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Zip.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java
@@ -125,7 +125,7 @@ public class Zip extends MatchingTask {
             };
 
     private String fixedModTime = null; // User-provided.
-    protected long modTimeMillis = 0; // Calculated.
+    private long modTimeMillis = 0; // Calculated.
 
     /**
      * If this flag is true, execute() will run most operations twice,
@@ -597,6 +597,7 @@ public class Zip extends MatchingTask {
      *        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.
+     * @since Ant 1.9.10
      */
     public void setModificationtime(String time) {
         fixedModTime = time;
@@ -605,6 +606,7 @@ public class Zip extends MatchingTask {
     /**
      * The file modification time previously provided to
      * {@link #setModificationtime(String)} or {@code null} if unset.
+     * @since Ant 1.9.10
      */
     public String getModificationtime() {
         return fixedModTime;

http://git-wip-us.apache.org/repos/asf/ant/blob/01613e0d/src/main/org/apache/tools/ant/util/DateUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/apache/tools/ant/util/DateUtils.java b/src/main/org/apache/tools/ant/util/DateUtils.java
index 992c3fc..016ce9d 100644
--- a/src/main/org/apache/tools/ant/util/DateUtils.java
+++ b/src/main/org/apache/tools/ant/util/DateUtils.java
@@ -95,6 +95,7 @@ public final class DateUtils {
      * Provides a thread-local US-style date format. Exactly as used by
      * {@code <touch>}, to minute precision:
      * {@code SimpleDateFormat("MM/dd/yyyy hh:mm a", Locale.US)}
+     * @since Ant 1.9.10
      */
     public static final ThreadLocal<DateFormat> EN_US_DATE_FORMAT_MIN =
         new ThreadLocal<DateFormat>() {
@@ -108,6 +109,7 @@ public final class DateUtils {
      * Provides a thread-local US-style date format. Exactly as used by
      * {@code <touch>}, to second precision:
      * {@code SimpleDateFormat("MM/dd/yyyy hh:mm:ss a", Locale.US)}
+     * @since Ant 1.9.10
      */
     public static final ThreadLocal<DateFormat> EN_US_DATE_FORMAT_SEC =
         new ThreadLocal<DateFormat>() {
@@ -349,6 +351,7 @@ public final class DateUtils {
      * 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.
+     * @since Ant 1.9.10
      */
     public static Date parseLenientDateTime(String dateStr) throws ParseException {
         try {