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

ChristopherSchultz 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 5ee0bc24b2 Fix handling of quoted literals and escaped 's
5ee0bc24b2 is described below

commit 5ee0bc24b2e6dfb485218e096066e2539f1b956d
Author: Christopher Schultz <[email protected]>
AuthorDate: Thu May 21 11:02:46 2026 -0400

    Fix handling of quoted literals and escaped 's
---
 java/org/apache/juli/DateFormatCache.java     | 35 ++++++++++------
 test/org/apache/juli/TestDateFormatCache.java | 57 +++++++++++++++++++++++++++
 webapps/docs/changelog.xml                    |  4 ++
 3 files changed, 84 insertions(+), 12 deletions(-)

diff --git a/java/org/apache/juli/DateFormatCache.java 
b/java/org/apache/juli/DateFormatCache.java
index 054f92265d..e40e8e3550 100644
--- a/java/org/apache/juli/DateFormatCache.java
+++ b/java/org/apache/juli/DateFormatCache.java
@@ -63,22 +63,33 @@ public class DateFormatCache {
      * formatted time stamps cacheable. Our consumer might choose to replace 
the dummy chars with the actual
      * milliseconds because that's relatively cheap.
      */
-    private String tidyFormat(String format) {
-        boolean escape = false;
+    protected static String tidyFormat(String format) {
+        final int length = format.length();
         StringBuilder result = new StringBuilder();
-        int len = format.length();
-        char x;
-        for (int i = 0; i < len; i++) {
-            x = format.charAt(i);
-            if (escape || x != 'S') {
-                result.append(x);
-            } else {
-                result.append(MSEC_PATTERN);
+        boolean literalMode = false;
+
+        for (int i = 0; i < length; i++) {
+            char c = format.charAt(i);
+
+            if (c == '\'') {
+                // Handle escaped quote ('') which isn't the same as entering 
literal-mode
+                if (i + 1 < length && format.charAt(i + 1) == '\'') {
+                    result.append("''");
+                    i++; // consume second quote
+                } else {
+                    literalMode = !literalMode;
+                    result.append(c);
+                }
+                continue;
             }
-            if (x == '\'') {
-                escape = !escape;
+
+            if (!literalMode && c == 'S') {
+                result.append(MSEC_PATTERN);
+            } else {
+                result.append(c);
             }
         }
+
         return result.toString();
     }
 
diff --git a/test/org/apache/juli/TestDateFormatCache.java 
b/test/org/apache/juli/TestDateFormatCache.java
index 9205364042..46a6e3a898 100644
--- a/test/org/apache/juli/TestDateFormatCache.java
+++ b/test/org/apache/juli/TestDateFormatCache.java
@@ -106,4 +106,61 @@ public class TestDateFormatCache {
         return sdf.format(new Date(secs * 1000));
     }
 
+    @Test
+    public void replacesUnquotedS() {
+        Assert.assertEquals(
+            "HH:mm:ss.###",
+            DateFormatCache.tidyFormat("HH:mm:ss.SSS")
+        );
+    }
+
+    @Test
+    public void doesNotReplaceQuotedS() {
+        Assert.assertEquals(
+            "HH:mm:ss.'SSS'",
+            DateFormatCache.tidyFormat("HH:mm:ss.'SSS'")
+        );
+    }
+
+    @Test
+    public void handlesEscapedQuoteInsideLiteral() {
+        Assert.assertEquals(
+            "'o''clock' ###",
+            DateFormatCache.tidyFormat("'o''clock' SSS")
+        );
+    }
+
+    @Test
+    public void doesNotReplaceSInsideLiteralAfterEscapedQuote() {
+        Assert.assertEquals(
+            "'abc''SSS'",
+            DateFormatCache.tidyFormat("'abc''SSS'")
+        );
+    }
+
+    @Test
+    public void handlesMultipleLiteralSections() {
+        Assert.assertEquals(
+            "'foo' ### 'bar'",
+            DateFormatCache.tidyFormat("'foo' SSS 'bar'")
+        );
+    }
+
+    @Test
+    public void handlesEscapedQuoteOutsideLiteral() {
+        Assert.assertEquals(
+            "'' ###",
+            DateFormatCache.tidyFormat("'' SSS")
+        );
+    }
+
+    @Test
+    public void complexQuoteScenario() {
+        Assert.assertEquals(
+            "'Start' ### 'o''clock' ### '' 'End'",
+            DateFormatCache.tidyFormat(
+                "'Start' SSS 'o''clock' SSS '' 'End'"
+            )
+        );
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 656615e4e9..7d78aa5f63 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -164,6 +164,10 @@
         Remove exception swallowing in <code>DataSourceStore</code> to align
         it with <code>FileStore</code> and avoid session loss on errors. (remm)
       </fix>
+      <fix>
+        Add support for single-quote escaped literal as well as quoted literals
+        in <code>DateFormatCache</code>. (schultz)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


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

Reply via email to