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