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 ;
3130import java .util .ListIterator ;
3231import java .util .Locale ;
3332import java .util .Map ;
34- import java .util .Set ;
3533import java .util .TimeZone ;
3634import java .util .TreeSet ;
3735import java .util .concurrent .ConcurrentHashMap ;
3836import java .util .concurrent .ConcurrentMap ;
3937import java .util .regex .Matcher ;
4038import java .util .regex .Pattern ;
4139
40+ import org .apache .logging .log4j .Logger ;
4241import org .apache .logging .log4j .core .util .Integers ;
42+ import org .apache .logging .log4j .status .StatusLogger ;
4343
4444import static org .apache .logging .log4j .util .Strings .toRootUpperCase ;
4545
7979 * @see FastDatePrinter
8080 */
8181public 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