This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ant.git


The following commit(s) were added to refs/heads/master by this push:
     new c13ddfd5a move DateUtils#getNow to Project and make it only do one 
thing
c13ddfd5a is described below

commit c13ddfd5a7e621c03f396569326de3e2aca6afb5
Author: Stefan Bodewig <[email protected]>
AuthorDate: Sun Aug 9 20:01:41 2026 +0200

    move DateUtils#getNow to Project and make it only do one thing
---
 WHATSNEW                                           |  6 ++
 src/main/org/apache/tools/ant/Project.java         | 66 ++++++++++++++++
 .../tools/ant/taskdefs/optional/PropertyFile.java  |  5 +-
 src/main/org/apache/tools/ant/util/DateUtils.java  | 88 +++-------------------
 .../dateutils-test.xml => core/project-test.xml}   | 13 +---
 5 files changed, 86 insertions(+), 92 deletions(-)

diff --git a/WHATSNEW b/WHATSNEW
index 1f719b63c..79f2c221d 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -22,6 +22,12 @@ Other changes:
    Based on a patch used by the Debian Ant package maintainers.
    Part of Bugzilla Report 61269
 
+ * a new method getNow in Project can now be used to calculate a
+   notion of "now" that consults the SOURCE_DATE_EPOCH environment
+   variable as well as the magic ant.tstamp.now and ant.tstamp.now.iso
+   properties in this order which you can use if you want to obtain a
+   reproducible timestamp in task you write yourself.
+
  * The "record" task now has a new "relativeToBaseDir" attribute,
    which can be set to "yes" or "no", to control where the
    recorder's file gets created. In the absence of this attribute,
diff --git a/src/main/org/apache/tools/ant/Project.java 
b/src/main/org/apache/tools/ant/Project.java
index 2cd2ba9c5..274b94de7 100644
--- a/src/main/org/apache/tools/ant/Project.java
+++ b/src/main/org/apache/tools/ant/Project.java
@@ -23,8 +23,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
@@ -35,6 +37,8 @@ import java.util.Set;
 import java.util.Stack;
 import java.util.Vector;
 import java.util.WeakHashMap;
+import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.apache.tools.ant.helper.DefaultExecutor;
@@ -48,6 +52,7 @@ import org.apache.tools.ant.types.Path;
 import org.apache.tools.ant.types.Resource;
 import org.apache.tools.ant.types.ResourceFactory;
 import org.apache.tools.ant.types.resources.FileResource;
+import org.apache.tools.ant.util.DateUtils;
 import org.apache.tools.ant.util.FileUtils;
 import org.apache.tools.ant.util.JavaEnvUtils;
 import org.apache.tools.ant.util.VectorSet;
@@ -2484,4 +2489,65 @@ public class Project implements ResourceFactory {
     public Resource getResource(final String name) {
         return new FileResource(getBaseDir(), name);
     }
+
+    /**
+     * Consults {@linkplain #ENV_SOURCE_DATE_EPOCH SOURCE_DATE_EPOCH} 
environment variable and
+     * the magic properties {@link MagicNames#TSTAMP_NOW_ISO} and {@link 
MagicNames#TSTAMP_NOW}
+     * for predefined values of "now" and falls back to {@code new Date()} if 
neither is set.
+     *
+     * <p>{@code SOURCE_DATE_EPOCH} takes precedence over {@link 
MagicNames#TSTAMP_NOW_ISO} which
+     * in turn takes precedence over {@link MagicNames#TSTAMP_NOW}.</p>
+     *
+     * @return "now" as explained above
+     * @since Ant 1.10.18
+     */
+    public Date getNow() {
+        final String epoch = System.getenv(DateUtils.ENV_SOURCE_DATE_EPOCH);
+        if (epoch != null) {
+            // Value of SOURCE_DATE_EPOCH will be an integer, representing 
seconds.
+            try {
+                Date d = new Date(Long.parseLong(epoch) * 1000L);
+                log("Honouring environment variable " + 
DateUtils.ENV_SOURCE_DATE_EPOCH
+                    + " which has been set to " + epoch);
+                return d;
+            } catch(NumberFormatException e) {
+                // ignore
+                log("Ignoring invalid value '" + epoch + "' for " + 
DateUtils.ENV_SOURCE_DATE_EPOCH
+                    + " environment variable", Project.MSG_DEBUG);
+            }
+        }
+        return getNowAsDate();
+    }
+
+    private Date getNowAsDate() {
+        Optional<Date> now = getNowAsDate(
+            MagicNames.TSTAMP_NOW_ISO,
+            s -> Date.from(Instant.parse(s)),
+            (k, v) -> "magic property " + k + " ignored as '" + v + "' is not 
in valid ISO pattern"
+        );
+        if (now.isPresent()) {
+            return now.get();
+        }
+
+        now = getNowAsDate(
+            MagicNames.TSTAMP_NOW,
+            s -> new Date(1000 * Long.parseLong(s)),
+            (k, v) -> "magic property " + k + " ignored as " + v + " is not a 
valid number"
+        );
+        return now.orElseGet(Date::new);
+    }
+
+    private Optional<Date> getNowAsDate(String propertyName, Function<String, 
Date> map,
+                                        BiFunction<String, String, String> 
log) {
+        String property = getProperty(propertyName);
+        if (property != null && !property.isEmpty()) {
+            try {
+                return Optional.ofNullable(map.apply(property));
+            } catch (Exception e) {
+                log(log.apply(propertyName, property));
+            }
+        }
+        return Optional.empty();
+    }
+
 }
diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java 
b/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
index fef3af613..a6cfdadce 100644
--- a/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
+++ b/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
@@ -193,10 +193,9 @@ public class PropertyFile extends Task {
             properties = new Properties();
         } else {
             LayoutPreservingProperties p = new LayoutPreservingProperties();
-            Map.Entry<Date, Boolean> now = DateUtils.getNow(getProject());
             Calendar c = Calendar.getInstance();
-            c.setTime(now.getKey());
-            TimeZone tz = Boolean.TRUE.equals(now.getValue())
+            c.setTime(getProject().getNow());
+            TimeZone tz = System.getenv(DateUtils.ENV_SOURCE_DATE_EPOCH) != 
null
                 ? TimeZone.getTimeZone("UTC") : null;
             p.setDateComment(c, tz);
             properties = p;
diff --git a/src/main/org/apache/tools/ant/util/DateUtils.java 
b/src/main/org/apache/tools/ant/util/DateUtils.java
index 9e2221749..abc338456 100644
--- a/src/main/org/apache/tools/ant/util/DateUtils.java
+++ b/src/main/org/apache/tools/ant/util/DateUtils.java
@@ -22,22 +22,13 @@ import java.text.DateFormat;
 import java.text.MessageFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.AbstractMap;
 import java.util.Calendar;
-import java.time.Instant;
 import java.util.Date;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
 import java.util.TimeZone;
-import java.util.function.BiFunction;
-import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.tools.ant.MagicNames;
-import org.apache.tools.ant.Project;
-
 /**
  * Helper methods to deal with date/time formatting with a specific
  * defined format (<a href="https://www.w3.org/TR/NOTE-datetime";>ISO8601</a>)
@@ -52,6 +43,15 @@ public final class DateUtils {
     private static final int ONE_MINUTE = 60;
     private static final int ONE_HOUR = 60;
     private static final int TEN = 10;
+
+    /**
+     * Name of the environment variable used to set timestamps for 
reproducible builds.
+     *
+     * @see "https://reproducible-builds.org/docs/source-date-epoch/";
+     * @since Ant 1.10.18
+     */
+    public static final String ENV_SOURCE_DATE_EPOCH = "SOURCE_DATE_EPOCH";
+
     /**
      * ISO8601-like pattern for date-time. It does not support timezone.
      *  <code>yyyy-MM-ddTHH:mm:ss</code>
@@ -395,74 +395,4 @@ public final class DateUtils {
             + (m.group(6) == null ? "00" : m.group(6));
         return iso8601WithTimeZone.get().parse(normISO);
     }
-
-    /**
-     * Name of the environment variable used to set timestamps for 
reproducible builds.
-     *
-     * @see "https://reproducible-builds.org/docs/source-date-epoch/";
-     * @since Ant 1.10.18
-     */
-    public static final String ENV_SOURCE_DATE_EPOCH = "SOURCE_DATE_EPOCH";
-
-    /**
-     * Consults {@linkplain #ENV_SOURCE_DATE_EPOCH SOURCE_DATE_EPOCH} 
environment variable and
-     * the magic properties {@link MagicNames#TSTAMP_NOW_ISO} and {@link 
MagicNames#TSTAMP_NOW}
-     * for predefined values of "now" and falls back to {@code new Date()} if 
neither is set.
-     *
-     * <p>{@code SOURCE_DATE_EPOCH} takes precedence over {@link 
MagicNames#TSTAMP_NOW_ISO} which
-     * in turn takes precedence over {@link MagicNames#TSTAMP_NOW}.</p>
-     *
-     * @param project Project instance to use when looking up the magic 
properties.
-     * @return a tuple of "now" and a boolean flag that indicates whether 
{@code SOURCE_DATE_EPOCH} has been set.
-     * @since Ant 1.10.18
-     */
-    public static Map.Entry<Date, Boolean> getNow(Project project) {
-        final String epoch = System.getenv(ENV_SOURCE_DATE_EPOCH);
-        if (epoch != null) {
-            // Value of SOURCE_DATE_EPOCH will be an integer, representing 
seconds.
-            try {
-                Date d = new Date(Long.parseLong(epoch) * 1000L);
-                project.log("Honouring environment variable " + 
ENV_SOURCE_DATE_EPOCH + " which has been set to " + epoch);
-                return new AbstractMap.SimpleImmutableEntry(d, true);
-            } catch(NumberFormatException e) {
-                // ignore
-                project.log("Ignoring invalid value '" + epoch + "' for " + 
ENV_SOURCE_DATE_EPOCH
-                            + " environment variable", Project.MSG_DEBUG);
-            }
-        }
-        return new AbstractMap.SimpleImmutableEntry(getNowAsDate(project), 
false);
-    }
-
-    private static Date getNowAsDate(Project p) {
-        Optional<Date> now = getNowAsDate(
-            p,
-            MagicNames.TSTAMP_NOW_ISO,
-            s -> Date.from(Instant.parse(s)),
-            (k, v) -> "magic property " + k + " ignored as '" + v + "' is not 
in valid ISO pattern"
-        );
-        if (now.isPresent()) {
-            return now.get();
-        }
-
-        now = getNowAsDate(
-            p,
-            MagicNames.TSTAMP_NOW,
-            s -> new Date(1000 * Long.parseLong(s)),
-            (k, v) -> "magic property " + k + " ignored as " + v + " is not a 
valid number"
-        );
-        return now.orElseGet(Date::new);
-    }
-
-    private static Optional<Date> getNowAsDate(Project p, String propertyName, 
Function<String, Date> map,
-                                               BiFunction<String, String, 
String> log) {
-        String property = p.getProperty(propertyName);
-        if (property != null && !property.isEmpty()) {
-            try {
-                return Optional.ofNullable(map.apply(property));
-            } catch (Exception e) {
-                p.log(log.apply(propertyName, property));
-            }
-        }
-        return Optional.empty();
-    }
 }
diff --git a/src/tests/antunit/util/dateutils-test.xml 
b/src/tests/antunit/core/project-test.xml
similarity index 86%
rename from src/tests/antunit/util/dateutils-test.xml
rename to src/tests/antunit/core/project-test.xml
index f488e4fb0..09c663fc3 100644
--- a/src/tests/antunit/util/dateutils-test.xml
+++ b/src/tests/antunit/core/project-test.xml
@@ -15,7 +15,7 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<project default="antunit" xmlns:au="antlib:org.apache.ant.antunit">
+<project default="antunit" name="project-test" 
xmlns:au="antlib:org.apache.ant.antunit">
   <import file="../antunit-base.xml" />
 
   <target name="-createNowPrinter">
@@ -24,7 +24,6 @@
     <echo file="${input}/NowPrinter.java"><![CDATA[
       import org.apache.tools.ant.BuildException;
       import org.apache.tools.ant.Project;
-      import org.apache.tools.ant.util.DateUtils;
       import java.text.SimpleDateFormat;
       import java.util.*;
       public class NowPrinter {
@@ -37,11 +36,10 @@
           for (int i = 0; i < argsLen; i += 2) {
             p.setProperty(args[i], args[i + 1]);
           }
-          Map.Entry<Date, Boolean> now = DateUtils.getNow(p);
+          Date now = p.getNow();
           SimpleDateFormat format = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
           format.setTimeZone(TimeZone.getTimeZone("UTC"));
-          System.out.println("NOW is " + format.format(now.getKey()));
-          System.out.println("REPRODUCIBLE_BUILDS is " + now.getValue());
+          System.out.println("NOW is " + format.format(now));
         }
       }
     ]]></echo>
@@ -62,7 +60,6 @@
       <arg value="86400"/>
     </java>
     <au:assertPropertyContains name="testout" value="NOW is 
1970-01-02T00:00:00"/>
-    <au:assertPropertyContains name="testout" value="REPRODUCIBLE_BUILDS is 
false"/>
   </target>
 
   <target name="testGetNowMagicPropertyIso" depends="-createNowPrinter">
@@ -79,7 +76,6 @@
       <arg value="1972-04-17T08:07:00Z"/>
     </java>
     <au:assertPropertyContains name="testout" value="NOW is 
1972-04-17T08:07:00"/>
-    <au:assertPropertyContains name="testout" value="REPRODUCIBLE_BUILDS is 
false"/>
   </target>
 
   <target name="testGetNowMagicPropertyBoth" depends="-createNowPrinter">
@@ -98,7 +94,6 @@
       <arg value="1972-04-17T08:07:00Z"/>
     </java>
     <au:assertPropertyContains name="testout" value="NOW is 
1972-04-17T08:07:00"/>
-    <au:assertPropertyContains name="testout" value="REPRODUCIBLE_BUILDS is 
false"/>
   </target>
 
   <target name="testGetNowSourceDateEpoch" depends="-createNowPrinter">
@@ -118,7 +113,6 @@
       <env key="SOURCE_DATE_EPOCH" value="1650585600"/>
     </java>
     <au:assertPropertyContains name="testout" value="NOW is 
2022-04-22T00:00:00"/>
-    <au:assertPropertyContains name="testout" value="REPRODUCIBLE_BUILDS is 
true"/>
   </target>
 
   <target name="testGetNowRealNow" depends="-createNowPrinter">
@@ -139,6 +133,5 @@
       </classpath>
     </java>
     <au:assertPropertyContains name="testout" value="NOW is ${dstamp}T"/>
-    <au:assertPropertyContains name="testout" value="REPRODUCIBLE_BUILDS is 
false"/>
   </target>
 </project>

Reply via email to