Skip to content

Commit eae928d

Browse files
committed
Refactor ParsedDuration to support TimeUnit in parsing methods
1 parent cc2be8a commit eae928d

File tree

3 files changed

+138
-101
lines changed

3 files changed

+138
-101
lines changed

SimpleAPI/src/main/java/com/bencodez/simpleapi/skull/SkullCacheHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public SkullCacheHandler(String skullDelayTime) {
7373

7474
private static int parseMs(String duration, int fallback) {
7575
try {
76-
long ms = ParsedDuration.parse(duration).getMillis();
76+
long ms = ParsedDuration.parse(duration, TimeUnit.MILLISECONDS).getMillis();
7777
return ms > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) ms;
7878
} catch (Exception e) {
7979
return fallback;

SimpleAPI/src/main/java/com/bencodez/simpleapi/time/ParsedDuration.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,20 +207,11 @@ public static ParsedDuration parse(String raw, TimeUnit defaultUnit) {
207207
* @param raw raw input string
208208
* @return parsed duration (never null)
209209
*/
210+
@Deprecated
210211
public static ParsedDuration parse(String raw) {
211212
return parse(raw, Unit.MINUTES);
212213
}
213214

214-
/**
215-
* Parses the input using {@link TimeUnit#MINUTES} as the default unit for number-only strings.
216-
*
217-
* @param raw raw input string
218-
* @return parsed duration (never null)
219-
*/
220-
public static ParsedDuration parseTimeUnit(String raw) {
221-
return parse(raw, TimeUnit.MINUTES);
222-
}
223-
224215
/**
225216
* If {@code raw} is just a number, returns the same value with {@code defaultUnit} applied,
226217
* otherwise returns {@link #parse(String, Unit)} result.

SimpleAPI/src/test/java/com/bencodez/simpleapi/tests/ParsedDurationTest.java

Lines changed: 136 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,157 +4,200 @@
44
import static org.junit.jupiter.api.Assertions.assertNotNull;
55
import static org.junit.jupiter.api.Assertions.assertTrue;
66

7+
import java.util.concurrent.TimeUnit;
8+
79
import org.junit.jupiter.api.DisplayName;
810
import org.junit.jupiter.api.Test;
911

1012
import com.bencodez.simpleapi.time.ParsedDuration;
1113
import com.bencodez.simpleapi.time.ParsedDuration.Unit;
1214

15+
/**
16+
* Unit tests for {@link ParsedDuration}.
17+
*/
1318
public class ParsedDurationTest {
1419

20+
// ---------------------------------------------------------------------------------
21+
// Shared/basic behavior (null/empty, tokens, ISO) for BOTH Unit and TimeUnit APIs
22+
// ---------------------------------------------------------------------------------
23+
1524
@Test
16-
@DisplayName("Empty and null inputs return empty duration")
25+
@DisplayName("Empty and null inputs return empty duration (Unit + TimeUnit)")
1726
public void testEmptyInputs() {
1827
assertTrue(ParsedDuration.parse(null).isEmpty());
1928
assertTrue(ParsedDuration.parse("").isEmpty());
2029
assertTrue(ParsedDuration.parse(" ").isEmpty());
21-
}
2230

23-
@Test
24-
@DisplayName("Plain number uses default MINUTES")
25-
public void testPlainNumberDefaultMinutes() {
26-
ParsedDuration d = ParsedDuration.parse("30");
27-
assertEquals(30 * 60_000L, d.getMillis());
28-
}
31+
assertTrue(ParsedDuration.parse(null, Unit.MINUTES).isEmpty());
32+
assertTrue(ParsedDuration.parse("", Unit.MINUTES).isEmpty());
33+
assertTrue(ParsedDuration.parse(" ", Unit.MINUTES).isEmpty());
2934

30-
@Test
31-
@DisplayName("Milliseconds parsing")
32-
public void testMilliseconds() {
33-
ParsedDuration d = ParsedDuration.parse("5000ms");
34-
assertEquals(5000L, d.getMillis());
35+
assertTrue(ParsedDuration.parse(null, TimeUnit.MINUTES).isEmpty());
36+
assertTrue(ParsedDuration.parse("", TimeUnit.MINUTES).isEmpty());
37+
assertTrue(ParsedDuration.parse(" ", TimeUnit.MINUTES).isEmpty());
3538
}
3639

3740
@Test
38-
@DisplayName("Seconds parsing")
39-
public void testSeconds() {
40-
ParsedDuration d = ParsedDuration.parse("60s");
41-
assertEquals(60_000L, d.getMillis());
42-
}
41+
@DisplayName("ISO-8601 duration parsing (PT/P1D) works the same (Unit + TimeUnit)")
42+
public void testIso8601Duration() {
43+
assertEquals(30 * 60_000L, ParsedDuration.parse("PT30M", Unit.MINUTES).getMillis());
44+
assertEquals(12 * 3_600_000L, ParsedDuration.parse("PT12H", Unit.MINUTES).getMillis());
45+
assertEquals(86_400_000L, ParsedDuration.parse("P1D", Unit.MINUTES).getMillis());
4346

44-
@Test
45-
@DisplayName("Minutes parsing")
46-
public void testMinutes() {
47-
ParsedDuration d = ParsedDuration.parse("30m");
48-
assertEquals(30 * 60_000L, d.getMillis());
47+
assertEquals(30 * 60_000L, ParsedDuration.parse("PT30M", TimeUnit.MINUTES).getMillis());
48+
assertEquals(12 * 3_600_000L, ParsedDuration.parse("PT12H", TimeUnit.MINUTES).getMillis());
49+
assertEquals(86_400_000L, ParsedDuration.parse("P1D", TimeUnit.MINUTES).getMillis());
4950
}
5051

5152
@Test
52-
@DisplayName("Hours parsing")
53-
public void testHours() {
54-
ParsedDuration d = ParsedDuration.parse("12h");
55-
assertEquals(12 * 3_600_000L, d.getMillis());
53+
@DisplayName("ISO-8601 months are rejected (P1M) for both APIs")
54+
public void testIsoMonthsRejected() {
55+
assertTrue(ParsedDuration.parse("P1M", Unit.MINUTES).isEmpty());
56+
assertTrue(ParsedDuration.parse("P1M", TimeUnit.MINUTES).isEmpty());
5657
}
5758

5859
@Test
59-
@DisplayName("Days parsing")
60-
public void testDays() {
61-
ParsedDuration d = ParsedDuration.parse("1d");
62-
assertEquals(86_400_000L, d.getMillis());
60+
@DisplayName("Fixed month tokens behave the same (3mo, 1mo2d) for both APIs")
61+
public void testFixedMonthTokens() {
62+
long expected3mo = 3L * 30L * 86_400_000L;
63+
assertEquals(expected3mo, ParsedDuration.parse("3mo", Unit.MINUTES).getMillis());
64+
assertEquals(expected3mo, ParsedDuration.parse("3mo", TimeUnit.MINUTES).getMillis());
65+
66+
long expected = 1L * 30L * 86_400_000L + 2L * 86_400_000L;
67+
assertEquals(expected, ParsedDuration.parse("1mo2d", Unit.MINUTES).getMillis());
68+
assertEquals(expected, ParsedDuration.parse("1mo2d", TimeUnit.MINUTES).getMillis());
6369
}
6470

6571
@Test
66-
@DisplayName("Weeks parsing")
67-
public void testWeeks() {
68-
ParsedDuration d = ParsedDuration.parse("2w");
69-
assertEquals(2 * 604_800_000L, d.getMillis());
72+
@DisplayName("Combined tokens allow spaces (2d 12h) for both APIs")
73+
public void testCombinedWithSpaces() {
74+
long expected = 2 * 86_400_000L + 12 * 3_600_000L;
75+
assertEquals(expected, ParsedDuration.parse("2d 12h", Unit.MINUTES).getMillis());
76+
assertEquals(expected, ParsedDuration.parse("2d 12h", TimeUnit.MINUTES).getMillis());
7077
}
7178

7279
@Test
73-
@DisplayName("Months parsing is fixed 30-day millis (3mo)")
74-
public void testMonthsFixedMillis() {
75-
ParsedDuration d = ParsedDuration.parse("3mo");
76-
assertEquals(3L * 30L * 86_400_000L, d.getMillis());
80+
@DisplayName("Garbage input returns empty for both APIs")
81+
public void testGarbageInput() {
82+
assertTrue(ParsedDuration.parse("abc", Unit.MINUTES).isEmpty());
83+
assertTrue(ParsedDuration.parse("!@#$", Unit.MINUTES).isEmpty());
84+
85+
assertTrue(ParsedDuration.parse("abc", TimeUnit.MINUTES).isEmpty());
86+
assertTrue(ParsedDuration.parse("!@#$", TimeUnit.MINUTES).isEmpty());
7787
}
7888

7989
@Test
80-
@DisplayName("Combined tokens can include months as fixed millis: 1mo2d")
81-
public void testCombinedMonthsPlusDaysFixed() {
82-
ParsedDuration d = ParsedDuration.parse("1mo2d");
83-
assertEquals(1L * 30L * 86_400_000L + 2L * 86_400_000L, d.getMillis());
90+
@DisplayName("Zero or negative values return empty for both APIs")
91+
public void testZeroAndNegative() {
92+
assertTrue(ParsedDuration.parse("0m", Unit.MINUTES).isEmpty());
93+
assertTrue(ParsedDuration.parse("-5m", Unit.MINUTES).isEmpty());
94+
95+
assertTrue(ParsedDuration.parse("0m", TimeUnit.MINUTES).isEmpty());
96+
assertTrue(ParsedDuration.parse("-5m", TimeUnit.MINUTES).isEmpty());
8497
}
8598

99+
// ---------------------------------------------------------------------------------
100+
// Unit-specific behavior tests
101+
// ---------------------------------------------------------------------------------
102+
86103
@Test
87-
@DisplayName("Combined tokens allow spaces: 2d 12h")
88-
public void testCombinedWithSpaces() {
89-
ParsedDuration d = ParsedDuration.parse("2d 12h");
90-
assertEquals(2 * 86_400_000L + 12 * 3_600_000L, d.getMillis());
104+
@DisplayName("Unit API: plain number uses default MINUTES")
105+
public void testUnitPlainNumberDefaultMinutes() {
106+
ParsedDuration d = ParsedDuration.parse("30"); // default Unit.MINUTES
107+
assertEquals(30 * 60_000L, d.getMillis());
91108
}
92109

93110
@Test
94-
@DisplayName("Combined tokens: 1w2d3h4m5s6ms")
95-
public void testCombinedManySegments() {
96-
ParsedDuration d = ParsedDuration.parse("1w2d3h4m5s6ms");
97-
long expected = 1 * 604_800_000L + 2 * 86_400_000L + 3 * 3_600_000L + 4 * 60_000L + 5 * 1000L + 6;
98-
assertEquals(expected, d.getMillis());
111+
@DisplayName("Unit API: plain number respects custom default Unit")
112+
public void testUnitPlainNumberCustomDefault() {
113+
assertEquals(15_000L, ParsedDuration.parse("15", Unit.SECONDS).getMillis());
114+
assertEquals(2 * 3_600_000L, ParsedDuration.parse("2", Unit.HOURS).getMillis());
99115
}
100116

101117
@Test
102-
@DisplayName("Plain number respects custom default unit")
103-
public void testPlainNumberCustomUnit() {
104-
ParsedDuration seconds = ParsedDuration.withDefaultUnit("15", Unit.SECONDS);
105-
assertEquals(15_000L, seconds.getMillis());
106-
107-
ParsedDuration hours = ParsedDuration.withDefaultUnit("2", Unit.HOURS);
108-
assertEquals(2 * 3_600_000L, hours.getMillis());
118+
@DisplayName("Unit API: unknown suffix falls back to provided default Unit (single token)")
119+
public void testUnitUnknownSuffixFallbackSingle() {
120+
// default = minutes
121+
assertEquals(10 * 60_000L, ParsedDuration.parse("10x", Unit.MINUTES).getMillis());
122+
// default = seconds
123+
assertEquals(10 * 1000L, ParsedDuration.parse("10x", Unit.SECONDS).getMillis());
109124
}
110125

111126
@Test
112-
@DisplayName("ISO-8601 duration parsing (PT)")
113-
public void testIso8601Duration() {
114-
ParsedDuration d1 = ParsedDuration.parse("PT30M");
115-
assertEquals(30 * 60_000L, d1.getMillis());
127+
@DisplayName("Unit API: unknown suffix falls back to provided default Unit (combined tokens)")
128+
public void testUnitUnknownSuffixFallbackCombined() {
129+
// 30x should fallback to minutes (default unit = MINUTES)
130+
long expectedMinutesFallback = 1 * 3_600_000L + 30 * 60_000L;
131+
assertEquals(expectedMinutesFallback, ParsedDuration.parse("1h30x", Unit.MINUTES).getMillis());
116132

117-
ParsedDuration d2 = ParsedDuration.parse("PT12H");
118-
assertEquals(12 * 3_600_000L, d2.getMillis());
119-
120-
ParsedDuration d3 = ParsedDuration.parse("P1D");
121-
assertEquals(86_400_000L, d3.getMillis());
133+
// 30x should fallback to seconds (default unit = SECONDS)
134+
long expectedSecondsFallback = 1 * 3_600_000L + 30 * 1000L;
135+
assertEquals(expectedSecondsFallback, ParsedDuration.parse("1h30x", Unit.SECONDS).getMillis());
122136
}
123137

138+
// ---------------------------------------------------------------------------------
139+
// TimeUnit-specific behavior tests
140+
// ---------------------------------------------------------------------------------
141+
124142
@Test
125-
@DisplayName("ISO-8601 months are rejected (P1M)")
126-
public void testIsoMonthsRejected() {
127-
ParsedDuration d = ParsedDuration.parse("P1M");
128-
assertTrue(d.isEmpty(), "ISO months should not be parsed as minutes or months");
143+
@DisplayName("TimeUnit API: plain number respects custom default TimeUnit")
144+
public void testTimeUnitPlainNumberCustomDefault() {
145+
assertEquals(15_000L, ParsedDuration.parse("15", TimeUnit.SECONDS).getMillis());
146+
assertEquals(2 * 3_600_000L, ParsedDuration.parse("2", TimeUnit.HOURS).getMillis());
129147
}
130148

131149
@Test
132-
@DisplayName("Unknown suffix falls back to default unit")
133-
public void testUnknownSuffixFallback() {
134-
ParsedDuration d = ParsedDuration.parse("10x");
135-
assertEquals(10 * 60_000L, d.getMillis()); // default = minutes
150+
@DisplayName("TimeUnit API: unknown suffix falls back to provided default TimeUnit (single token)")
151+
public void testTimeUnitUnknownSuffixFallbackSingle() {
152+
// default = minutes
153+
assertEquals(10 * 60_000L, ParsedDuration.parse("10x", TimeUnit.MINUTES).getMillis());
154+
// default = seconds
155+
assertEquals(10 * 1000L, ParsedDuration.parse("10x", TimeUnit.SECONDS).getMillis());
136156
}
137157

138158
@Test
139-
@DisplayName("Unknown suffix inside combined tokens falls back to default unit")
140-
public void testUnknownSuffixFallbackCombined() {
141-
ParsedDuration d = ParsedDuration.parse("1h30x"); // 30x => 30 minutes by default
142-
assertEquals(1 * 3_600_000L + 30 * 60_000L, d.getMillis());
159+
@DisplayName("TimeUnit API: unknown suffix falls back to provided default TimeUnit (combined tokens)")
160+
public void testTimeUnitUnknownSuffixFallbackCombined() {
161+
// 30x should fallback to minutes (default timeunit = MINUTES)
162+
long expectedMinutesFallback = 1 * 3_600_000L + 30 * 60_000L;
163+
assertEquals(expectedMinutesFallback, ParsedDuration.parse("1h30x", TimeUnit.MINUTES).getMillis());
164+
165+
// 30x should fallback to seconds (default timeunit = SECONDS)
166+
long expectedSecondsFallback = 1 * 3_600_000L + 30 * 1000L;
167+
assertEquals(expectedSecondsFallback, ParsedDuration.parse("1h30x", TimeUnit.SECONDS).getMillis());
143168
}
144169

145170
@Test
146-
@DisplayName("Garbage input returns empty")
147-
public void testGarbageInput() {
148-
assertTrue(ParsedDuration.parse("abc").isEmpty());
149-
assertTrue(ParsedDuration.parse("!@#$").isEmpty());
171+
@DisplayName("TimeUnit API: nanos/micros default units truncate to millis (number-only)")
172+
public void testTimeUnitSubMillisDefaultsTruncate() {
173+
// 999,999ns -> 0ms => empty (because ParsedDuration clamps <=0 to empty)
174+
assertTrue(ParsedDuration.parse("999999", TimeUnit.NANOSECONDS).isEmpty());
175+
176+
// 1,500,000ns -> 1ms
177+
assertEquals(1L, ParsedDuration.parse("1500000", TimeUnit.NANOSECONDS).getMillis());
178+
179+
// 999us -> 0ms => empty
180+
assertTrue(ParsedDuration.parse("999", TimeUnit.MICROSECONDS).isEmpty());
181+
182+
// 1,500us -> 1ms
183+
assertEquals(1L, ParsedDuration.parse("1500", TimeUnit.MICROSECONDS).getMillis());
150184
}
151185

186+
// ---------------------------------------------------------------------------------
187+
// A few sanity checks for parsing suffixes are consistent across both APIs
188+
// ---------------------------------------------------------------------------------
189+
152190
@Test
153-
@DisplayName("Zero or negative values return empty")
154-
public void testZeroAndNegative() {
155-
assertTrue(ParsedDuration.parse("0m").isEmpty());
156-
assertTrue(ParsedDuration.parse("-5m").isEmpty());
157-
assertTrue(ParsedDuration.withDefaultUnit("0", Unit.SECONDS).isEmpty());
191+
@DisplayName("Suffix parsing yields same millis regardless of default (Unit + TimeUnit)")
192+
public void testSuffixParsingIndependentOfDefault() {
193+
assertEquals(5000L, ParsedDuration.parse("5000ms", Unit.HOURS).getMillis());
194+
assertEquals(5000L, ParsedDuration.parse("5000ms", TimeUnit.HOURS).getMillis());
195+
196+
assertEquals(60_000L, ParsedDuration.parse("60s", Unit.DAYS).getMillis());
197+
assertEquals(60_000L, ParsedDuration.parse("60s", TimeUnit.DAYS).getMillis());
198+
199+
assertEquals(12 * 3_600_000L, ParsedDuration.parse("12h", Unit.SECONDS).getMillis());
200+
assertEquals(12 * 3_600_000L, ParsedDuration.parse("12h", TimeUnit.SECONDS).getMillis());
158201
}
159202

160203
@Test
@@ -163,7 +206,10 @@ public void testToString() {
163206
ParsedDuration empty = ParsedDuration.empty();
164207
assertNotNull(empty.toString());
165208

166-
ParsedDuration d = ParsedDuration.parse("5m");
209+
ParsedDuration d = ParsedDuration.parse("5m", Unit.MINUTES);
167210
assertTrue(d.toString().contains("millis"));
211+
212+
ParsedDuration dt = ParsedDuration.parse("5m", TimeUnit.MINUTES);
213+
assertTrue(dt.toString().contains("millis"));
168214
}
169215
}

0 commit comments

Comments
 (0)