19
19
import java .io .IOException ;
20
20
import java .io .ObjectInputStream ;
21
21
import java .io .Serializable ;
22
- import java .text .DateFormatSymbols ;
23
22
import java .text .ParseException ;
24
23
import java .text .ParsePosition ;
25
24
import java .util .ArrayList ;
40
39
import java .util .regex .Pattern ;
41
40
42
41
import org .apache .logging .log4j .core .util .Integers ;
42
+ import org .apache .logging .log4j .util .PropertiesUtil ;
43
43
44
44
import static org .apache .logging .log4j .util .Strings .toRootUpperCase ;
45
45
79
79
* @see FastDatePrinter
80
80
*/
81
81
public class FastDateParser implements DateParser , Serializable {
82
+ /**
83
+ * Enables the parsing of "friendly" timezone names. Since building a list of all available names incurs an additional heap cost (~3MB on x86_64),
84
+ * in internal JDK structures which cannot be garbage collected, it is disabled by default.
85
+ */
86
+ public static final String LOG4J2_ENABLE_FRIENDLY_TIMEZONE_NAMES = "log4j2.enableFriendlyTimezoneNames" ;
82
87
83
88
/**
84
89
* Required for serialization support.
@@ -102,7 +107,7 @@ public class FastDateParser implements DateParser, Serializable {
102
107
// comparator used to sort regex alternatives
103
108
// alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
104
109
// all entries must be lowercase by locale.
105
- private static final Comparator <String > LONGER_FIRST_LOWERCASE = ( left , right ) -> right . compareTo ( left );
110
+ private static final Comparator <String > LONGER_FIRST_LOWERCASE = Comparator . reverseOrder ( );
106
111
107
112
/**
108
113
* <p>Constructs a new FastDateParser.</p>
@@ -551,7 +556,7 @@ boolean parse(final FastDateParser parser, final Calendar calendar, final String
551
556
552
557
/**
553
558
* Obtain a Strategy given a field from a SimpleDateFormat pattern
554
- * @param formatField A sub-sequence of the SimpleDateFormat pattern
559
+ * @param f A sub-sequence of the SimpleDateFormat pattern
555
560
* @param definingCalendar The calendar to obtain the short and long values
556
561
* @return The Strategy that will handle parsing for the field
557
562
*/
@@ -719,7 +724,7 @@ private static class CaseInsensitiveTextStrategy extends PatternStrategy {
719
724
@ Override
720
725
void setCalendar (final FastDateParser parser , final Calendar cal , final String value ) {
721
726
final Integer iVal = lKeyValues .get (value .toLowerCase (locale ));
722
- cal .set (field , iVal . intValue () );
727
+ cal .set (field , iVal );
723
728
}
724
729
}
725
730
@@ -843,44 +848,31 @@ private static class TzInfo {
843
848
final StringBuilder sb = new StringBuilder ();
844
849
sb .append ("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
845
850
846
- final Set <String > sorted = new TreeSet <>(LONGER_FIRST_LOWERCASE );
851
+ if (PropertiesUtil .getProperties ().getBooleanProperty (LOG4J2_ENABLE_FRIENDLY_TIMEZONE_NAMES )) {
852
+ final Set <String > sorted = new TreeSet <>(LONGER_FIRST_LOWERCASE );
847
853
848
- final String [][] zones = DateFormatSymbols .getInstance (locale ).getZoneStrings ();
849
- for (final String [] zoneNames : zones ) {
850
- // offset 0 is the time zone ID and is not localized
851
- final String tzId = zoneNames [ID ];
852
- if (tzId .equalsIgnoreCase ("GMT" )) {
853
- continue ;
854
- }
855
- final TimeZone tz = TimeZone .getTimeZone (tzId );
856
- // offset 1 is long standard name
857
- // offset 2 is short standard name
858
- final TzInfo standard = new TzInfo (tz , false );
859
- TzInfo tzInfo = standard ;
860
- for (int i = 1 ; i < zoneNames .length ; ++i ) {
861
- switch (i ) {
862
- case 3 : // offset 3 is long daylight savings (or summertime) name
863
- // offset 4 is the short summertime name
864
- tzInfo = new TzInfo (tz , true );
865
- break ;
866
- case 5 : // offset 5 starts additional names, probably standard time
867
- tzInfo = standard ;
868
- break ;
854
+ for (final String tzId : TimeZone .getAvailableIDs ()) {
855
+ if (tzId .equalsIgnoreCase ("GMT" )) {
856
+ continue ;
869
857
}
870
- if (zoneNames [i ] != null ) {
871
- final String key = zoneNames [i ].toLowerCase (locale );
872
- // ignore the data associated with duplicates supplied in
873
- // the additional names
874
- if (sorted .add (key )) {
875
- tzNames .put (key , tzInfo );
858
+ final TimeZone tz = TimeZone .getTimeZone (tzId );
859
+ final TzInfo standard = new TzInfo (tz , false );
860
+ final TzInfo dst = new TzInfo (tz , true );
861
+
862
+ for (boolean daylight : new Boolean [] {false , true }) {
863
+ for (int style : new Integer []{TimeZone .SHORT , TimeZone .LONG }) {
864
+ String name = tz .getDisplayName (daylight , style , locale ).toLowerCase (locale );
865
+ if (sorted .add (name )) {
866
+ tzNames .put (name , daylight ? dst : standard );
867
+ }
876
868
}
877
869
}
878
870
}
879
- }
880
- // order the regex alternatives with longer strings first, greedy
881
- // match will ensure longest string will be consumed
882
- for ( final String zoneName : sorted ) {
883
- simpleQuote ( sb . append ( '|' ), zoneName );
871
+ // order the regex alternatives with longer strings first, greedy
872
+ // match will ensure longest string will be consumed
873
+ for ( final String zoneName : sorted ) {
874
+ simpleQuote ( sb . append ( '|' ), zoneName );
875
+ }
884
876
}
885
877
sb .append (")" );
886
878
createPattern (sb );
0 commit comments