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

markt-asf pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new b2471fdbc9 Review "Fix TIME_MON and TIME_WDAY values in the rewrite 
valve"
b2471fdbc9 is described below

commit b2471fdbc95caa3f4d6963621fb0a9a72e960708
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Oct 29 17:28:25 2026 +0000

    Review "Fix TIME_MON and TIME_WDAY values in the rewrite valve"
    
    Further align behaviour with mod_rewrite
    - zero pad time components where required
    - user correct time format
---
 .../catalina/valves/rewrite/ResolverImpl.java      | 30 +++++++++++++++-------
 .../catalina/valves/rewrite/TestRewriteValve.java  | 20 +++++++++++++--
 webapps/docs/changelog.xml                         |  8 ++++++
 3 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/java/org/apache/catalina/valves/rewrite/ResolverImpl.java 
b/java/org/apache/catalina/valves/rewrite/ResolverImpl.java
index b91811db64..6a410092ea 100644
--- a/java/org/apache/catalina/valves/rewrite/ResolverImpl.java
+++ b/java/org/apache/catalina/valves/rewrite/ResolverImpl.java
@@ -22,6 +22,8 @@ import java.nio.charset.Charset;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
@@ -35,7 +37,6 @@ import org.apache.catalina.WebResource;
 import org.apache.catalina.WebResourceRoot;
 import org.apache.catalina.connector.Request;
 import org.apache.juli.logging.Log;
-import org.apache.tomcat.util.http.FastHttpDateFormat;
 import org.apache.tomcat.util.http.RequestUtil;
 import org.apache.tomcat.util.net.SSLSupport;
 import org.apache.tomcat.util.net.jsse.PEMFile;
@@ -51,6 +52,11 @@ public class ResolverImpl extends Resolver {
 
     private static final StringManager sm = 
StringManager.getManager(ResolverImpl.class);
 
+    /**
+     * Format of the TIME variable, as used by mod_rewrite.
+     */
+    private static final DateTimeFormatter TIME_FORMAT = 
DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
     /**
      * The request being processed.
      */
@@ -138,15 +144,16 @@ public class ResolverImpl extends Resolver {
             case "REQUEST_FILENAME" -> request.getPathTranslated();
             case "HTTPS" -> request.isSecure() ? "on" : "off";
             case "TIME_YEAR" -> 
String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
-            // Like mod_rewrite, TIME_MON is 1-12 and TIME_WDAY is 0-6 with 
Sunday first,
-            // unlike the zero-based Calendar.MONTH and one-based Sunday-first 
Calendar.DAY_OF_WEEK
-            case "TIME_MON" -> 
String.valueOf(Calendar.getInstance().get(Calendar.MONTH) + 1);
-            case "TIME_DAY" -> 
String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
-            case "TIME_HOUR" -> 
String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
-            case "TIME_MIN" -> 
String.valueOf(Calendar.getInstance().get(Calendar.MINUTE));
-            case "TIME_SEC" -> 
String.valueOf(Calendar.getInstance().get(Calendar.SECOND));
+            // Like mod_rewrite, TIME_MON is 01-12, TIME_WDAY is 0-6 with 
Sunday first and the other time values
+            // are zero padded to two digits, unlike the zero-based 
Calendar.MONTH and the one-based Sunday-first
+            // Calendar.DAY_OF_WEEK
+            case "TIME_MON" -> 
twoDigits(Calendar.getInstance().get(Calendar.MONTH) + 1);
+            case "TIME_DAY" -> 
twoDigits(Calendar.getInstance().get(Calendar.DAY_OF_MONTH));
+            case "TIME_HOUR" -> 
twoDigits(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
+            case "TIME_MIN" -> 
twoDigits(Calendar.getInstance().get(Calendar.MINUTE));
+            case "TIME_SEC" -> 
twoDigits(Calendar.getInstance().get(Calendar.SECOND));
             case "TIME_WDAY" -> 
String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1);
-            case "TIME" -> FastHttpDateFormat.getCurrentDate();
+            case "TIME" -> TIME_FORMAT.format(LocalDateTime.now());
             default -> null;
         };
     }
@@ -420,6 +427,11 @@ public class ResolverImpl extends Resolver {
         }
     }
 
+    private static String twoDigits(int value) {
+        return value < 10 ? "0" + value : String.valueOf(value);
+    }
+
+
     private static String emptyStringIfNull(String value) {
         return Objects.requireNonNullElse(value, "");
     }
diff --git a/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java 
b/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
index ce8dc0af7b..aa3153ba7b 100644
--- a/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
+++ b/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
@@ -344,9 +344,9 @@ public class TestRewriteValve extends TomcatBaseTest {
 
     @Test
     public void testRewriteTimeMonModRewriteNumbering() throws Exception {
-        // mod_rewrite numbering: 1-12; two tokens in one rule also cover
+        // mod_rewrite numbering: 01-12; two tokens in one rule also cover
         // concatenation parsing
-        String expected = String.valueOf(LocalDate.now().getMonthValue());
+        String expected = String.format("%02d", 
Integer.valueOf(LocalDate.now().getMonthValue()));
         doTestRewrite("RewriteRule ^/c/x$ - 
[E=X-Test:%{TIME_MON}-%{TIME_MON}]",
                 "/c/x", "/c/x", null, expected + "-" + expected);
     }
@@ -360,6 +360,22 @@ public class TestRewriteValve extends TomcatBaseTest {
                 "/c/x", "/c/x", null, expected + "TUE" + expected);
     }
 
+    @Test
+    public void testRewriteTimeValuesModRewriteFormat() throws Exception {
+        // The time values are zero padded to two digits (the year and the
+        // weekday are not) and TIME is yyyyMMddHHmmss. Matching the format
+        // rather than a value keeps the test independent of the clock.
+        doTestRewrite("RewriteCond %{TIME_YEAR} ^[0-9]{4}$\n" +
+                "RewriteCond %{TIME_MON} ^(0[1-9]|1[0-2])$\n" +
+                "RewriteCond %{TIME_DAY} ^(0[1-9]|[12][0-9]|3[01])$\n" +
+                "RewriteCond %{TIME_HOUR} ^([01][0-9]|2[0-3])$\n" +
+                "RewriteCond %{TIME_MIN} ^[0-5][0-9]$\n" +
+                "RewriteCond %{TIME_SEC} ^[0-5][0-9]$\n" +
+                "RewriteCond %{TIME_WDAY} ^[0-6]$\n" +
+                "RewriteCond %{TIME} ^[0-9]{14}$\n" +
+                "RewriteRule ^/c/x$ /c/ok", "/c/x", "/c/ok");
+    }
+
     @Test
     public void testRemoteIdentIsNotTheRemoteUser() throws Exception {
         // REMOTE_IDENT is the RFC 931 ident protocol user name, which Tomcat
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a40cf6e03c..b6e5396d20 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -300,6 +300,14 @@
         Ensure that requests cannot try to start asynchronous processing and 
the
         HTTP upgrade process on the same request. (markt)
       </fix>
+      <fix>
+        Align the format of the <code>TIME_MON</code>, <code>TIME_DAY</code>,
+        <code>TIME_HOUR</code>, <code>TIME_MIN</code>, <code>TIME_SEC</code>
+        and <code>TIME</code> server variables in the rewrite valve with
+        mod_rewrite. The first five are now zero padded to two digits and
+        <code>TIME</code> is now of the form <code>yyyyMMddHHmmss</code>
+        rather than an HTTP date.
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to