Complete Joda-Time to java.time migration (#3039)

This completes the exhaustive refactoring of foundational temporal types from Joda-Time to the native java.time API across the entire codebase.

- Replaced org.joda.time.DateTime, Instant, LocalDate, and Duration with java.time equivalents.
- Audited and updated Clock implementations (FakeClock, SystemClock). Added nowMillis(), nowDate(), and nowDateTime() to eliminate repetitive conversions and maintain parallel naming.
- Replaced ZonedDateTime with OffsetDateTime globally per go/avoid-zdt. OffsetDateTime is a better fit as we use a hardcoded ZoneOffset.UTC throughout the system, making geographical time zone rules (like daylight saving time) irrelevant and preventing serialization ambiguities. Added a presubmit check.
- Completely removed all transitional bridge methods from DateTimeUtils and deleted obsolete converters (e.g., DateTimeConverter).
- Updated testing infrastructure, Apache Beam pipelines, custom JCommander parameters, and networking modules to solely rely on java.time primitives.
- Retained the lone necessary org.joda.time.Instant usage in SafeBrowsingTransforms required by the Apache Beam API.
- Cleared Gradle lockfiles and removed the joda-time dependency entirely from the build configuration.
This commit is contained in:
Ben McIlwain
2026-05-13 16:07:19 +00:00
committed by GitHub
parent b33c2f4874
commit 56fe588b56
218 changed files with 589 additions and 1390 deletions
-1
View File
@@ -33,7 +33,6 @@ dependencies {
implementation deps['io.netty:netty-handler']
implementation deps['io.netty:netty-transport']
implementation deps['jakarta.inject:jakarta.inject-api']
implementation deps['joda-time:joda-time']
implementation deps['org.bouncycastle:bcpkix-jdk18on']
implementation deps['org.bouncycastle:bcprov-jdk18on']
implementation deps['xerces:xmlParserAPIs']
-1
View File
@@ -113,7 +113,6 @@ jakarta.mail:jakarta.mail-api:2.2.0-M1=deploy_jar,runtimeClasspath,testRuntimeCl
jakarta.xml.bind:jakarta.xml.bind-api:4.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath
javax.annotation:javax.annotation-api:1.3.2=deploy_jar,runtimeClasspath,testRuntimeClasspath
javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
joda-time:joda-time:2.14.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.17.7=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=testCompileClasspath,testRuntimeClasspath
@@ -31,8 +31,8 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.OpenSsl;
import io.netty.handler.ssl.SslProvider;
import jakarta.inject.Singleton;
import java.time.Duration;
import java.util.Set;
import org.joda.time.Duration;
/**
* Dagger main module, which {@link Provides} all objects that are shared between sequences and
@@ -43,7 +43,7 @@ import org.joda.time.Duration;
public class ProberModule {
/** Default {@link Duration} chosen to be time between each {@link ProbingAction} call. */
private static final Duration DEFAULT_PROBER_INTERVAL = Duration.standardSeconds(4);
private static final Duration DEFAULT_PROBER_INTERVAL = Duration.ofSeconds(4);
/** {@link Provides} the {@link SslProvider} used by instances of {@link SslClientInitializer} */
@Provides
@@ -111,7 +111,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
* get().generateAction}.
*/
private void runStep(Token token) {
long start = clock.now().toEpochMilli();
long start = clock.nowMillis();
ProbingAction currentAction;
ChannelFuture future;
@@ -133,7 +133,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
get().messageTemplate().name(),
get().messageTemplate().responseName(),
MetricsCollector.ResponseType.ERROR,
clock.now().toEpochMilli() - start);
clock.nowMillis() - start);
return;
} catch (Exception e) {
@@ -146,7 +146,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
get().messageTemplate().name(),
get().messageTemplate().responseName(),
MetricsCollector.ResponseType.ERROR,
clock.now().toEpochMilli() - start);
clock.nowMillis() - start);
// Restart the sequence at the very first step.
restartSequence();
@@ -165,7 +165,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
get().messageTemplate().name(),
get().messageTemplate().responseName(),
MetricsCollector.ResponseType.SUCCESS,
clock.now().toEpochMilli() - start);
clock.nowMillis() - start);
} else {
// On a failed result, we log the failure and note either a failure or error.
logger.atSevere().withCause(f.cause()).log("Did not result in future success");
@@ -177,14 +177,14 @@ public class ProbingSequence extends CircularList<ProbingStep> {
get().messageTemplate().name(),
get().messageTemplate().responseName(),
MetricsCollector.ResponseType.FAILURE,
clock.now().toEpochMilli() - start);
clock.nowMillis() - start);
} else {
metrics.recordResult(
get().protocol().name(),
get().messageTemplate().name(),
get().messageTemplate().responseName(),
MetricsCollector.ResponseType.ERROR,
clock.now().toEpochMilli() - start);
clock.nowMillis() - start);
}
// If not unrecoverable, we restart the sequence.
@@ -22,7 +22,7 @@ import google.registry.monitoring.blackbox.exception.UndeterminedStateException;
import google.registry.monitoring.blackbox.message.OutboundMessageType;
import google.registry.monitoring.blackbox.token.Token;
import io.netty.bootstrap.Bootstrap;
import org.joda.time.Duration;
import java.time.Duration;
/**
* {@link AutoValue} class that represents generator of actions performed at each step in {@link
@@ -75,9 +75,9 @@ public record ProbingStep(
public String toString() {
return String.format(
"""
ProbingStep with Protocol: %s
OutboundMessage: %s
""",
ProbingStep with Protocol: %s
OutboundMessage: %s
""",
protocol(), messageTemplate().getClass().getName());
}
@@ -40,9 +40,9 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.joda.time.Duration;
/**
* AutoValue class that represents action generated by {@link ProbingStep}
@@ -186,7 +186,7 @@ public abstract class ProbingAction implements Callable<ChannelFuture> {
}
});
},
delay().getStandardSeconds(),
delay().toSeconds(),
TimeUnit.SECONDS);
} else {
// if we receive a failure, log the failure, and close the channel
@@ -208,7 +208,7 @@ public abstract class ProbingAction implements Callable<ChannelFuture> {
protocol: %s
host: %s
""",
delay().getStandardSeconds(), outboundMessage(), protocol(), host());
delay().toSeconds(), outboundMessage(), protocol(), host());
}
/** {@link AutoValue.Builder} that does work of creating connection when not already present. */
@@ -30,9 +30,9 @@ import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.joda.time.Duration;
/**
* Dagger module that provides bindings needed to inject server certificate chain and private key.
@@ -63,7 +63,7 @@ public class CertificateModule {
@Provides
@LocalSecrets
static Duration provideCacheDuration() {
return Duration.standardSeconds(2);
return Duration.ofSeconds(2);
}
@Singleton
@@ -111,8 +111,7 @@ public class CertificateModule {
@LocalSecrets
static Supplier<PrivateKey> providePrivatekeySupplier(
@LocalSecrets Provider<PrivateKey> privateKeyProvider, @LocalSecrets Duration duration) {
return memoizeWithExpiration(
privateKeyProvider::get, duration.getStandardSeconds(), TimeUnit.SECONDS);
return memoizeWithExpiration(privateKeyProvider::get, duration.toSeconds(), TimeUnit.SECONDS);
}
@Singleton
@@ -121,7 +120,6 @@ public class CertificateModule {
static Supplier<ImmutableList<X509Certificate>> provideCertificatesSupplier(
@LocalSecrets Provider<ImmutableList<X509Certificate>> certificatesProvider,
@LocalSecrets Duration duration) {
return memoizeWithExpiration(
certificatesProvider::get, duration.getStandardSeconds(), TimeUnit.SECONDS);
return memoizeWithExpiration(certificatesProvider::get, duration.toSeconds(), TimeUnit.SECONDS);
}
}
@@ -52,8 +52,8 @@ import jakarta.inject.Qualifier;
import jakarta.inject.Singleton;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.function.Supplier;
import org.joda.time.Duration;
/**
* A module that provides the components necessary for and the overall {@link ProbingSequence} to
@@ -81,8 +81,7 @@ public abstract class EppToken extends Token {
*/
private String getNewTRID() {
return String.format(
"prober-%s-%d-%d",
"localhost", clock.now().toEpochMilli(), clientIdSuffix.incrementAndGet());
"prober-%s-%d-%d", "localhost", clock.nowMillis(), clientIdSuffix.incrementAndGet());
}
/** Return a fully qualified domain label to use, derived from the client transaction ID. */
@@ -36,7 +36,7 @@ import google.registry.util.Clock;
import io.netty.channel.Channel;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import org.joda.time.Duration;
import java.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -60,7 +60,7 @@ class ProbingSequenceTest {
private static final String PROTOCOL_NAME = "PROTOCOL";
private static final String MESSAGE_NAME = "MESSAGE";
private static final String RESPONSE_NAME = "RESPONSE";
private static final Duration LATENCY = Duration.millis(2L);
private static final Duration LATENCY = Duration.ofMillis(2L);
/** Default mock {@link ProbingAction} returned when generating an action with a mockStep. */
private ProbingAction mockAction = Mockito.mock(ProbingAction.class);
@@ -223,7 +223,7 @@ class ProbingSequenceTest {
// name and message name).
verify(metrics)
.recordResult(
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.SUCCESS, LATENCY.getMillis());
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.SUCCESS, LATENCY.toMillis());
}
@Test
@@ -305,7 +305,7 @@ class ProbingSequenceTest {
// name and message name) two times: once for mockStep and once for secondStep.
verify(metrics, times(2))
.recordResult(
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.SUCCESS, LATENCY.getMillis());
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.SUCCESS, LATENCY.toMillis());
// Verify that on second pass, since we purposely throw UnrecoverableStateException, we
// record the ERROR. Also, we haven't had any time pass in the fake clock, so recorded
@@ -390,13 +390,13 @@ class ProbingSequenceTest {
// name and message name).
verify(metrics)
.recordResult(
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.FAILURE, LATENCY.getMillis());
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.FAILURE, LATENCY.toMillis());
// Verify that on second pass, since we purposely throw UnrecoverableStateException, we
// record the ERROR. We also should make sure LATENCY seconds have passed.
verify(metrics)
.recordResult(
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.ERROR, LATENCY.getMillis());
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.ERROR, LATENCY.toMillis());
}
@Test
@@ -421,6 +421,6 @@ class ProbingSequenceTest {
// for terminating the sequence.
verify(metrics, times(2))
.recordResult(
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.ERROR, LATENCY.getMillis());
PROTOCOL_NAME, MESSAGE_NAME, RESPONSE_NAME, ResponseType.ERROR, LATENCY.toMillis());
}
}
@@ -39,7 +39,7 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import org.joda.time.Duration;
import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito;
@@ -33,7 +33,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import org.joda.time.Duration;
import java.time.Duration;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;