1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.logging.log4j.core.util.datetime;
19
20 import java.util.Calendar;
21 import java.util.Objects;
22 import java.util.TimeZone;
23
24
25
26
27
28
29
30
31 public class FixedDateFormat {
32
33
34
35
36
37 public enum FixedFormat {
38
39
40
41 ABSOLUTE("HH:mm:ss,SSS", null, 0, ':', 1, ',', 1),
42
43
44
45
46 ABSOLUTE_PERIOD("HH:mm:ss.SSS", null, 0, ':', 1, '.', 1),
47
48
49
50
51 COMPACT("yyyyMMddHHmmssSSS", "yyyyMMdd", 0, ' ', 0, ' ', 0),
52
53
54
55
56 DATE("dd MMM yyyy HH:mm:ss,SSS", "dd MMM yyyy ", 0, ':', 1, ',', 1),
57
58
59
60
61 DATE_PERIOD("dd MMM yyyy HH:mm:ss.SSS", "dd MMM yyyy ", 0, ':', 1, '.', 1),
62
63
64
65
66 DEFAULT("yyyy-MM-dd HH:mm:ss,SSS", "yyyy-MM-dd ", 0, ':', 1, ',', 1),
67
68
69
70
71 DEFAULT_PERIOD("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd ", 0, ':', 1, '.', 1),
72
73
74
75
76 ISO8601_BASIC("yyyyMMdd'T'HHmmss,SSS", "yyyyMMdd'T'", 2, ' ', 0, ',', 1),
77
78
79
80
81 ISO8601_BASIC_PERIOD("yyyyMMdd'T'HHmmss.SSS", "yyyyMMdd'T'", 2, ' ', 0, '.', 1),
82
83
84
85
86 ISO8601("yyyy-MM-dd'T'HH:mm:ss,SSS", "yyyy-MM-dd'T'", 2, ':', 1, ',', 1),
87
88
89
90
91 ISO8601_PERIOD("yyyy-MM-dd'T'HH:mm:ss.SSS", "yyyy-MM-dd'T'", 2, ':', 1, '.', 1);
92
93 private final String pattern;
94 private final String datePattern;
95 private final int escapeCount;
96 private final char timeSeparatorChar;
97 private final int timeSeparatorLength;
98 private final char millisSeparatorChar;
99 private final int millisSeparatorLength;
100
101 FixedFormat(final String pattern, final String datePattern, final int escapeCount, final char timeSeparator,
102 final int timeSepLength, final char millisSeparator, final int millisSepLength) {
103 this.timeSeparatorChar = timeSeparator;
104 this.timeSeparatorLength = timeSepLength;
105 this.millisSeparatorChar = millisSeparator;
106 this.millisSeparatorLength = millisSepLength;
107 this.pattern = Objects.requireNonNull(pattern);
108 this.datePattern = datePattern;
109 this.escapeCount = escapeCount;
110 }
111
112
113
114
115
116
117 public String getPattern() {
118 return pattern;
119 }
120
121
122
123
124
125
126 public String getDatePattern() {
127 return datePattern;
128 }
129
130
131
132
133
134
135
136 public static FixedFormat lookup(final String nameOrPattern) {
137 for (final FixedFormat type : FixedFormat.values()) {
138 if (type.name().equals(nameOrPattern) || type.getPattern().equals(nameOrPattern)) {
139 return type;
140 }
141 }
142 return null;
143 }
144
145
146
147
148
149
150 public int getLength() {
151 return pattern.length() - escapeCount;
152 }
153
154
155
156
157
158
159 public int getDatePatternLength() {
160 return getDatePattern() == null ? 0 : getDatePattern().length() - escapeCount;
161 }
162
163
164
165
166
167
168
169 public FastDateFormat getFastDateFormat() {
170 return getFastDateFormat(null);
171 }
172
173
174
175
176
177
178
179
180 public FastDateFormat getFastDateFormat(final TimeZone tz) {
181 return getDatePattern() == null ? null : FastDateFormat.getInstance(getDatePattern(), tz);
182 }
183 }
184
185 private final FixedFormat fixedFormat;
186 private final TimeZone timeZone;
187 private final int length;
188 private final FastDateFormat fastDateFormat;
189 private final char timeSeparatorChar;
190 private final char millisSeparatorChar;
191 private final int timeSeparatorLength;
192 private final int millisSeparatorLength;
193
194 private volatile long midnightToday = 0;
195 private volatile long midnightTomorrow = 0;
196
197
198
199
200
201
202 private char[] cachedDate;
203 private int dateLength;
204
205
206
207
208
209
210
211
212 FixedDateFormat(final FixedFormat fixedFormat, final TimeZone tz) {
213 this.fixedFormat = Objects.requireNonNull(fixedFormat);
214 this.timeZone = Objects.requireNonNull(tz);
215 this.timeSeparatorChar = fixedFormat.timeSeparatorChar;
216 this.timeSeparatorLength = fixedFormat.timeSeparatorLength;
217 this.millisSeparatorChar = fixedFormat.millisSeparatorChar;
218 this.millisSeparatorLength = fixedFormat.millisSeparatorLength;
219 this.length = fixedFormat.getLength();
220 this.fastDateFormat = fixedFormat.getFastDateFormat(tz);
221 }
222
223 public static FixedDateFormat createIfSupported(final String... options) {
224 if (options == null || options.length == 0 || options[0] == null) {
225 return new FixedDateFormat(FixedFormat.DEFAULT, TimeZone.getDefault());
226 }
227 final TimeZone tz;
228 if (options.length > 1) {
229 if (options[1] != null){
230 tz = TimeZone.getTimeZone(options[1]);
231 } else {
232 tz = TimeZone.getDefault();
233 }
234 } else if (options.length > 2) {
235 return null;
236 } else {
237 tz = TimeZone.getDefault();
238 }
239
240 final FixedFormat type = FixedFormat.lookup(options[0]);
241 return type == null ? null : new FixedDateFormat(type, tz);
242 }
243
244
245
246
247
248
249
250 public static FixedDateFormat create(final FixedFormat format) {
251 return new FixedDateFormat(format, TimeZone.getDefault());
252 }
253
254
255
256
257
258
259
260
261 public static FixedDateFormat create(final FixedFormat format, final TimeZone tz) {
262 return new FixedDateFormat(format, tz != null ? tz : TimeZone.getDefault());
263 }
264
265
266
267
268
269
270 public String getFormat() {
271 return fixedFormat.getPattern();
272 }
273
274
275
276
277
278
279
280 public TimeZone getTimeZone() {
281 return timeZone;
282 }
283
284
285
286 private long millisSinceMidnight(final long now) {
287 if (now >= midnightTomorrow || now < midnightToday) {
288 updateMidnightMillis(now);
289 }
290 return now - midnightToday;
291 }
292
293 private void updateMidnightMillis(final long now) {
294
295 updateCachedDate(now);
296
297 midnightToday = calcMidnightMillis(now, 0);
298 midnightTomorrow = calcMidnightMillis(now, 1);
299 }
300
301 private long calcMidnightMillis(final long time, final int addDays) {
302 final Calendar cal = Calendar.getInstance(timeZone);
303 cal.setTimeInMillis(time);
304 cal.set(Calendar.HOUR_OF_DAY, 0);
305 cal.set(Calendar.MINUTE, 0);
306 cal.set(Calendar.SECOND, 0);
307 cal.set(Calendar.MILLISECOND, 0);
308 cal.add(Calendar.DATE, addDays);
309 return cal.getTimeInMillis();
310 }
311
312 private void updateCachedDate(final long now) {
313 if (fastDateFormat != null) {
314 final StringBuilder result = fastDateFormat.format(now, new StringBuilder());
315 cachedDate = result.toString().toCharArray();
316 dateLength = result.length();
317 }
318 }
319
320
321
322 public String format(final long time) {
323 final char[] result = new char[length << 1];
324 final int written = format(time, result, 0);
325 return new String(result, 0, written);
326 }
327
328
329
330 public int format(final long time, final char[] buffer, final int startPos) {
331
332
333
334
335
336 final int ms = (int) (millisSinceMidnight(time));
337 writeDate(buffer, startPos);
338 return writeTime(ms, buffer, startPos + dateLength) - startPos;
339 }
340
341
342
343 private void writeDate(final char[] buffer, final int startPos) {
344 if (cachedDate != null) {
345 System.arraycopy(cachedDate, 0, buffer, startPos, dateLength);
346 }
347 }
348
349
350
351 private int writeTime(int ms, final char[] buffer, int pos) {
352 final int hours = ms / 3600000;
353 ms -= 3600000 * hours;
354
355 final int minutes = ms / 60000;
356 ms -= 60000 * minutes;
357
358 final int seconds = ms / 1000;
359 ms -= 1000 * seconds;
360
361
362 int temp = hours / 10;
363 buffer[pos++] = ((char) (temp + '0'));
364
365
366 buffer[pos++] = ((char) (hours - 10 * temp + '0'));
367 buffer[pos] = timeSeparatorChar;
368 pos += timeSeparatorLength;
369
370
371 temp = minutes / 10;
372 buffer[pos++] = ((char) (temp + '0'));
373
374
375 buffer[pos++] = ((char) (minutes - 10 * temp + '0'));
376 buffer[pos] = timeSeparatorChar;
377 pos += timeSeparatorLength;
378
379
380 temp = seconds / 10;
381 buffer[pos++] = ((char) (temp + '0'));
382 buffer[pos++] = ((char) (seconds - 10 * temp + '0'));
383 buffer[pos] = millisSeparatorChar;
384 pos += millisSeparatorLength;
385
386
387 temp = ms / 100;
388 buffer[pos++] = ((char) (temp + '0'));
389
390 ms -= 100 * temp;
391 temp = ms / 10;
392 buffer[pos++] = ((char) (temp + '0'));
393
394 ms -= 10 * temp;
395 buffer[pos++] = ((char) (ms + '0'));
396 return pos;
397 }
398 }