mirror of
https://github.com/google/nomulus
synced 2026-05-18 13:51:45 +00:00
Migrate core temporal models and related entities to java.time.Instant (#3001)
This comprehensive refactor continues the migration from Joda-Time to java.time (Instant), focusing on core timestamp models, transition properties, and their integration across the codebase. Key changes: - Migrated CreateAutoTimestamp and UpdateAutoTimestamp to use Instant internally, providing Joda-Time bridge methods for backward compatibility. - Updated TimedTransitionProperty to handle Instant-based transition maps and updated corresponding Hibernate UserTypes (TimedTransitionBaseUserType). - Migrated GracePeriod, BillingBase, BillingEvent, PollMessage, and PendingActionNotificationResponse fields (e.g., expirationTime, eventTime) to Instant. - Migrated additional core entities (DomainBase, Registrar, HostBase, LaunchNotice, BsaLabel, DomainTransactionRecord) to use Instant for registrationExpirationTime, lastTransferTime, creationTime, etc. - Updated Tld and FeatureFlag models to use Instant for claimsPeriodEnd, bsaEnrollStartTime, and status transitions. - Enhanced CLI tools and parameters (TransitionListParameter, InstantParameter, RequestParameters) to support Instant-based input and output. - Updated EntityYamlUtils with custom Instant serializers/deserializers to maintain format consistency (e.g., .SSSZ precision) required for YAML-based tests. - Implemented UtcInstantAdapter to ensure JAXB XML serialization maintains millisecond accuracy, matching legacy Joda-Time behavior. - Resolved Hibernate 6 type mismatches in JPQL and Native queries by ensuring consistent use of Instant for comparisons. - Updated GEMINI.md with project-specific engineering standards, including the 'one commit per PR' mandate, full-build validation requirement, and commit message style rules. - Cleaned up unnecessary @JsonIgnore and @JsonProperty annotations that were previously added to methods with parameters or redundant fields. - Refactored DateTimeUtils to use strongly-typed overloads and standardized naming (earliestOf, latestOf) while avoiding type erasure clashes. - Cleaned up fully qualified calls to toDateTime and toInstant by adding static imports across core model and flow files. - Refactored test suites to use clock.now() (Instant) instead of nowUtc() (DateTime) and removed custom Truth subjects in favor of standard assertions.
This commit is contained in:
@@ -22,6 +22,11 @@ import com.google.common.collect.Ordering;
|
||||
import java.sql.Date;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.SignStyle;
|
||||
import java.time.temporal.ChronoField;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
@@ -55,6 +60,38 @@ public abstract class DateTimeUtils {
|
||||
*/
|
||||
public static final Instant END_INSTANT = Instant.ofEpochMilli(Long.MAX_VALUE / 1000);
|
||||
|
||||
/**
|
||||
* Standard ISO 8601 formatter with millisecond precision in UTC.
|
||||
*
|
||||
* <p>Example: {@code 2024-03-27T10:15:30.105Z}
|
||||
*
|
||||
* <p>Handles large/negative years by using a sign prefix if necessary, compatible with {@link
|
||||
* Instant#parse}.
|
||||
*/
|
||||
public static final DateTimeFormatter ISO_8601_FORMATTER =
|
||||
new DateTimeFormatterBuilder()
|
||||
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.NORMAL)
|
||||
.appendPattern("-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.toFormatter()
|
||||
.withZone(ZoneOffset.UTC);
|
||||
|
||||
/**
|
||||
* Parses an ISO-8601 string to an {@link Instant}.
|
||||
*
|
||||
* <p>This method is lenient and supports both strings with and without millisecond precision
|
||||
* (e.g. {@code 2024-03-27T10:15:30Z} and {@code 2024-03-27T10:15:30.105Z}). It also supports
|
||||
* large years (e.g. {@code 294247-01-10T04:00:54.775Z}).
|
||||
*/
|
||||
public static Instant parseInstant(String timestamp) {
|
||||
try {
|
||||
// Try the standard millisecond precision format first.
|
||||
return Instant.from(ISO_8601_FORMATTER.parse(timestamp));
|
||||
} catch (DateTimeParseException e) {
|
||||
// Fall back to the standard ISO instant parser which handles varied precision.
|
||||
return Instant.parse(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the earliest of a number of given {@link DateTime} instances. */
|
||||
public static DateTime earliestOf(DateTime first, DateTime... rest) {
|
||||
return earliestDateTimeOf(Lists.asList(first, rest));
|
||||
@@ -79,15 +116,26 @@ public abstract class DateTimeUtils {
|
||||
|
||||
/** Returns the latest of a number of given {@link DateTime} instances. */
|
||||
public static DateTime latestOf(DateTime first, DateTime... rest) {
|
||||
return latestDateTimeOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the latest of a number of given {@link Instant} instances. */
|
||||
public static Instant latestOf(Instant first, Instant... rest) {
|
||||
return latestOf(Lists.asList(first, rest));
|
||||
}
|
||||
|
||||
/** Returns the latest element in a {@link DateTime} iterable. */
|
||||
public static DateTime latestOf(Iterable<DateTime> dates) {
|
||||
public static DateTime latestDateTimeOf(Iterable<DateTime> dates) {
|
||||
checkArgument(!Iterables.isEmpty(dates));
|
||||
return Ordering.<DateTime>natural().max(dates);
|
||||
}
|
||||
|
||||
/** Returns the latest element in a {@link Instant} iterable. */
|
||||
public static Instant latestOf(Iterable<Instant> instants) {
|
||||
checkArgument(!Iterables.isEmpty(instants));
|
||||
return Ordering.<Instant>natural().max(instants);
|
||||
}
|
||||
|
||||
/** Returns whether the first {@link DateTime} is equal to or earlier than the second. */
|
||||
public static boolean isBeforeOrAt(DateTime timeToCheck, DateTime timeToCompareTo) {
|
||||
return !timeToCheck.isAfter(timeToCompareTo);
|
||||
@@ -112,8 +160,7 @@ public abstract class DateTimeUtils {
|
||||
* Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of
|
||||
* {@link DateTime#plusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
@Deprecated
|
||||
public static DateTime leapSafeAddYears(DateTime now, int years) {
|
||||
public static DateTime plusYears(DateTime now, int years) {
|
||||
checkArgument(years >= 0);
|
||||
return years == 0 ? now : now.plusYears(1).plusYears(years - 1);
|
||||
}
|
||||
@@ -122,7 +169,7 @@ public abstract class DateTimeUtils {
|
||||
* Adds years to a date, in the {@code Duration} sense of semantic years. Use this instead of
|
||||
* {@link java.time.ZonedDateTime#plusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
public static Instant leapSafeAddYears(Instant now, long years) {
|
||||
public static Instant plusYears(Instant now, long years) {
|
||||
checkArgument(years >= 0);
|
||||
return (years == 0)
|
||||
? now
|
||||
@@ -133,8 +180,7 @@ public abstract class DateTimeUtils {
|
||||
* Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead
|
||||
* of {@link DateTime#minusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
@Deprecated
|
||||
public static DateTime leapSafeSubtractYears(DateTime now, int years) {
|
||||
public static DateTime minusYears(DateTime now, int years) {
|
||||
checkArgument(years >= 0);
|
||||
return years == 0 ? now : now.minusYears(1).minusYears(years - 1);
|
||||
}
|
||||
@@ -143,13 +189,31 @@ public abstract class DateTimeUtils {
|
||||
* Subtracts years from a date, in the {@code Duration} sense of semantic years. Use this instead
|
||||
* of {@link java.time.ZonedDateTime#minusYears} to ensure that we never end up on February 29.
|
||||
*/
|
||||
public static Instant leapSafeSubtractYears(Instant now, int years) {
|
||||
public static Instant minusYears(Instant now, long years) {
|
||||
checkArgument(years >= 0);
|
||||
return (years == 0)
|
||||
? now
|
||||
: now.atZone(ZoneOffset.UTC).minusYears(1).minusYears(years - 1).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #plusYears(DateTime, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static DateTime leapSafeAddYears(DateTime now, int years) {
|
||||
return plusYears(now, years);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #minusYears(DateTime, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("InlineMeSuggester")
|
||||
public static DateTime leapSafeSubtractYears(DateTime now, int years) {
|
||||
return minusYears(now, years);
|
||||
}
|
||||
|
||||
public static Date toSqlDate(LocalDate localDate) {
|
||||
return new Date(localDate.toDateTimeAtStartOfDay().getMillis());
|
||||
}
|
||||
@@ -176,10 +240,6 @@ public abstract class DateTimeUtils {
|
||||
return (instant == null) ? null : org.joda.time.Instant.ofEpochMilli(instant.toEpochMilli());
|
||||
}
|
||||
|
||||
public static Instant plusYears(Instant instant, int years) {
|
||||
return instant.atZone(ZoneOffset.UTC).plusYears(years).toInstant();
|
||||
}
|
||||
|
||||
public static Instant plusDays(Instant instant, int days) {
|
||||
return instant.atZone(ZoneOffset.UTC).plusDays(days).toInstant();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user