Skip to content

Commit ddc7459

Browse files
seonwooj0810ceki
authored andcommitted
Support HALF_DAY periodicity for AM/PM date patterns
The HALF_DAY periodicity type and its printPeriodicity() message have existed since LBCORE-11 (2008), but HALF_DAY was never added to PeriodicityType.VALID_ORDERED_LIST and no HALF_DAY branch was wired into RollingCalendar. As a result a TimeBasedRollingPolicy whose date pattern carries the AM/PM token (e.g. %d{yyyy-MM-dd-a}) is detected as TOP_OF_DAY and rolls over only at midnight instead of at 00:00 and 12:00. Add HALF_DAY to the ordered detection list and implement the missing HALF_DAY cases in innerGetEndOfNextNthPeriod (floor to the current half-day boundary, advance in 12h steps), periodBarriersCrossed, and isCollisionFree. Add regression tests to RollingCalendarTest. Fixes #976 Signed-off-by: seonwoo_jung <79202163+seonwooj0810@users.noreply.github.com>
1 parent 42d75d7 commit ddc7459

3 files changed

Lines changed: 51 additions & 1 deletion

File tree

‎logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public enum PeriodicityType {
2121
// The followed list consists of valid periodicity types in increasing period lengths
2222
static PeriodicityType[] VALID_ORDERED_LIST = new PeriodicityType[] { TOP_OF_MILLISECOND,
2323
PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR,
24-
PeriodicityType.TOP_OF_DAY, PeriodicityType.TOP_OF_WEEK, PeriodicityType.TOP_OF_MONTH };
24+
PeriodicityType.HALF_DAY, PeriodicityType.TOP_OF_DAY, PeriodicityType.TOP_OF_WEEK,
25+
PeriodicityType.TOP_OF_MONTH };
2526

2627
}

‎logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public boolean isCollisionFree() {
105105
// isolated hh or KK
106106
return !collision(12 * MILLIS_IN_ONE_HOUR);
107107

108+
case HALF_DAY:
109+
// isolated 'a' (AM/PM) without a date component repeats every day
110+
return !collision(MILLIS_IN_ONE_DAY);
111+
108112
case TOP_OF_DAY:
109113
// EE or uu
110114
if (collision(7 * MILLIS_IN_ONE_DAY))
@@ -198,6 +202,8 @@ public long periodBarriersCrossed(long start, long end) {
198202
return diff / MILLIS_IN_ONE_MINUTE;
199203
case TOP_OF_HOUR:
200204
return diff / MILLIS_IN_ONE_HOUR;
205+
case HALF_DAY:
206+
return diff / (12 * MILLIS_IN_ONE_HOUR);
201207
case TOP_OF_DAY:
202208
return diff / MILLIS_IN_ONE_DAY;
203209
case TOP_OF_WEEK:
@@ -251,6 +257,15 @@ static private Instant innerGetEndOfNextNthPeriod(Calendar cal, PeriodicityType
251257
cal.add(Calendar.HOUR_OF_DAY, numPeriods);
252258
break;
253259

260+
case HALF_DAY:
261+
// floor to the start of the current half-day (00:00 or 12:00), then advance
262+
cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) < 12 ? 0 : 12);
263+
cal.set(Calendar.MINUTE, 0);
264+
cal.set(Calendar.SECOND, 0);
265+
cal.set(Calendar.MILLISECOND, 0);
266+
cal.add(Calendar.HOUR_OF_DAY, numPeriods * 12);
267+
break;
268+
254269
case TOP_OF_DAY:
255270
cal.set(Calendar.HOUR_OF_DAY, 0);
256271
cal.set(Calendar.MINUTE, 0);

‎logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ public void testPeriodicity() {
8080
assertEquals(PeriodicityType.TOP_OF_HOUR, rc.getPeriodicityType());
8181
}
8282

83+
{
84+
// 'a' (AM/PM) without a finer time token means twice-a-day roll-over
85+
RollingCalendar rc = new RollingCalendar("yyyy-MM-dd-a");
86+
assertEquals(PeriodicityType.HALF_DAY, rc.getPeriodicityType());
87+
}
88+
89+
{
90+
RollingCalendar rc = new RollingCalendar("yyyy-MM-dd a");
91+
assertEquals(PeriodicityType.HALF_DAY, rc.getPeriodicityType());
92+
}
93+
8394
{
8495
RollingCalendar rc = new RollingCalendar("yyyy-MM-dd");
8596
assertEquals(PeriodicityType.TOP_OF_DAY, rc.getPeriodicityType());
@@ -132,6 +143,23 @@ public void testVaryingNumberOfDailyPeriods() {
132143
}
133144
}
134145

146+
@Test
147+
public void testVaryingNumberOfHalfDailyPeriods() {
148+
RollingCalendar rc = new RollingCalendar("yyyy-MM-dd-a");
149+
final long MILLIS_IN_HALF_DAY = 12 * 3600 * 1000;
150+
151+
for (int p = 20; p > -100; p--) {
152+
long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008
153+
Instant nowInstant = Instant.ofEpochMilli(now);
154+
Instant result = rc.getEndOfNextNthPeriod(nowInstant, p);
155+
long offset = rc.getTimeZone().getRawOffset() + rc.getTimeZone().getDSTSavings();
156+
157+
long origin = now - ((now + offset) % (MILLIS_IN_HALF_DAY));
158+
long expected = origin + p * MILLIS_IN_HALF_DAY;
159+
assertEquals(expected, result.toEpochMilli(), "p=" + p);
160+
}
161+
}
162+
135163
// Wed Mar 23 23:07:05 CET 2016
136164
final long WED_2016_03_23_T_230705_CET = 1458770825333L;
137165

@@ -143,6 +171,8 @@ public void testBarrierCrossingComputation() {
143171
WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_MINUTE, 3);
144172
checkPeriodBarriersCrossed("yyyy-MM-dd'T'HH", WED_2016_03_23_T_230705_CET,
145173
WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_HOUR, 3);
174+
checkPeriodBarriersCrossed("yyyy-MM-dd-a", WED_2016_03_23_T_230705_CET,
175+
WED_2016_03_23_T_230705_CET + 3 * 12 * CoreConstants.MILLIS_IN_ONE_HOUR, 3);
146176
checkPeriodBarriersCrossed("yyyy-MM-dd", WED_2016_03_23_T_230705_CET,
147177
WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY, 3);
148178
}
@@ -164,6 +194,10 @@ public void testCollisionFreenes() {
164194
checkCollisionFreeness("yyyy-MM-dd KK", false);
165195
checkCollisionFreeness("yyyy-MM-dd KK a", true);
166196

197+
// half-daily
198+
checkCollisionFreeness("yyyy-MM-dd-a", true);
199+
checkCollisionFreeness("a", false);
200+
167201
// daily
168202
checkCollisionFreeness("yyyy-MM-dd", true);
169203
checkCollisionFreeness("yyyy-dd", false);

0 commit comments

Comments
 (0)