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 import org.apache.logging.log4j.core.util.Constants;
27
28
29
30
31
32
33
34
35
36 final class CachedDateFormat extends DateFormat {
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 private static final long serialVersionUID = -1253877934598423628L;
51
52
53
54
55
56
57
58 private static final String DIGITS = "0123456789";
59
60
61
62
63 private static final int MAGIC1 = 654;
64
65
66
67
68 private static final String MAGICSTRING1 = "654";
69
70
71
72
73 private static final int MAGIC2 = 987;
74
75
76
77
78 private static final String MAGICSTRING2 = "987";
79
80
81
82
83 private static final String ZERO_STRING = "000";
84
85 private static final int BUF_SIZE = 50;
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 final 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 / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;
174
175 if (slotBegin > time) {
176 slotBegin -= Constants.MILLIS_IN_SECONDS;
177 }
178
179 final 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 final String plusMagic = formatter.format(new Date(slotBegin + magic));
190
191
192
193
194
195 if (plusMagic.length() != formatted.length()) {
196 return UNRECOGNIZED_MILLISECONDS;
197 }
198
199 for (int i = 0; i < formatted.length(); i++) {
200 if (formatted.charAt(i) != plusMagic.charAt(i)) {
201
202
203 final StringBuffer formattedMillis = new StringBuffer("ABC");
204 millisecondFormat(millis, formattedMillis, 0);
205
206 final 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 }
220 return UNRECOGNIZED_MILLISECONDS;
221 }
222 }
223
224 return NO_MILLISECONDS;
225 }
226
227
228
229
230
231
232
233
234
235 @Override
236 public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
237 format(date.getTime(), sbuf);
238
239 return sbuf;
240 }
241
242
243
244
245
246
247
248
249 public StringBuffer format(final long now, final StringBuffer buf) {
250
251
252
253
254 if (now == previousTime) {
255 buf.append(cache);
256
257 return buf;
258 }
259
260
261
262
263
264 if (millisecondStart != UNRECOGNIZED_MILLISECONDS &&
265
266
267
268 (now < (slotBegin + expiration)) && (now >= slotBegin) && (now < (slotBegin + SLOTS))) {
269
270
271
272 if (millisecondStart >= 0) {
273 millisecondFormat((int) (now - slotBegin), cache, millisecondStart);
274 }
275
276
277
278
279 previousTime = now;
280 buf.append(cache);
281
282 return buf;
283 }
284
285
286
287
288 cache.setLength(0);
289 tmpDate.setTime(now);
290 cache.append(formatter.format(tmpDate));
291 buf.append(cache);
292 previousTime = now;
293 slotBegin = (previousTime / Constants.MILLIS_IN_SECONDS) * Constants.MILLIS_IN_SECONDS;
294
295 if (slotBegin > previousTime) {
296 slotBegin -= Constants.MILLIS_IN_SECONDS;
297 }
298
299
300
301
302
303 if (millisecondStart >= 0) {
304 millisecondStart =
305 findMillisecondStart(now, cache.toString(), formatter);
306 }
307
308 return buf;
309 }
310
311
312
313
314
315
316
317
318
319 private static void millisecondFormat(
320 final int millis, final StringBuffer buf, final int offset) {
321 buf.setCharAt(offset, DIGITS.charAt(millis / THREE_DIGITS));
322 buf.setCharAt(offset + 1, DIGITS.charAt((millis / TWO_DIGITS) % TWO_DIGITS));
323 buf.setCharAt(offset + 2, DIGITS.charAt(millis % TWO_DIGITS));
324 }
325
326
327
328
329
330
331
332
333
334
335 @Override
336 public void setTimeZone(final TimeZone timeZone) {
337 formatter.setTimeZone(timeZone);
338 previousTime = Long.MIN_VALUE;
339 slotBegin = Long.MIN_VALUE;
340 }
341
342
343
344
345
346
347
348
349
350 @Override
351 public Date parse(final String s, final ParsePosition pos) {
352 return formatter.parse(s, pos);
353 }
354
355
356
357
358
359
360 @Override
361 public NumberFormat getNumberFormat() {
362 return formatter.getNumberFormat();
363 }
364
365
366
367
368
369
370
371
372
373 public static int getMaximumCacheValidity(final String pattern) {
374
375
376
377
378
379 final int firstS = pattern.indexOf('S');
380
381 if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) {
382 return 1;
383 }
384
385 return DEFAULT_VALIDITY;
386 }
387 }