1616 */ 
1717package  org .apache .logging .log4j .core .pattern ;
1818
19+ import  java .util .regex .Pattern ;
1920import  org .jspecify .annotations .NullMarked ;
2021
2122/** 
2829public  enum  NamedInstantPattern  {
2930    ABSOLUTE ("HH:mm:ss,SSS" ),
3031
31-     ABSOLUTE_MICROS ("HH:mm:ss,SSSSSS" ,  "HH:mm:ss,nnnnnn" ),
32+     ABSOLUTE_MICROS ("HH:mm:ss,SSSSSS" ),
3233
33-     ABSOLUTE_NANOS ("HH:mm:ss,SSSSSSSSS" ,  "HH:mm:ss,nnnnnnnnn" ),
34+     ABSOLUTE_NANOS ("HH:mm:ss,SSSSSSSSS" ),
3435
3536    ABSOLUTE_PERIOD ("HH:mm:ss.SSS" ),
3637
@@ -42,9 +43,9 @@ public enum NamedInstantPattern {
4243
4344    DEFAULT ("yyyy-MM-dd HH:mm:ss,SSS" ),
4445
45-     DEFAULT_MICROS ("yyyy-MM-dd HH:mm:ss,SSSSSS" ,  "yyyy-MM-dd HH:mm:ss,nnnnnn" ),
46+     DEFAULT_MICROS ("yyyy-MM-dd HH:mm:ss,SSSSSS" ),
4647
47-     DEFAULT_NANOS ("yyyy-MM-dd HH:mm:ss,SSSSSSSSS" ,  "yyyy-MM-dd HH:mm:ss,nnnnnnnnn" ),
48+     DEFAULT_NANOS ("yyyy-MM-dd HH:mm:ss,SSSSSSSSS" ),
4849
4950    DEFAULT_PERIOD ("yyyy-MM-dd HH:mm:ss.SSS" ),
5051
@@ -54,30 +55,26 @@ public enum NamedInstantPattern {
5455
5556    ISO8601 ("yyyy-MM-dd'T'HH:mm:ss,SSS" ),
5657
57-     ISO8601_OFFSET_DATE_TIME_HH ("yyyy-MM-dd'T'HH:mm:ss,SSSx" ,  "yyyy-MM-dd'T'HH:mm:ss,SSSX" ),
58+     ISO8601_OFFSET_DATE_TIME_HH ("yyyy-MM-dd'T'HH:mm:ss,SSSx" ),
5859
59-     ISO8601_OFFSET_DATE_TIME_HHMM ("yyyy-MM-dd'T'HH:mm:ss,SSSxx" ,  "yyyy-MM-dd'T'HH:mm:ss,SSSXX" ),
60+     ISO8601_OFFSET_DATE_TIME_HHMM ("yyyy-MM-dd'T'HH:mm:ss,SSSxx" ),
6061
61-     ISO8601_OFFSET_DATE_TIME_HHCMM ("yyyy-MM-dd'T'HH:mm:ss,SSSxxx" ,  "yyyy-MM-dd'T'HH:mm:ss,SSSXXX" ),
62+     ISO8601_OFFSET_DATE_TIME_HHCMM ("yyyy-MM-dd'T'HH:mm:ss,SSSxxx" ),
6263
6364    ISO8601_PERIOD ("yyyy-MM-dd'T'HH:mm:ss.SSS" ),
6465
65-     ISO8601_PERIOD_MICROS ("yyyy-MM-dd'T'HH:mm:ss.SSSSSS" ,  "yyyy-MM-dd'T'HH:mm:ss.nnnnnn" ),
66+     ISO8601_PERIOD_MICROS ("yyyy-MM-dd'T'HH:mm:ss.SSSSSS" ),
6667
6768    US_MONTH_DAY_YEAR2_TIME ("dd/MM/yy HH:mm:ss.SSS" ),
6869
6970    US_MONTH_DAY_YEAR4_TIME ("dd/MM/yyyy HH:mm:ss.SSS" );
7071
72+     private  static  final  Pattern  NANO_PATTERN  = Pattern .compile ("S{4,}" );
73+ 
7174    private  final  String  pattern ;
72-     private  final  String  legacyPattern ;
7375
7476    NamedInstantPattern (String  pattern ) {
75-         this (pattern , pattern );
76-     }
77- 
78-     NamedInstantPattern (String  pattern , String  legacyPattern ) {
7977        this .pattern  = pattern ;
80-         this .legacyPattern  = legacyPattern ;
8178    }
8279
8380    /** 
@@ -93,11 +90,74 @@ public String getPattern() {
9390    /** 
9491     * Returns the legacy {@link org.apache.logging.log4j.core.util.datetime.FixedDateFormat} pattern 
9592     * associated with this named pattern. 
93+      * <p> 
94+      * If legacy formatters are enabled, output is produced for 
95+      * {@code FixedDateFormat} and {@code FastDateFormat}. To convert the {@code DateTimeFormatter} 
96+      * to its legacy counterpart, the following transformations need to be applied: 
97+      * </p> 
98+      * <table> 
99+      *   <caption>Pattern Differences</caption> 
100+      *   <thead> 
101+      *     <tr> 
102+      *       <th></th> 
103+      *       <th>Microseconds</th> 
104+      *       <th>Nanoseconds</th> 
105+      *       <th>Time-zone</th> 
106+      *     </tr> 
107+      *   </thead> 
108+      *   <tbody> 
109+      *     <tr> 
110+      *       <td>Legacy formatter directive</td> 
111+      *       <td><code>nnnnnn</code></td> 
112+      *       <td><code>nnnnnnnnn</code></td> 
113+      *       <td><code>X</code>, <code>XX</code>, <code>XXX</code></td> 
114+      *     </tr> 
115+      *     <tr> 
116+      *       <td>{@code DateTimeFormatter} directive</td> 
117+      *       <td><code>SSSSSS</code></td> 
118+      *       <td><code>SSSSSSSSS</code></td> 
119+      *       <td><code>x</code>, <code>xx</code>, <code>xxx</code></td> 
120+      *     </tr> 
121+      *   </tbody> 
122+      * </table> 
123+      * <h2></h2> 
124+      * <ul> 
125+      *   <li> 
126+      *     <p> 
127+      *       Legacy formatters are largely compatible with the {@code SimpleDateFormat} specification, 
128+      *       but introduce a custom {@code n} pattern letter, unique to Log4j, to represent sub-millisecond precision. 
129+      *       This {@code n} is not part of the standard {@code SimpleDateFormat}. 
130+      *     </p> 
131+      *     <p> 
132+      *       In legacy formatters, repeating {@code n} increases the precision, similar to how repeated {@code S} 
133+      *       is used for fractional seconds in {@code DateTimeFormatter}. 
134+      *     </p> 
135+      *     <p> 
136+      *       In contrast, {@code DateTimeFormatter} interprets {@code n} as nano-of-second. 
137+      *       In Java 17, both {@code n} and {@code N} always output nanosecond precision, 
138+      *       regardless of the number of pattern letters. 
139+      *     </p> 
140+      *   </li> 
141+      *   <li> 
142+      *     <p> 
143+      *       Legacy formatters use <code>X</code>, <code>XX</code>, and <code>XXX</code> to format time zones as 
144+      *       <code>+00</code>, <code>+0000</code>, or <code>+00:00</code>, following {@code SimpleDateFormat} conventions. 
145+      *       In contrast, {@code DateTimeFormatter} outputs <code>Z</code> for zero-offset when using <code>X</code>. 
146+      *       To ensure numeric output for zero-offset (e.g., <code>+00</code>), 
147+      *       we use <code>x</code>, <code>xx</code>, or <code>xxx</code> instead. 
148+      *     </p> 
149+      *   </li> 
150+      * </ul> 
96151     * 
97152     * @return the legacy pattern string as used in 
98153     * {@link org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat} 
99154     */ 
100155    String  getLegacyPattern () {
156+         String  legacyPattern  = pattern .replace ('x' , 'X' );
157+         if  (NANO_PATTERN .matcher (pattern ).find ()) {
158+             // If the pattern contains sub-millis, replace 'S' with 'n' for legacy formatters 
159+             return  legacyPattern .replace ('S' , 'n' );
160+         }
101161        return  legacyPattern ;
102162    }
103163}
0 commit comments