4
4
import java .text .SimpleDateFormat ;
5
5
import java .util .Calendar ;
6
6
import java .util .Date ;
7
+ import java .util .Map ;
7
8
import java .util .TimeZone ;
9
+ import java .util .concurrent .ConcurrentHashMap ;
8
10
9
11
import javax .xml .bind .DatatypeConverter ;
10
12
11
13
/**
12
14
* This class provides utility methods for parsing and formatting ISO8601 formatted dates.
13
15
*/
14
16
public class ISO8601 {
17
+
15
18
public static final String PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ" ;
16
19
public static final String PATTERN_MSEC = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ;
17
20
public static final String OUTPUT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'" ;
18
21
public static final String OUTPUT_MSEC_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ;
19
22
public static final String UTC_PATTERN = "yyyy-MM-dd HH:mm:ss 'UTC'" ;
20
23
21
- private static final SimpleDateFormat iso8601Format ;
22
- private static final SimpleDateFormat iso8601MsecFormat ;
23
- private static final SimpleDateFormat iso8601OutputFormat ;
24
- private static final SimpleDateFormat iso8601OutputMsecFormat ;
25
- private static final SimpleDateFormat iso8601UtcFormat ;
26
- static {
27
- iso8601Format = new SimpleDateFormat (PATTERN );
28
- iso8601Format .setLenient (true );
29
- iso8601Format .setTimeZone (TimeZone .getTimeZone ("UTC" ));
30
- iso8601MsecFormat = new SimpleDateFormat (PATTERN_MSEC );
31
- iso8601MsecFormat .setLenient (true );
32
- iso8601MsecFormat .setTimeZone (TimeZone .getTimeZone ("UTC" ));
33
- iso8601OutputFormat = new SimpleDateFormat (OUTPUT_PATTERN );
34
- iso8601OutputFormat .setLenient (true );
35
- iso8601OutputFormat .setTimeZone (TimeZone .getTimeZone ("UTC" ));
36
- iso8601OutputMsecFormat = new SimpleDateFormat (OUTPUT_MSEC_PATTERN );
37
- iso8601OutputMsecFormat .setLenient (true );
38
- iso8601OutputMsecFormat .setTimeZone (TimeZone .getTimeZone ("UTC" ));
39
- iso8601UtcFormat = new SimpleDateFormat (UTC_PATTERN );
40
- iso8601UtcFormat .setLenient (true );
41
- iso8601UtcFormat .setTimeZone (TimeZone .getTimeZone ("UTC" ));
24
+ // Set up ThreadLocal storage to save a thread local SimpleDateFormat keyed with the format stringf
25
+ private static final class SafeDateFormatter {
26
+
27
+ private static final ThreadLocal <Map <String , SimpleDateFormat >> safeFormats = new ThreadLocal <Map <String , SimpleDateFormat >>() {
28
+
29
+ @ Override
30
+ public Map <String , SimpleDateFormat > initialValue () {
31
+ return (new ConcurrentHashMap <>());
32
+ }
33
+ };
34
+
35
+ private static SimpleDateFormat getDateFormat (String formatSpec ) {
36
+
37
+ Map <String , SimpleDateFormat > formatMap = safeFormats .get ();
38
+ SimpleDateFormat format = formatMap .get (formatSpec );
39
+ if (format == null ) {
40
+ format = new SimpleDateFormat (formatSpec );
41
+ format .setLenient (true );
42
+ format .setTimeZone (TimeZone .getTimeZone ("UTC" ));
43
+ formatMap .put (formatSpec , format );
44
+ }
45
+
46
+ return (format );
47
+ }
42
48
}
43
49
44
50
/**
@@ -47,7 +53,7 @@ public class ISO8601 {
47
53
* @return a ISO8601 formatted string for the current date and time
48
54
*/
49
55
public static String getTimestamp () {
50
- return (iso8601Format .format (new Date ()));
56
+ return (SafeDateFormatter . getDateFormat ( PATTERN ) .format (new Date ()));
51
57
}
52
58
53
59
/**
@@ -57,7 +63,8 @@ public static String getTimestamp() {
57
63
* @return a ISO8601 formatted string for the current date and time
58
64
*/
59
65
public static String getTimestamp (boolean withMsec ) {
60
- return (withMsec ? iso8601MsecFormat .format (new Date ()) : iso8601Format .format (new Date ()));
66
+ return (withMsec ? SafeDateFormatter .getDateFormat (PATTERN_MSEC ).format (new Date ()) :
67
+ SafeDateFormatter .getDateFormat (PATTERN ).format (new Date ()));
61
68
}
62
69
63
70
/**
@@ -89,7 +96,9 @@ public static synchronized String toString(Date date, boolean withMsec) {
89
96
}
90
97
91
98
long time = date .getTime ();
92
- return (withMsec && time % 1000 != 0 ? iso8601OutputMsecFormat .format (date ) : iso8601OutputFormat .format (date ));
99
+ return (withMsec && time % 1000 != 0 ?
100
+ SafeDateFormatter .getDateFormat (OUTPUT_MSEC_PATTERN ).format (date ) :
101
+ SafeDateFormatter .getDateFormat (OUTPUT_PATTERN ).format (date ));
93
102
}
94
103
95
104
/**
@@ -117,9 +126,7 @@ public static Date toDate(String dateTimeString) throws ParseException {
117
126
118
127
dateTimeString = dateTimeString .trim ();
119
128
if (dateTimeString .endsWith ("UTC" )) {
120
- synchronized (iso8601UtcFormat ) {
121
- return (iso8601UtcFormat .parse (dateTimeString ));
122
- }
129
+ return (SafeDateFormatter .getDateFormat (UTC_PATTERN ).parse (dateTimeString ));
123
130
} else {
124
131
Calendar cal = DatatypeConverter .parseDateTime (dateTimeString );
125
132
return (cal .getTime ());
0 commit comments