1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.pattern;
18
19 import java.text.DateFormat;
20 import java.text.FieldPosition;
21 import java.text.NumberFormat;
22 import java.text.ParsePosition;
23 import java.util.Date;
24 import java.util.TimeZone;
25
26
27
28
29
30
31
32
33
34 final class CachedDateFormat extends DateFormat {
35
36
37
38
39 public static final int NO_MILLISECONDS = -2;
40
41
42
43
44
45 public static final int UNRECOGNIZED_MILLISECONDS = -1;
46
47
48
49
50
51
52
53 private static final String DIGITS = "0123456789";
54
55
56
57
58 private static final int MAGIC1 = 654;
59
60
61
62
63 private static final String MAGICSTRING1 = "654";
64
65
66
67
68 private static final int MAGIC2 = 987;
69
70
71
72
73 private static final String MAGICSTRING2 = "987";
74
75
76
77
78 private static final String ZERO_STRING = "000";
79
80 private static final int BUF_SIZE = 50;
81
82 private static final int MILLIS_IN_SECONDS = 1000;
83
84 private static final int DEFAULT_VALIDITY = 1000;
85
86 private static final int THREE_DIGITS = 100;
87
88 private static final int TWO_DIGITS = 10;
89
90 private static final long SLOTS = 1000L;
91
92
93
94
95 private final DateFormat formatter;
96
97
98
99
100
101 private int millisecondStart;
102
103
104
105
106 private long slotBegin;
107
108
109
110
111 private StringBuffer cache = new StringBuffer(BUF_SIZE);
112
113
114
115
116
117
118 private final int expiration;
119
120
121
122
123 private long previousTime;
124
125
126
127
128 private final Date tmpDate = new Date(0);
129
130
131
132
133
134
135
136
137
138
139 public CachedDateFormat(final DateFormat dateFormat, final int expiration) {
140 if (dateFormat == null) {
141 throw new IllegalArgumentException("dateFormat cannot be null");
142 }
143
144 if (expiration < 0) {
145 throw new IllegalArgumentException("expiration must be non-negative");
146 }
147
148 formatter = dateFormat;
149 this.expiration = expiration;
150 millisecondStart = 0;
151
152
153
154
155 previousTime = Long.MIN_VALUE;
156 slotBegin = Long.MIN_VALUE;
157 }
158
159
160
161
162
163
164
165
166
167
168
169 public static int findMillisecondStart(final long time, final String formatted, final DateFormat formatter) {
170 long slotBegin = (time / MILLIS_IN_SECONDS) * MILLIS_IN_SECONDS;
171
172 if (slotBegin > time) {
173 slotBegin -= MILLIS_IN_SECONDS;
174 }
175
176 int millis = (int) (time - slotBegin);
177
178 int magic = MAGIC1;
179 String magicString = MAGICSTRING1;
180
181 if (millis == MAGIC1) {
182 magic = MAGIC2;
183 magicString = MAGICSTRING2;
184 }
185
186 String plusMagic = formatter.format(new Date(slotBegin + magic));
187
188
189
190
191
192 if (plusMagic.length() != formatted.length()) {
193 return UNRECOGNIZED_MILLISECONDS;
194 } else {
195
196 for (int i = 0; i < formatted.length(); i++) {
197 if (formatted.charAt(i) != plusMagic.charAt(i)) {
198
199
200 StringBuffer formattedMillis = new StringBuffer("ABC");
201 millisecondFormat(millis, formattedMillis, 0);
202
203 String plusZero = formatter.format(new Date(slotBegin));
204
205
206
207 if (
208 (plusZero.length() == formatted.length())
209 && magicString.regionMatches(
210 0, plusMagic, i, magicString.length())
211 && formattedMillis.toString().regionMatches(
212 0, formatted, i, magicString.length())
213 && ZERO_STRING.regionMatches(
214 0, plusZero, i, ZERO_STRING.length())) {
215 return i;
216 } else {
217 return UNRECOGNIZED_MILLISECONDS;
218 }
219 }
220 }
221 }
222
223 return NO_MILLISECONDS;
224 }
225
226
227
228
229
230
231
232
233
234 public StringBuffer format(Date date, StringBuffer sbuf, FieldPosition fieldPosition) {
235 format(date.getTime(), sbuf);
236
237 return sbuf;
238 }
239
240
241
242
243
244
245
246
247 public StringBuffer format(long now, StringBuffer buf) {
248
249
250
251
252 if (now == previousTime) {
253 buf.append(cache);
254
255 return buf;
256 }
257
258
259
260
261
262 if (millisecondStart != UNRECOGNIZED_MILLISECONDS &&
263
264
265
266 (now < (slotBegin + expiration)) && (now >= slotBegin) && (now < (slotBegin + SLOTS))) {
267
268
269
270 if (millisecondStart >= 0) {
271 millisecondFormat((int) (now - slotBegin), cache, millisecondStart);
272 }
273
274
275
276
277 previousTime = now;
278 buf.append(cache);
279
280 return buf;
281 }
282
283
284
285
286 cache.setLength(0);
287 tmpDate.setTime(now);
288 cache.append(formatter.format(tmpDate));
289 buf.append(cache);
290 previousTime = now;
291 slotBegin = (previousTime / MILLIS_IN_SECONDS) * MILLIS_IN_SECONDS;
292
293 if (slotBegin > previousTime) {
294 slotBegin -= MILLIS_IN_SECONDS;
295 }
296
297
298
299
300
301 if (millisecondStart >= 0) {
302 millisecondStart =
303 findMillisecondStart(now, cache.toString(), formatter);
304 }
305
306 return buf;
307 }
308
309
310
311
312
313
314
315
316
317 private static void millisecondFormat(
318 final int millis, final StringBuffer buf, final int offset) {
319 buf.setCharAt(offset, DIGITS.charAt(millis / THREE_DIGITS));
320 buf.setCharAt(offset + 1, DIGITS.charAt((millis / TWO_DIGITS) % TWO_DIGITS));
321 buf.setCharAt(offset + 2, DIGITS.charAt(millis % TWO_DIGITS));
322 }
323
324
325
326
327
328
329
330
331
332 public void setTimeZone(final TimeZone timeZone) {
333 formatter.setTimeZone(timeZone);
334 previousTime = Long.MIN_VALUE;
335 slotBegin = Long.MIN_VALUE;
336 }
337
338
339
340
341
342
343
344
345
346 public Date parse(String s, ParsePosition pos) {
347 return formatter.parse(s, pos);
348 }
349
350
351
352
353
354
355 public NumberFormat getNumberFormat() {
356 return formatter.getNumberFormat();
357 }
358
359
360
361
362
363
364
365
366
367 public static int getMaximumCacheValidity(final String pattern) {
368
369
370
371
372
373 int firstS = pattern.indexOf('S');
374
375 if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) {
376 return 1;
377 }
378
379 return DEFAULT_VALIDITY;
380 }
381 }