Skip to content

Commit 03eb7d4

Browse files
LOG4J2-3672 Avoid invoking DateFormatSymbols.getZoneStrings() in FastDateParser
1 parent 47c85c6 commit 03eb7d4

File tree

1 file changed

+35
-57
lines changed

1 file changed

+35
-57
lines changed

log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FastDateParser.java

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020
import java.io.ObjectInputStream;
2121
import java.io.Serializable;
22-
import java.text.DateFormatSymbols;
2322
import java.text.ParseException;
2423
import java.text.ParsePosition;
2524
import java.util.ArrayList;
@@ -31,15 +30,16 @@
3130
import java.util.ListIterator;
3231
import java.util.Locale;
3332
import java.util.Map;
34-
import java.util.Set;
3533
import java.util.TimeZone;
3634
import java.util.TreeSet;
3735
import java.util.concurrent.ConcurrentHashMap;
3836
import java.util.concurrent.ConcurrentMap;
3937
import java.util.regex.Matcher;
4038
import java.util.regex.Pattern;
4139

40+
import org.apache.logging.log4j.Logger;
4241
import org.apache.logging.log4j.core.util.Integers;
42+
import org.apache.logging.log4j.status.StatusLogger;
4343

4444
import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
4545

@@ -79,7 +79,6 @@
7979
* @see FastDatePrinter
8080
*/
8181
public class FastDateParser implements DateParser, Serializable {
82-
8382
/**
8483
* Required for serialization support.
8584
*
@@ -102,7 +101,7 @@ public class FastDateParser implements DateParser, Serializable {
102101
// comparator used to sort regex alternatives
103102
// alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
104103
// all entries must be lowercase by locale.
105-
private static final Comparator<String> LONGER_FIRST_LOWERCASE = (left, right) -> right.compareTo(left);
104+
private static final Comparator<String> LONGER_FIRST_LOWERCASE = Comparator.reverseOrder();
106105

107106
/**
108107
* <p>Constructs a new FastDateParser.</p>
@@ -552,7 +551,7 @@ boolean parse(final FastDateParser parser, final Calendar calendar, final String
552551

553552
/**
554553
* Obtain a Strategy given a field from a SimpleDateFormat pattern
555-
* @param formatField A sub-sequence of the SimpleDateFormat pattern
554+
* @param f A sub-sequence of the SimpleDateFormat pattern
556555
* @param definingCalendar The calendar to obtain the short and long values
557556
* @return The Strategy that will handle parsing for the field
558557
*/
@@ -720,7 +719,7 @@ private static class CaseInsensitiveTextStrategy extends PatternStrategy {
720719
@Override
721720
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
722721
final Integer iVal = lKeyValues.get(value.toLowerCase(locale));
723-
cal.set(field, iVal.intValue());
722+
cal.set(field, iVal);
724723
}
725724
}
726725

@@ -813,11 +812,9 @@ int modify(final FastDateParser parser, final int iValue) {
813812
* A strategy that handles a timezone field in the parsing pattern
814813
*/
815814
static class TimeZoneStrategy extends PatternStrategy {
816-
private static final String RFC_822_TIME_ZONE = "[+-]\\d{4}";
817-
private static final String GMT_OPTION= "GMT[+-]\\d{1,2}:\\d{2}";
818-
815+
static final Logger LOGGER = StatusLogger.getLogger();
819816
private final Locale locale;
820-
private final Map<String, TzInfo> tzNames= new HashMap<>();
817+
private volatile Map<String, TzInfo> tzNames;
821818

822819
private static class TzInfo {
823820
TimeZone zone;
@@ -829,62 +826,43 @@ private static class TzInfo {
829826
}
830827
}
831828

832-
/**
833-
* Index of zone id
834-
*/
835-
private static final int ID = 0;
836-
837829
/**
838830
* Construct a Strategy that parses a TimeZone
839831
* @param locale The Locale
840832
*/
841833
TimeZoneStrategy(final Locale locale) {
842834
this.locale = locale;
843-
844-
final StringBuilder sb = new StringBuilder();
845-
sb.append("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
846-
847-
final Set<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
848-
849-
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
850-
for (final String[] zoneNames : zones) {
851-
// offset 0 is the time zone ID and is not localized
852-
final String tzId = zoneNames[ID];
853-
if (tzId.equalsIgnoreCase("GMT")) {
854-
continue;
855-
}
856-
final TimeZone tz = TimeZone.getTimeZone(tzId);
857-
// offset 1 is long standard name
858-
// offset 2 is short standard name
859-
final TzInfo standard = new TzInfo(tz, false);
860-
TzInfo tzInfo = standard;
861-
for (int i = 1; i < zoneNames.length; ++i) {
862-
switch (i) {
863-
case 3: // offset 3 is long daylight savings (or summertime) name
864-
// offset 4 is the short summertime name
865-
tzInfo = new TzInfo(tz, true);
866-
break;
867-
case 5: // offset 5 starts additional names, probably standard time
868-
tzInfo = standard;
869-
break;
870-
}
871-
if (zoneNames[i] != null) {
872-
final String key = zoneNames[i].toLowerCase(locale);
873-
// ignore the data associated with duplicates supplied in
874-
// the additional names
875-
if (sorted.add(key)) {
876-
tzNames.put(key, tzInfo);
835+
createPattern("([A-Za-z0-9/\\s\\+\\-]+)");
836+
}
837+
838+
private TzInfo getTzName(Locale locale, String value) {
839+
if (tzNames == null) {
840+
synchronized (this) {
841+
if (tzNames == null) {
842+
LOGGER.warn("Date pattern uses literal timezone '" + value + "'. Use of GMT or RFC 822 offsets is preferred.");
843+
844+
String[] tzIds = TimeZone.getAvailableIDs();
845+
Map<String, TzInfo> map = new HashMap<>(tzIds.length * 4);
846+
for (final String tzId : tzIds) {
847+
if (tzId.equalsIgnoreCase("GMT")) {
848+
continue;
849+
}
850+
final TimeZone tz = TimeZone.getTimeZone(tzId);
851+
final TzInfo standard = new TzInfo(tz, false);
852+
final TzInfo dst = new TzInfo(tz, true);
853+
854+
for (boolean daylight : new Boolean[]{false, true}) {
855+
for (int style : new Integer[]{TimeZone.SHORT, TimeZone.LONG}) {
856+
String name = tz.getDisplayName(daylight, style, locale).toLowerCase(locale);
857+
map.putIfAbsent(name, daylight ? dst : standard);
858+
}
859+
}
877860
}
861+
tzNames = map;
878862
}
879863
}
880864
}
881-
// order the regex alternatives with longer strings first, greedy
882-
// match will ensure longest string will be consumed
883-
for (final String zoneName : sorted) {
884-
simpleQuote(sb.append('|'), zoneName);
885-
}
886-
sb.append(")");
887-
createPattern(sb);
865+
return tzNames.get(value.toLowerCase(locale));
888866
}
889867

890868
/**
@@ -899,7 +877,7 @@ void setCalendar(final FastDateParser parser, final Calendar cal, final String v
899877
final TimeZone tz = TimeZone.getTimeZone(toRootUpperCase(value));
900878
cal.setTimeZone(tz);
901879
} else {
902-
final TzInfo tzInfo = tzNames.get(value.toLowerCase(locale));
880+
final TzInfo tzInfo = getTzName(locale, value);
903881
cal.set(Calendar.DST_OFFSET, tzInfo.dstOffset);
904882
cal.set(Calendar.ZONE_OFFSET, tzInfo.zone.getRawOffset());
905883
}

0 commit comments

Comments
 (0)