Skip to content

Commit 34597dd

Browse files
committed
Add support for unpadded seconds
1 parent 007b983 commit 34597dd

File tree

2 files changed

+52
-41
lines changed

2 files changed

+52
-41
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ static List<Arguments> sequencingTestCases() {
6565
testCases.add(Arguments.of(
6666
"yyyyMMddHHmmssSSSX",
6767
ChronoUnit.HOURS,
68-
asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec("", 3), pDyn("X"))));
68+
asList(pDyn("yyyyMMddHH", ChronoUnit.HOURS), pDyn("mm"), pSec(2, "", 3), pDyn("X"))));
6969

7070
// `yyyyMMddHHmmssSSSX` instant cache updated per minute
7171
testCases.add(Arguments.of(
7272
"yyyyMMddHHmmssSSSX",
7373
ChronoUnit.MINUTES,
74-
asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec("", 3), pDyn("X"))));
74+
asList(pDyn("yyyyMMddHHmm", ChronoUnit.MINUTES), pSec(2, "", 3), pDyn("X"))));
7575

7676
// ISO9601 instant cache updated daily
7777
final String iso8601InstantPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
@@ -81,27 +81,29 @@ static List<Arguments> sequencingTestCases() {
8181
asList(
8282
pDyn("yyyy'-'MM'-'dd'T'", ChronoUnit.DAYS),
8383
pDyn("HH':'mm':'", ChronoUnit.MINUTES),
84-
pSec(".", 3),
84+
pSec(2, ".", 3),
8585
pDyn("X"))));
8686

8787
// ISO9601 instant cache updated per minute
8888
testCases.add(Arguments.of(
8989
iso8601InstantPattern,
9090
ChronoUnit.MINUTES,
91-
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
91+
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
9292

9393
// ISO9601 instant cache updated per second
9494
testCases.add(Arguments.of(
9595
iso8601InstantPattern,
9696
ChronoUnit.SECONDS,
97-
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 3), pDyn("X"))));
97+
asList(pDyn("yyyy'-'MM'-'dd'T'HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 3), pDyn("X"))));
9898

9999
// Seconds and micros
100100
testCases.add(Arguments.of(
101-
"HH:mm:ss.SSSSSS", ChronoUnit.MINUTES, asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(".", 6))));
101+
"HH:mm:ss.SSSSSS",
102+
ChronoUnit.MINUTES,
103+
asList(pDyn("HH':'mm':'", ChronoUnit.MINUTES), pSec(2, ".", 6))));
102104

103105
// Seconds without padding
104-
testCases.add(Arguments.of("s.SSS", ChronoUnit.SECONDS, asList(pDyn("s'.'", ChronoUnit.SECONDS), pMilliSec())));
106+
testCases.add(Arguments.of("s.SSS", ChronoUnit.SECONDS, singletonList(pSec(1, ".", 3))));
105107

106108
return testCases;
107109
}
@@ -114,12 +116,12 @@ private static DynamicPatternSequence pDyn(final String pattern, final ChronoUni
114116
return new DynamicPatternSequence(pattern, precision);
115117
}
116118

117-
private static SecondPatternSequence pSec(String separator, int fractionalDigits) {
118-
return new SecondPatternSequence(true, separator, fractionalDigits);
119+
private static SecondPatternSequence pSec(int secondDigits, String separator, int fractionalDigits) {
120+
return new SecondPatternSequence(secondDigits, separator, fractionalDigits);
119121
}
120122

121123
private static SecondPatternSequence pMilliSec() {
122-
return new SecondPatternSequence(false, "", 3);
124+
return new SecondPatternSequence(0, "", 3);
123125
}
124126

125127
@ParameterizedTest
@@ -369,7 +371,7 @@ static Stream<Arguments> formatterInputs() {
369371
Arrays.stream(TimeZone.getAvailableIDs()).map(TimeZone::getTimeZone).toArray(TimeZone[]::new);
370372

371373
static Stream<Arguments> formatterInputs(final String pattern) {
372-
return IntStream.range(0, 500).mapToObj(ignoredIndex -> {
374+
return IntStream.range(0, 100).mapToObj(ignoredIndex -> {
373375
final Locale locale = LOCALES[RANDOM.nextInt(LOCALES.length)];
374376
final TimeZone timeZone = TIME_ZONES[RANDOM.nextInt(TIME_ZONES.length)];
375377
final MutableInstant instant = randomInstant();

log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,10 @@ private static List<PatternSequence> sequencePattern(final String pattern) {
239239
final PatternSequence sequence;
240240
switch (c) {
241241
case 's':
242-
if (sequenceContent.length() == 2) {
243-
sequence = new SecondPatternSequence(true, "", 0);
244-
} else {
245-
sequence = new DynamicPatternSequence(sequenceContent);
246-
}
242+
sequence = new SecondPatternSequence(sequenceContent.length(), "", 0);
247243
break;
248244
case 'S':
249-
sequence = new SecondPatternSequence(false, "", sequenceContent.length());
245+
sequence = new SecondPatternSequence(0, "", sequenceContent.length());
250246
break;
251247
default:
252248
sequence = new DynamicPatternSequence(sequenceContent);
@@ -698,39 +694,50 @@ static class SecondPatternSequence extends PatternSequence {
698694
100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1
699695
};
700696

701-
private final boolean printSeconds;
697+
private final int secondDigits;
702698
private final String separator;
703699
private final int fractionalDigits;
704700

705-
SecondPatternSequence(boolean printSeconds, String separator, int fractionalDigits) {
701+
SecondPatternSequence(int secondDigits, String separator, int fractionalDigits) {
706702
super(
707-
createPattern(printSeconds, separator, fractionalDigits),
708-
determinePrecision(printSeconds, fractionalDigits));
709-
this.printSeconds = printSeconds;
703+
createPattern(secondDigits, separator, fractionalDigits),
704+
determinePrecision(secondDigits, fractionalDigits));
705+
if (secondDigits < 0 || secondDigits > 2) {
706+
throw new IllegalArgumentException("Unsupported number of `s` pattern letters.");
707+
}
708+
if (fractionalDigits > 9) {
709+
throw new IllegalArgumentException("Unsupported number of `S` pattern letters.");
710+
}
711+
this.secondDigits = secondDigits;
710712
this.separator = separator;
711713
this.fractionalDigits = fractionalDigits;
712714
}
713715

714-
private static String createPattern(boolean printSeconds, String separator, int fractionalDigits) {
715-
StringBuilder builder = new StringBuilder();
716-
if (printSeconds) {
717-
builder.append("ss");
718-
}
719-
builder.append(StaticPatternSequence.escapeLiteral(separator));
720-
if (fractionalDigits > 0) {
721-
builder.append(Strings.repeat("S", fractionalDigits));
722-
}
723-
return builder.toString();
716+
private static String createPattern(int secondDigits, String separator, int fractionalDigits) {
717+
return Strings.repeat("s", secondDigits)
718+
+ StaticPatternSequence.escapeLiteral(separator)
719+
+ Strings.repeat("S", fractionalDigits);
724720
}
725721

726-
private static ChronoUnit determinePrecision(boolean printSeconds, int digits) {
722+
private static ChronoUnit determinePrecision(int secondDigits, int digits) {
727723
if (digits > 6) return ChronoUnit.NANOS;
728724
if (digits > 3) return ChronoUnit.MICROS;
729725
if (digits > 0) return ChronoUnit.MILLIS;
730-
return printSeconds ? ChronoUnit.SECONDS : ChronoUnit.FOREVER;
726+
return secondDigits > 0 ? ChronoUnit.SECONDS : ChronoUnit.FOREVER;
727+
}
728+
729+
private void formatSeconds(StringBuilder buffer, Instant instant) {
730+
switch (secondDigits) {
731+
case 1:
732+
buffer.append(instant.getEpochSecond() % 60L);
733+
break;
734+
case 2:
735+
formatPaddedSeconds(buffer, instant);
736+
break;
737+
}
731738
}
732739

733-
private static void formatSeconds(StringBuilder buffer, Instant instant) {
740+
private static void formatPaddedSeconds(StringBuilder buffer, Instant instant) {
734741
long secondsInMinute = instant.getEpochSecond() % 60L;
735742
buffer.append((char) ((secondsInMinute / 10L) + '0'));
736743
buffer.append((char) ((secondsInMinute % 10L) + '0'));
@@ -761,9 +768,11 @@ private static void formatMillis(StringBuilder buffer, Instant instant) {
761768

762769
@Override
763770
InstantPatternFormatter createFormatter(Locale locale, TimeZone timeZone) {
771+
final BiConsumer<StringBuilder, Instant> secondDigitsFormatter =
772+
secondDigits == 2 ? SecondPatternSequence::formatPaddedSeconds : this::formatSeconds;
764773
final BiConsumer<StringBuilder, Instant> fractionDigitsFormatter =
765774
fractionalDigits == 3 ? SecondPatternSequence::formatMillis : this::formatFractionalDigits;
766-
if (!printSeconds) {
775+
if (secondDigits == 0) {
767776
return new AbstractFormatter(pattern, locale, timeZone, precision) {
768777
@Override
769778
public void formatTo(StringBuilder buffer, Instant instant) {
@@ -776,15 +785,15 @@ public void formatTo(StringBuilder buffer, Instant instant) {
776785
return new AbstractFormatter(pattern, locale, timeZone, precision) {
777786
@Override
778787
public void formatTo(StringBuilder buffer, Instant instant) {
779-
formatSeconds(buffer, instant);
788+
secondDigitsFormatter.accept(buffer, instant);
780789
buffer.append(separator);
781790
}
782791
};
783792
}
784793
return new AbstractFormatter(pattern, locale, timeZone, precision) {
785794
@Override
786795
public void formatTo(StringBuilder buffer, Instant instant) {
787-
formatSeconds(buffer, instant);
796+
secondDigitsFormatter.accept(buffer, instant);
788797
buffer.append(separator);
789798
fractionDigitsFormatter.accept(buffer, instant);
790799
}
@@ -799,15 +808,15 @@ PatternSequence tryMerge(PatternSequence other, ChronoUnit thresholdPrecision) {
799808
StaticPatternSequence staticOther = (StaticPatternSequence) other;
800809
if (fractionalDigits == 0) {
801810
return new SecondPatternSequence(
802-
printSeconds, this.separator + staticOther.literal, fractionalDigits);
811+
this.secondDigits, this.separator + staticOther.literal, fractionalDigits);
803812
}
804813
}
805814
// We can always append more fractional digits
806815
if (other instanceof SecondPatternSequence) {
807816
SecondPatternSequence secondOther = (SecondPatternSequence) other;
808-
if (!secondOther.printSeconds && secondOther.separator.isEmpty()) {
817+
if (secondOther.secondDigits == 0 && secondOther.separator.isEmpty()) {
809818
return new SecondPatternSequence(
810-
printSeconds, this.separator, this.fractionalDigits + secondOther.fractionalDigits);
819+
this.secondDigits, this.separator, this.fractionalDigits + secondOther.fractionalDigits);
811820
}
812821
}
813822
return null;

0 commit comments

Comments
 (0)