mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2026-10-12 09:24:52 -05:00
fix(expr-api): render datetime seconds even on a whole minute
The ISO forms of LocalTime and LocalDateTime omit the seconds field when the second is 00, so currentTime(), currentDateTime(), currentTimeInZone() and millisToDateTime() each returned a shorter string once a minute — HH:mm instead of HH:mm:ss. That failed testCurrentTime in CI roughly one run in sixty, and silently handed workloads a differently-shaped timestamp the rest of the time. Each now formats with an explicit pattern rather than toString(), which also makes the truncatedTo(SECONDS) calls redundant. The new test pins the boundary through millisToDateTime, whose instant comes from the caller: the wall-clock functions can only be asserted for shape, which catches this one second in sixty. It builds a timestamp on a whole minute, requires the seconds to be there, and repeats at second 30 so a fix in the other direction is caught too.
This commit is contained in:
1 parent
59d604aed6
commit
5a82d60305
2 files changed
+37
-5
No files matched your search
@@ -76,7 +76,10 @@ def currentDate() {
|
||||
)
|
||||
@ExprExample(expectNotNull = true)
|
||||
def currentTime() {
|
||||
LocalTime.now().truncatedTo(ChronoUnit.SECONDS).toString()
|
||||
// Explicit pattern, not toString(): LocalTime's ISO form omits the
|
||||
// seconds field entirely when the second is 00, so toString() emits
|
||||
// HH:mm once a minute and HH:mm:ss the rest of the time.
|
||||
LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||
}
|
||||
|
||||
@ExprFunctionSpec(
|
||||
@@ -85,7 +88,8 @@ def currentTime() {
|
||||
)
|
||||
@ExprExample(expectNotNull = true)
|
||||
def currentDateTime() {
|
||||
LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS).toString()
|
||||
// See currentTime(): the ISO form drops seconds at second 00.
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))
|
||||
}
|
||||
|
||||
@ExprFunctionSpec(
|
||||
@@ -266,11 +270,12 @@ def millisToDate(millis) {
|
||||
)
|
||||
@ExprExample(expectNotNull = true)
|
||||
def millisToDateTime(millis) {
|
||||
// See currentTime(): the ISO form drops seconds at second 00, so a
|
||||
// timestamp landing on a whole minute rendered as yyyy-MM-ddTHH:mm.
|
||||
Instant.ofEpochMilli(millis as long)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime()
|
||||
.truncatedTo(ChronoUnit.SECONDS)
|
||||
.toString()
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))
|
||||
}
|
||||
|
||||
@ExprFunctionSpec(
|
||||
@@ -300,5 +305,6 @@ def currentDateInZone(timezone) {
|
||||
)
|
||||
@ExprExample(expectNotNull = true)
|
||||
def currentTimeInZone(timezone) {
|
||||
LocalTime.now(ZoneId.of(timezone)).truncatedTo(ChronoUnit.SECONDS).toString()
|
||||
// See currentTime(): the ISO form drops seconds at second 00.
|
||||
LocalTime.now(ZoneId.of(timezone)).format(DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||
}
|
||||
@@ -19,6 +19,10 @@ package io.nosqlbench.nb.api.expr;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -122,6 +126,28 @@ class BundledLibrariesTest {
|
||||
assertTrue(result.matches("\\d{2}:\\d{2}:\\d{2}"));
|
||||
}
|
||||
|
||||
/// The seconds field is optional in the ISO forms of `LocalTime` and
|
||||
/// `LocalDateTime`: `toString()` omits it entirely when the second
|
||||
/// is `00`, so a shape assertion against the wall clock only catches
|
||||
/// the defect one second in sixty. `millisToDateTime` takes its
|
||||
/// instant from the caller and shares the same rendering, so the
|
||||
/// boundary can be pinned exactly rather than waited for.
|
||||
@Test
|
||||
void timesOnAWholeMinuteStillRenderTheirSeconds() {
|
||||
long onTheMinute = ZonedDateTime.now(ZoneId.systemDefault())
|
||||
.truncatedTo(ChronoUnit.MINUTES).toInstant().toEpochMilli();
|
||||
String atSecondZero = processor.process("{{= millisToDateTime(" + onTheMinute + ") }}", null, Map.of());
|
||||
assertTrue(atSecondZero.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}"),
|
||||
"a timestamp on a whole minute must still render seconds, got: " + atSecondZero);
|
||||
assertTrue(atSecondZero.endsWith(":00"), "and those seconds are 00, got: " + atSecondZero);
|
||||
|
||||
String atSecondThirty = processor.process(
|
||||
"{{= millisToDateTime(" + (onTheMinute + 30_000) + ") }}", null, Map.of());
|
||||
assertTrue(atSecondThirty.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}"),
|
||||
"and a non-zero second renders the same width, got: " + atSecondThirty);
|
||||
assertTrue(atSecondThirty.endsWith(":30"), atSecondThirty);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCurrentDateTime() {
|
||||
String result = processor.process("{{= currentDateTime() }}", null, Map.of());
|
||||
|
||||
Reference in new issue
Block a user