1919import java .io .IOException ;
2020import java .io .ObjectInputStream ;
2121import java .io .Serializable ;
22- import java .text .DateFormatSymbols ;
2322import java .text .ParseException ;
2423import java .text .ParsePosition ;
2524import java .util .ArrayList ;
4039import java .util .regex .Pattern ;
4140
4241import org .apache .logging .log4j .core .util .Integers ;
42+ import org .apache .logging .log4j .util .PropertiesUtil ;
4343
4444import static org .apache .logging .log4j .util .Strings .toRootUpperCase ;
4545
7979 * @see FastDatePrinter
8080 */
8181public 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" ;
8287
8388 /**
8489 * Required for serialization support.
@@ -102,7 +107,7 @@ public class FastDateParser implements DateParser, Serializable {
102107 // comparator used to sort regex alternatives
103108 // alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
104109 // 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 ( );
106111
107112 /**
108113 * <p>Constructs a new FastDateParser.</p>
@@ -551,7 +556,7 @@ boolean parse(final FastDateParser parser, final Calendar calendar, final String
551556
552557 /**
553558 * 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
555560 * @param definingCalendar The calendar to obtain the short and long values
556561 * @return The Strategy that will handle parsing for the field
557562 */
@@ -719,7 +724,7 @@ private static class CaseInsensitiveTextStrategy extends PatternStrategy {
719724 @ Override
720725 void setCalendar (final FastDateParser parser , final Calendar cal , final String value ) {
721726 final Integer iVal = lKeyValues .get (value .toLowerCase (locale ));
722- cal .set (field , iVal . intValue () );
727+ cal .set (field , iVal );
723728 }
724729 }
725730
@@ -843,44 +848,31 @@ private static class TzInfo {
843848 final StringBuilder sb = new StringBuilder ();
844849 sb .append ("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
845850
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 );
847853
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 ;
869857 }
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+ }
876868 }
877869 }
878870 }
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+ }
884876 }
885877 sb .append (")" );
886878 createPattern (sb );
0 commit comments