From bc7442f74203918f1c6d7534d388492b7ac33601 Mon Sep 17 00:00:00 2001 From: Donald King Date: Wed, 6 Dec 2023 11:47:48 -0800 Subject: [PATCH] Truncate to millisecond precision. --- .../internal/common/ProtobufTimeUtils.java | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/temporal-sdk/src/main/java/io/temporal/internal/common/ProtobufTimeUtils.java b/temporal-sdk/src/main/java/io/temporal/internal/common/ProtobufTimeUtils.java index 31c99015a..a12a225c8 100644 --- a/temporal-sdk/src/main/java/io/temporal/internal/common/ProtobufTimeUtils.java +++ b/temporal-sdk/src/main/java/io/temporal/internal/common/ProtobufTimeUtils.java @@ -32,15 +32,37 @@ public class ProtobufTimeUtils { private static final int NANOS_PER_SECOND = 1000000000; + private static final int NANOS_PER_MILLI = 1000000; + /** + * Converts a Protobuf Duration to a Java Duration with millisecond precision. Null inputs are + * treated as zero. + */ @Nonnull - public static Duration toJavaDuration(com.google.protobuf.Duration d) { - // TODO we should refactor an implicit conversion of empty values into ZERO and rename the - // current method into toJavaDurationSafe, toJavaDurationOrDefault or something like that + public static Duration toJavaDuration(@Nullable com.google.protobuf.Duration d) { if (Objects.isNull(d)) { return Duration.ZERO; } + return truncateToMillis(toJavaDurationExact(d)); + } + + /** + * Converts a Java Duration to a Protobuf Duration with millisecond precision. Null inputs are + * treated as zero. + */ + @Nonnull + public static com.google.protobuf.Duration toProtoDuration(@Nullable Duration d) { + if (Objects.isNull(d)) { + return Durations.ZERO; + } + return toProtoDurationExact(truncateToMillis(d)); + } + /** + * Converts a Protobuf Duration to a Java Duration with nanosecond precision. + */ + @Nonnull + public static Duration toJavaDurationExact(@Nonnull com.google.protobuf.Duration d) { // See the comment in toProtoDuration for details about how Java Duration and Protobuf Duration // have almost-but-not-quite identical representations. // @@ -50,14 +72,11 @@ public static Duration toJavaDuration(com.google.protobuf.Duration d) { return Duration.ofSeconds(d.getSeconds(), d.getNanos()); } + /** + * Converts a Java Duration to a Protobuf Duration with nanosecond precision. + */ @Nonnull - public static com.google.protobuf.Duration toProtoDuration(Duration d) { - // TODO we should refactor an implicit conversion of empty values into ZERO and rename the - // current method into toJavaDurationSafe, toJavaDurationOrDefault or something like that - if (Objects.isNull(d)) { - return Durations.ZERO; - } - + public static com.google.protobuf.Duration toProtoDurationExact(@Nonnull Duration d) { // Java Duration and Protobuf Duration are *almost* identical in representation: both use a // 96-bit value divided between a 64-bit (long) seconds count and a 32-bit (int) nanoseconds // count. However, they represent negative durations slightly differently: @@ -116,4 +135,14 @@ public static com.uber.m3.util.Duration toM3DurationSinceNow(Timestamp t) { return Timestamp.newBuilder().setSeconds(t.getEpochSecond()).setNanos(t.getNano()).build(); } + + @Nonnull + private static Duration truncateToMillis(@Nonnull Duration exact) { + // In JDK 9+, this would just be a call to Duration.truncatedTo(ChronoUnit.MILLIS). + // Alas, we must remain compatible with JDK 8. + final long s = exact.getSeconds(); + final int ns = exact.getNano(); + final int ms = ns / NANOS_PER_MILLI; + return Duration.ofSeconds(s).plusMillis(ms); + } }