Remove ofy support from auto timestamp classes (#1708)

Also remove the use of ZonedDateTime, as joda DateTime can already be persisted to SQL with an existing converted.
This commit is contained in:
Lai Jiang 2022-07-26 09:51:35 -04:00 committed by GitHub
parent e0a0030837
commit badfb29ce1
22 changed files with 100 additions and 512 deletions

View file

@ -19,15 +19,13 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.TimeZone;
import java.sql.Date;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
/** Utilities methods and constants related to Joda {@link DateTime} objects. */
public class DateTimeUtils {
public abstract class DateTimeUtils {
/** The start of the epoch, in a convenient constant. */
public static final DateTime START_OF_TIME = new DateTime(0, DateTimeZone.UTC);
@ -91,39 +89,11 @@ public class DateTimeUtils {
return years == 0 ? now : now.minusYears(1).minusYears(years - 1);
}
/**
* Converts a Joda {@link DateTime} object to an equivalent java.time {@link ZonedDateTime}
* object.
*/
public static ZonedDateTime toZonedDateTime(DateTime dateTime) {
java.time.Instant instant = java.time.Instant.ofEpochMilli(dateTime.getMillis());
return ZonedDateTime.ofInstant(instant, ZoneId.of(dateTime.getZone().getID()).normalized());
public static Date toSqlDate(LocalDate localDate) {
return new Date(localDate.toDateTimeAtStartOfDay().getMillis());
}
/**
* Converts a Joda {@link DateTime} object to an equivalent java.time {@link ZonedDateTime}
* object.
*/
public static ZonedDateTime toZonedDateTime(DateTime dateTime, ZoneId zoneId) {
java.time.Instant instant = java.time.Instant.ofEpochMilli(dateTime.getMillis());
return ZonedDateTime.ofInstant(instant, zoneId);
}
/**
* Converts a java.time {@link ZonedDateTime} object to an equivalent Joda {@link DateTime}
* object.
*/
public static DateTime toJodaDateTime(ZonedDateTime zonedDateTime) {
return new DateTime(
zonedDateTime.toInstant().toEpochMilli(),
DateTimeZone.forTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone())));
}
public static java.sql.Date toSqlDate(LocalDate localDate) {
return new java.sql.Date(localDate.toDateTimeAtStartOfDay().getMillis());
}
public static LocalDate toLocalDate(java.sql.Date date) {
public static LocalDate toLocalDate(Date date) {
return new LocalDate(date.getTime(), DateTimeZone.UTC);
}
}

View file

@ -28,16 +28,16 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.coders.AtomicCoder;
import org.apache.beam.sdk.coders.Coder;
import org.apache.beam.sdk.coders.StringUtf8Coder;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
* A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}.
@ -51,7 +51,7 @@ import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
public abstract class BillingEvent implements Serializable {
private static final DateTimeFormatter DATE_TIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz");
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz");
/** The amount we multiply the price for sunrise creates. This is currently a 15% discount. */
private static final double SUNRISE_DISCOUNT_PRICE_MODIFIER = 0.85;
@ -77,10 +77,10 @@ public abstract class BillingEvent implements Serializable {
abstract long id();
/** Returns the UTC DateTime this event becomes billable. */
abstract ZonedDateTime billingTime();
abstract DateTime billingTime();
/** Returns the UTC DateTime this event was generated. */
abstract ZonedDateTime eventTime();
abstract DateTime eventTime();
/** Returns the billed registrar's name. */
abstract String registrarId();
@ -132,10 +132,8 @@ public abstract class BillingEvent implements Serializable {
// Objects, which contain a string representation of their underlying types.
Long.parseLong(extractField(record, "id")),
// Bigquery provides UNIX timestamps with microsecond precision.
Instant.ofEpochMilli(Long.parseLong(extractField(record, "billingTime")) / 1000)
.atZone(ZoneId.of("UTC")),
Instant.ofEpochMilli(Long.parseLong(extractField(record, "eventTime")) / 1000)
.atZone(ZoneId.of("UTC")),
new DateTime(Long.parseLong(extractField(record, "billingTime")) / 1000, DateTimeZone.UTC),
new DateTime(Long.parseLong(extractField(record, "eventTime")) / 1000, DateTimeZone.UTC),
extractField(record, "registrarId"),
extractField(record, "billingId"),
extractField(record, "poNumber"),
@ -180,8 +178,8 @@ public abstract class BillingEvent implements Serializable {
@VisibleForTesting
static BillingEvent create(
long id,
ZonedDateTime billingTime,
ZonedDateTime eventTime,
DateTime billingTime,
DateTime eventTime,
String registrarId,
String billingId,
String poNumber,
@ -231,8 +229,8 @@ public abstract class BillingEvent implements Serializable {
.join(
ImmutableList.of(
id(),
DATE_TIME_FORMATTER.format(billingTime()),
DATE_TIME_FORMATTER.format(eventTime()),
DATE_TIME_FORMATTER.print(billingTime()),
DATE_TIME_FORMATTER.print(eventTime()),
registrarId(),
billingId(),
poNumber(),

View file

@ -27,14 +27,12 @@ import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.reporting.billing.BillingModule;
import google.registry.util.DateTimeUtils;
import google.registry.util.DomainNameUtils;
import google.registry.util.SqlTemplate;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.Optional;
@ -123,8 +121,8 @@ public class InvoicingPipeline implements Serializable {
return Optional.of(
BillingEvent.create(
oneTime.getId(),
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
oneTime.getBillingTime(),
oneTime.getEventTime(),
registrar.getRegistrarId(),
registrar.getBillingAccountMap().get(currency),
registrar.getPoNumber().orElse(""),

View file

@ -14,6 +14,7 @@
package google.registry.model;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.util.PreconditionsUtils;
import javax.persistence.Access;
import javax.persistence.AccessType;
@ -44,6 +45,7 @@ public abstract class BackupGroupRoot extends ImmutableObject implements UnsafeS
// require an unnecessary non-private setter method.
@Access(AccessType.FIELD)
@AttributeOverride(name = "lastUpdateTime", column = @Column(name = "updateTimestamp"))
@Ignore
UpdateAutoTimestamp updateTimestamp = UpdateAutoTimestamp.create(null);
/** Get the {@link UpdateAutoTimestamp} for this entity. */

View file

@ -10,73 +10,43 @@
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// limitations under the Licenseschema..
package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime;
/**
* A timestamp that auto-updates when first saved to Datastore.
*
* @see CreateAutoTimestampTranslatorFactory
*/
/** A timestamp that auto-updates when first saved to Datastore. */
@Embeddable
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@Transient DateTime timestamp;
@Column(nullable = false)
@Ignore
ZonedDateTime creationTime;
DateTime creationTime;
@PrePersist
@PreUpdate
void setTimestamp() {
if (creationTime == null) {
timestamp = jpaTm().getTransactionTime();
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
creationTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (creationTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(creationTime);
creationTime = jpaTm().getTransactionTime();
}
}
/** Returns the timestamp. */
@Nullable
public DateTime getTimestamp() {
return timestamp;
return creationTime;
}
public static CreateAutoTimestamp create(@Nullable DateTime timestamp) {
public static CreateAutoTimestamp create(@Nullable DateTime creationTime) {
CreateAutoTimestamp instance = new CreateAutoTimestamp();
instance.timestamp = timestamp;
instance.creationTime = (timestamp == null) ? null : DateTimeUtils.toZonedDateTime(timestamp);
instance.creationTime = creationTime;
return instance;
}
}

View file

@ -32,6 +32,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import google.registry.config.RegistryConfig;
import google.registry.model.CacheUtils.AppEngineEnvironmentCacheLoader;
@ -111,7 +112,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
* resource fields.
*/
@AttributeOverrides(@AttributeOverride(name = "creationTime", column = @Column))
@Index
@Ignore
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/**

View file

@ -59,8 +59,7 @@ public final class SchemaVersion {
* types (for classes), or else a list of all possible values (for enums).
*/
public static String getSchema() {
return getAllPersistedTypes()
.stream()
return getAllPersistedTypes().stream()
.filter(or(subtypeOf(Enum.class), subtypeOf(ImmutableObject.class)))
.map(ModelUtils::getSchema)
.collect(joining("\n"));

View file

@ -17,23 +17,15 @@ package google.registry.model;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.joda.time.DateTime;
/**
* A timestamp that auto-updates on each save to Datastore/Cloud SQL.
*/
/** A timestamp that auto-updates on each save to Datastore/Cloud SQL. */
@Embeddable
public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
@ -42,11 +34,8 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
// during a replay).
private static final ThreadLocal<Boolean> autoUpdateEnabled = ThreadLocal.withInitial(() -> true);
@Transient DateTime timestamp;
@Ignore
@Column(nullable = false)
ZonedDateTime lastUpdateTime;
DateTime lastUpdateTime;
// Unfortunately, we cannot use the @UpdateTimestamp annotation on "lastUpdateTime" in this class
// because Hibernate does not allow it to be used on @Embeddable classes, see
@ -55,34 +44,18 @@ public class UpdateAutoTimestamp extends ImmutableObject implements UnsafeSerial
@PreUpdate
void setTimestamp() {
if (autoUpdateEnabled() || lastUpdateTime == null) {
timestamp = jpaTm().getTransactionTime();
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@OnLoad
void onLoad() {
if (timestamp != null) {
lastUpdateTime = DateTimeUtils.toZonedDateTime(timestamp);
}
}
@PostLoad
void postLoad() {
if (lastUpdateTime != null) {
timestamp = DateTimeUtils.toJodaDateTime(lastUpdateTime);
lastUpdateTime = jpaTm().getTransactionTime();
}
}
/** Returns the timestamp, or {@code START_OF_TIME} if it's null. */
public DateTime getTimestamp() {
return Optional.ofNullable(timestamp).orElse(START_OF_TIME);
return Optional.ofNullable(lastUpdateTime).orElse(START_OF_TIME);
}
public static UpdateAutoTimestamp create(@Nullable DateTime timestamp) {
UpdateAutoTimestamp instance = new UpdateAutoTimestamp();
instance.timestamp = timestamp;
instance.lastUpdateTime = timestamp == null ? null : DateTimeUtils.toZonedDateTime(timestamp);
instance.lastUpdateTime = timestamp;
return instance;
}

View file

@ -16,15 +16,12 @@ package google.registry.model.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.UpdateAutoTimestamp;
import google.registry.util.DateTimeUtils;
import java.time.ZonedDateTime;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.AttributeOverride;
@ -109,19 +106,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
private CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
/** When the unlock is first requested. */
private ZonedDateTime unlockRequestTime;
private DateTime unlockRequestTime;
/**
* When the user has verified the lock. If this field is null, it means the lock has not been
* verified yet (and thus not been put into effect).
*/
private ZonedDateTime lockCompletionTime;
private DateTime lockCompletionTime;
/**
* When the user has verified the unlock of this lock. If this field is null, it means the unlock
* action has not been verified yet (and has not been put into effect).
*/
private ZonedDateTime unlockCompletionTime;
private DateTime unlockCompletionTime;
/** The user must provide the random verification code in order to complete the action. */
@Column(nullable = false)
@ -168,19 +165,19 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
/** Returns the unlock request timestamp or null if an unlock has not been requested yet. */
public Optional<DateTime> getUnlockRequestTime() {
return Optional.ofNullable(unlockRequestTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(unlockRequestTime);
}
/** Returns the completion timestamp, or empty if this lock has not been completed yet. */
public Optional<DateTime> getLockCompletionTime() {
return Optional.ofNullable(lockCompletionTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(lockCompletionTime);
}
/**
* Returns the unlock completion timestamp, or empty if this unlock has not been completed yet.
*/
public Optional<DateTime> getUnlockCompletionTime() {
return Optional.ofNullable(unlockCompletionTime).map(DateTimeUtils::toJodaDateTime);
return Optional.ofNullable(unlockCompletionTime);
}
public String getVerificationCode() {
@ -278,17 +275,17 @@ public final class RegistryLock extends ImmutableObject implements Buildable {
}
public Builder setUnlockRequestTime(DateTime unlockRequestTime) {
getInstance().unlockRequestTime = toZonedDateTime(unlockRequestTime);
getInstance().unlockRequestTime = unlockRequestTime;
return this;
}
public Builder setLockCompletionTime(DateTime lockCompletionTime) {
getInstance().lockCompletionTime = toZonedDateTime(lockCompletionTime);
getInstance().lockCompletionTime = lockCompletionTime;
return this;
}
public Builder setUnlockCompletionTime(DateTime unlockCompletionTime) {
getInstance().unlockCompletionTime = toZonedDateTime(unlockCompletionTime);
getInstance().unlockCompletionTime = unlockCompletionTime;
return this;
}

View file

@ -33,6 +33,7 @@ import com.google.common.collect.Range;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.OnLoad;
import google.registry.flows.EppException;
@ -138,7 +139,7 @@ public class AllocationToken extends BackupGroupRoot implements Buildable {
@Nullable @Index String domainName;
/** When this token was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** Allowed registrar client IDs for this token, or null if all registrars are allowed. */
@Column(name = "allowedRegistrarIds")

View file

@ -38,7 +38,6 @@ import google.registry.model.ImmutableObject;
import google.registry.model.annotations.DeleteAfterMigration;
import google.registry.model.translators.BloomFilterOfStringTranslatorFactory;
import google.registry.model.translators.CidrAddressBlockTranslatorFactory;
import google.registry.model.translators.CreateAutoTimestampTranslatorFactory;
import google.registry.model.translators.CurrencyUnitTranslatorFactory;
import google.registry.model.translators.DurationTranslatorFactory;
import google.registry.model.translators.EppHistoryVKeyTranslatorFactory;
@ -122,7 +121,6 @@ public class ObjectifyService {
ImmutableList.of(
new BloomFilterOfStringTranslatorFactory(),
new CidrAddressBlockTranslatorFactory(),
new CreateAutoTimestampTranslatorFactory(),
new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(),
new EppHistoryVKeyTranslatorFactory(),

View file

@ -58,6 +58,7 @@ import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.IgnoreSave;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;
@ -407,10 +408,10 @@ public class Registrar extends ImmutableObject
// Metadata.
/** The time when this registrar was created. */
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Ignore CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** An automatically managed last-saved timestamp. */
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
@Ignore UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;

View file

@ -1,58 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.translators;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.joda.time.DateTimeZone.UTC;
import google.registry.model.CreateAutoTimestamp;
import java.util.Date;
import org.joda.time.DateTime;
/** Saves {@link CreateAutoTimestamp} as either its value, or the current time if it was null. */
public class CreateAutoTimestampTranslatorFactory
extends AbstractSimpleTranslatorFactory<CreateAutoTimestamp, Date> {
public CreateAutoTimestampTranslatorFactory() {
super(CreateAutoTimestamp.class);
}
@Override
SimpleTranslator<CreateAutoTimestamp, Date> createTranslator() {
return new SimpleTranslator<CreateAutoTimestamp, Date>() {
/**
* Load an existing timestamp. It can be assumed to be non-null since if the field is null in
* Datastore then Objectify will skip this translator and directly load a null.
*/
@Override
public CreateAutoTimestamp loadValue(Date datastoreValue) {
return CreateAutoTimestamp.create(new DateTime(datastoreValue, UTC));
}
/** Save a timestamp, setting it to the current time if it did not have a previous value. */
@Override
public Date saveValue(CreateAutoTimestamp pojoValue) {
// Note that we use the current transaction manager -- we need to do this under JPA when we
// serialize the entity from a Transaction object, but we need to use the JPA transaction
// manager in that case.
return (pojoValue.getTimestamp() == null
? tm().getTransactionTime()
: pojoValue.getTimestamp())
.toDate();
}
};
}
}

View file

@ -1,48 +0,0 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.converter;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import javax.annotation.Nullable;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* JPA converter to for storing/retrieving {@link ZonedDateTime} objects.
*
* <p>Hibernate provides a default converter for {@link ZonedDateTime}, but it converts timestamp to
* a non-normalized format, e.g., 2019-09-01T01:01:01Z will be converted to
* 2019-09-01T01:01:01Z[UTC]. This converter solves that problem by explicitly calling {@link
* ZoneId#normalized()} to normalize the zone id.
*/
@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
@Nullable
public Timestamp convertToDatabaseColumn(@Nullable ZonedDateTime attribute) {
return attribute == null ? null : Timestamp.from(attribute.toInstant());
}
@Override
@Nullable
public ZonedDateTime convertToEntityAttribute(@Nullable Timestamp dbData) {
return dbData == null
? null
: ZonedDateTime.ofInstant(dbData.toInstant(), ZoneId.of("UTC").normalized());
}
}

View file

@ -96,7 +96,6 @@
<class>google.registry.persistence.converter.StringSetConverter</class>
<class>google.registry.persistence.converter.TldStateTransitionConverter</class>
<class>google.registry.persistence.converter.TransferServerApproveEntitySetConverter</class>
<class>google.registry.persistence.converter.ZonedDateTimeConverter</class>
<!-- Generated converters for VKey -->
<class>google.registry.model.billing.VKeyConverter_Cancellation</class>

View file

@ -23,12 +23,11 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.beam.sdk.io.gcp.bigquery.SchemaAndRecord;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -86,10 +85,8 @@ class BillingEventTest {
void testParseBillingEventFromRecord_success() {
BillingEvent event = BillingEvent.parseFromRecord(schemaAndRecord);
assertThat(event.id()).isEqualTo(1);
assertThat(event.billingTime())
.isEqualTo(ZonedDateTime.of(2017, 10, 24, 9, 6, 3, 0, ZoneId.of("UTC")));
assertThat(event.eventTime())
.isEqualTo(ZonedDateTime.of(2017, 1, 19, 23, 59, 43, 0, ZoneId.of("UTC")));
assertThat(event.billingTime()).isEqualTo(DateTime.parse("2017-10-24T09:06:03Z"));
assertThat(event.eventTime()).isEqualTo(DateTime.parse("2017-01-19T23:59:43Z"));
assertThat(event.registrarId()).isEqualTo("myRegistrar");
assertThat(event.billingId()).isEqualTo("12345-CRRHELLO");
assertThat(event.poNumber()).isEmpty();

View file

@ -55,8 +55,6 @@ import google.registry.util.ResourceUtils;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.Optional;
@ -104,8 +102,8 @@ class InvoicingPipelineTest {
ImmutableList.of(
BillingEvent.create(
1,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar",
"234",
"",
@ -119,8 +117,8 @@ class InvoicingPipelineTest {
""),
BillingEvent.create(
2,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar",
"234",
"",
@ -134,8 +132,8 @@ class InvoicingPipelineTest {
""),
BillingEvent.create(
3,
ZonedDateTime.of(2017, 10, 2, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 9, 29, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-02T00:00:00Z"),
DateTime.parse("2017-09-29T00:00:00Z"),
"theRegistrar",
"234",
"",
@ -149,8 +147,8 @@ class InvoicingPipelineTest {
""),
BillingEvent.create(
4,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"bestdomains",
"456",
"116688",
@ -164,8 +162,8 @@ class InvoicingPipelineTest {
""),
BillingEvent.create(
5,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"anotherRegistrar",
"789",
"",
@ -179,8 +177,8 @@ class InvoicingPipelineTest {
"SUNRISE ANCHOR_TENANT"),
BillingEvent.create(
6,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar",
"234",
"",
@ -194,8 +192,8 @@ class InvoicingPipelineTest {
""),
BillingEvent.create(
7,
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
ZonedDateTime.of(2017, 10, 4, 0, 0, 0, 0, ZoneId.of("UTC")),
DateTime.parse("2017-10-04T00:00:00Z"),
DateTime.parse("2017-10-04T00:00:00Z"),
"theRegistrar",
"234",
"",

View file

@ -19,10 +19,11 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static org.joda.time.DateTimeZone.UTC;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton;
import google.registry.testing.AppEngineExtension;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@ -31,22 +32,19 @@ import org.junit.jupiter.api.extension.RegisterExtension;
public class CreateAutoTimestampTest {
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withCloudSql()
.withOfyTestEntities(CreateAutoTimestampTestObject.class)
.withJpaUnitTestEntities(CreateAutoTimestampTestObject.class)
.build();
public final JpaUnitTestExtension jpaUnitTestExtension =
new JpaTestExtensions.Builder()
.withEntityClass(CreateAutoTimestampTestObject.class)
.buildUnitTestExtension();
/** Timestamped class. */
@Entity(name = "CatTestEntity")
@javax.persistence.Entity
@Entity
public static class CreateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID;
@Id long id = SINGLETON_ID;
CreateAutoTimestamp createTime = CreateAutoTimestamp.create(null);
}
private CreateAutoTimestampTestObject reload() {
private static CreateAutoTimestampTestObject reload() {
return loadByEntity(new CreateAutoTimestampTestObject());
}
@ -61,7 +59,7 @@ public class CreateAutoTimestampTest {
return tm().getTransactionTime();
});
tm().clearSessionCache();
assertThat(reload().createTime.timestamp).isEqualTo(transactionTime);
assertThat(reload().createTime.getTimestamp()).isEqualTo(transactionTime);
}
@Test
@ -74,6 +72,6 @@ public class CreateAutoTimestampTest {
tm().put(object);
});
tm().clearSessionCache();
assertThat(reload().createTime.timestamp).isEqualTo(oldCreateTime);
assertThat(reload().createTime.getTimestamp()).isEqualTo(oldCreateTime);
}
}

View file

@ -16,59 +16,42 @@ package google.registry.model;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.time.DateTimeZone.UTC;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.common.CrossTldSingleton;
import google.registry.model.ofy.Ofy;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.InjectExtension;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link UpdateAutoTimestamp}. */
public class UpdateAutoTimestampTest {
FakeClock clock = new FakeClock();
private final FakeClock clock = new FakeClock();
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withCloudSql()
.withJpaUnitTestEntities(UpdateAutoTimestampTestObject.class)
.withOfyTestEntities(UpdateAutoTimestampTestObject.class)
public final JpaUnitTestExtension jpaUnitTestExtension =
new JpaTestExtensions.Builder()
.withClock(clock)
.build();
@RegisterExtension public final InjectExtension inject = new InjectExtension();
@BeforeEach
void beforeEach() {
inject.setStaticField(Ofy.class, "clock", clock);
}
.withEntityClass(UpdateAutoTimestampTestObject.class)
.buildUnitTestExtension();
/** Timestamped class. */
@Entity(name = "UatTestEntity")
@javax.persistence.Entity
@Entity
public static class UpdateAutoTimestampTestObject extends CrossTldSingleton {
@Ignore @javax.persistence.Id long id = SINGLETON_ID;
@Id long id = SINGLETON_ID;
UpdateAutoTimestamp updateTime = UpdateAutoTimestamp.create(null);
}
private UpdateAutoTimestampTestObject reload() {
private static UpdateAutoTimestampTestObject reload() {
return tm().transact(
() ->
tm().loadByKey(
VKey.create(
UpdateAutoTimestampTestObject.class,
1L,
Key.create(new UpdateAutoTimestampTestObject()))));
() -> tm().loadByKey(VKey.createSql(UpdateAutoTimestampTestObject.class, 1L)));
}
@Test
@ -78,12 +61,12 @@ public class UpdateAutoTimestampTest {
() -> {
clock.advanceOneMilli();
UpdateAutoTimestampTestObject object = new UpdateAutoTimestampTestObject();
assertThat(object.updateTime.timestamp).isNull();
assertThat(object.updateTime.getTimestamp()).isEqualTo(START_OF_TIME);
tm().insert(object);
return tm().getTransactionTime();
});
tm().clearSessionCache();
assertThat(reload().updateTime.timestamp).isEqualTo(transactionTime);
assertThat(reload().updateTime.getTimestamp()).isEqualTo(transactionTime);
}
@Test
@ -99,7 +82,7 @@ public class UpdateAutoTimestampTest {
UpdateAutoTimestampTestObject object = reload();
clock.advanceOneMilli();
try (UpdateAutoTimestamp.DisableAutoUpdateResource disabler =
try (UpdateAutoTimestamp.DisableAutoUpdateResource ignoredDisabler =
new UpdateAutoTimestamp.DisableAutoUpdateResource()) {
DateTime secondTransactionTime =
tm().transact(
@ -109,7 +92,7 @@ public class UpdateAutoTimestampTest {
});
assertThat(secondTransactionTime).isGreaterThan(initialTime);
}
assertThat(reload().updateTime.timestamp).isEqualTo(initialTime);
assertThat(reload().updateTime.getTimestamp()).isEqualTo(initialTime);
}
@Test
@ -124,7 +107,7 @@ public class UpdateAutoTimestampTest {
return tm().getTransactionTime();
});
tm().clearSessionCache();
assertThat(reload().updateTime.timestamp).isEqualTo(transactionTime);
assertThat(reload().updateTime.getTimestamp()).isEqualTo(transactionTime);
}
@Test

View file

@ -1,116 +0,0 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.converter;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatabaseHelper.insertInDb;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZonedDateTime;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ZonedDateTimeConverter}. */
public class ZonedDateTimeConverterTest {
@RegisterExtension
public final JpaUnitTestExtension jpaExtension =
new JpaTestExtensions.Builder().withEntityClass(TestEntity.class).buildUnitTestExtension();
private final ZonedDateTimeConverter converter = new ZonedDateTimeConverter();
@Test
void convertToDatabaseColumn_returnsNullIfInputIsNull() {
assertThat(converter.convertToDatabaseColumn(null)).isNull();
}
@Test
void convertToDatabaseColumn_convertsCorrectly() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-01T01:01:01Z");
assertThat(converter.convertToDatabaseColumn(zonedDateTime).toInstant())
.isEqualTo(zonedDateTime.toInstant());
}
@Test
void convertToEntityAttribute_returnsNullIfInputIsNull() {
assertThat(converter.convertToEntityAttribute(null)).isNull();
}
@Test
void convertToEntityAttribute_convertsCorrectly() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-01T01:01:01Z");
Instant instant = zonedDateTime.toInstant();
assertThat(converter.convertToEntityAttribute(Timestamp.from(instant)))
.isEqualTo(zonedDateTime);
}
@Test
void converter_generatesTimestampWithNormalizedZone() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01Z");
TestEntity entity = new TestEntity("normalized_utc_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm()
.transact(
() -> jpaTm().getEntityManager().find(TestEntity.class, "normalized_utc_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-09-01T01:01:01Z");
}
@Test
void converter_convertsNonNormalizedZoneCorrectly() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01Z[UTC]");
TestEntity entity = new TestEntity("non_normalized_utc_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm()
.transact(
() -> jpaTm().getEntityManager().find(TestEntity.class, "non_normalized_utc_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-09-01T01:01:01Z");
}
@Test
void converter_convertsNonUtcZoneCorrectly() {
ZonedDateTime zdt = ZonedDateTime.parse("2019-09-01T01:01:01+05:00");
TestEntity entity = new TestEntity("new_york_time", zdt);
insertInDb(entity);
TestEntity retrievedEntity =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "new_york_time"));
assertThat(retrievedEntity.zdt.toString()).isEqualTo("2019-08-31T20:01:01Z");
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name;
ZonedDateTime zdt;
public TestEntity() {}
TestEntity(String name, ZonedDateTime zdt) {
this.name = name;
this.zdt = zdt;
}
}
}

View file

@ -1,9 +1,3 @@
class google.registry.model.CreateAutoTimestamp {
org.joda.time.DateTime timestamp;
}
class google.registry.model.UpdateAutoTimestamp {
org.joda.time.DateTime timestamp;
}
class google.registry.model.billing.BillingEvent$Cancellation {
@Id java.lang.Long id;
@Parent com.googlecode.objectify.Key<google.registry.model.domain.DomainHistory> parent;
@ -81,7 +75,6 @@ enum google.registry.model.billing.BillingEvent$RenewalPriceBehavior {
}
class google.registry.model.common.EntityGroupRoot {
@Id java.lang.String id;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
}
class google.registry.model.common.GaeUserIdConverter {
@Id long id;
@ -102,8 +95,6 @@ class google.registry.model.contact.ContactAuthInfo {
}
class google.registry.model.contact.ContactBase {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.contact.ContactAuthInfo authInfo;
google.registry.model.contact.ContactPhoneNumber fax;
google.registry.model.contact.ContactPhoneNumber voice;
@ -144,8 +135,6 @@ class google.registry.model.contact.ContactPhoneNumber {
}
class google.registry.model.contact.ContactResource {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.contact.ContactAuthInfo authInfo;
google.registry.model.contact.ContactPhoneNumber fax;
google.registry.model.contact.ContactPhoneNumber voice;
@ -191,8 +180,6 @@ class google.registry.model.domain.DomainAuthInfo {
}
class google.registry.model.domain.DomainBase {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.domain.DomainAuthInfo authInfo;
google.registry.model.domain.launch.LaunchNotice launchNotice;
google.registry.model.transfer.DomainTransferData transferData;
@ -223,8 +210,6 @@ class google.registry.model.domain.DomainBase {
}
class google.registry.model.domain.DomainContent {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.domain.DomainAuthInfo authInfo;
google.registry.model.domain.launch.LaunchNotice launchNotice;
google.registry.model.transfer.DomainTransferData transferData;
@ -334,8 +319,6 @@ class google.registry.model.domain.token.AllocationToken {
@Id java.lang.String token;
boolean discountPremiums;
double discountFraction;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.model.billing.BillingEvent$RenewalPriceBehavior renewalPriceBehavior;
google.registry.model.common.TimedTransitionProperty<google.registry.model.domain.token.AllocationToken$TokenStatus> tokenStatusTransitions;
google.registry.model.domain.token.AllocationToken$RegistrationBehavior registrationBehavior;
@ -393,8 +376,6 @@ class google.registry.model.eppcommon.Trid {
}
class google.registry.model.host.HostBase {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain;
java.lang.String creationClientId;
java.lang.String currentSponsorClientId;
@ -425,8 +406,6 @@ class google.registry.model.host.HostHistory {
}
class google.registry.model.host.HostResource {
@Id java.lang.String repoId;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<google.registry.model.domain.DomainBase> superordinateDomain;
java.lang.String creationClientId;
java.lang.String currentSponsorClientId;
@ -443,7 +422,6 @@ class google.registry.model.index.EppResourceIndex {
@Id java.lang.String id;
@Parent com.googlecode.objectify.Key<google.registry.model.index.EppResourceIndexBucket> bucket;
com.googlecode.objectify.Key<? extends google.registry.model.EppResource> reference;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
java.lang.String kind;
}
class google.registry.model.index.EppResourceIndexBucket {
@ -451,19 +429,16 @@ class google.registry.model.index.EppResourceIndexBucket {
}
class google.registry.model.index.ForeignKeyIndex$ForeignKeyContactIndex {
@Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime;
}
class google.registry.model.index.ForeignKeyIndex$ForeignKeyDomainIndex {
@Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime;
}
class google.registry.model.index.ForeignKeyIndex$ForeignKeyHostIndex {
@Id java.lang.String foreignKey;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
google.registry.persistence.VKey<E> topReference;
org.joda.time.DateTime deletionTime;
}
@ -489,7 +464,6 @@ class google.registry.model.poll.PollMessage$OneTime {
}
class google.registry.model.rde.RdeRevision {
@Id java.lang.String id;
google.registry.model.UpdateAutoTimestamp updateTimestamp;
int revision;
}
class google.registry.model.registrar.Registrar {
@ -498,8 +472,6 @@ class google.registry.model.registrar.Registrar {
boolean blockPremiumNames;
boolean contactsRequireSyncing;
boolean registryLockAllowed;
google.registry.model.CreateAutoTimestamp creationTime;
google.registry.model.UpdateAutoTimestamp lastUpdateTime;
google.registry.model.registrar.Registrar$State state;
google.registry.model.registrar.Registrar$Type type;
google.registry.model.registrar.RegistrarAddress internationalizedAddress;

View file

@ -23,15 +23,12 @@ import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.latestOf;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DateTimeUtils.leapSafeSubtractYears;
import static google.registry.util.DateTimeUtils.toJodaDateTime;
import static google.registry.util.DateTimeUtils.toLocalDate;
import static google.registry.util.DateTimeUtils.toSqlDate;
import static google.registry.util.DateTimeUtils.toZonedDateTime;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import java.sql.Date;
import java.time.ZonedDateTime;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.junit.jupiter.api.Test;
@ -99,48 +96,6 @@ class DateTimeUtilsTest {
assertThrows(IllegalArgumentException.class, () -> earliestOf(ImmutableList.of()));
}
@Test
void testSuccess_toZonedDateTime_preservesTimeZone() {
DateTime dateTime = DateTime.parse("2019-09-06T10:59:36.283-07:00"); // PDT
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2019-09-06T10:59:36.283-07:00"); // still PDT
}
@Test
void testSuccess_toZonedDateTime_fromStringZulu() {
DateTime dateTime = DateTime.parse("2015-10-13T11:22:33.168Z");
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2015-10-13T11:22:33.168Z");
}
@Test
void testSuccess_toZonedDateTime_leapYear() {
DateTime dateTime = DateTime.parse("2016-02-29T11:22:33.168Z");
ZonedDateTime zonedDateTime = toZonedDateTime(dateTime);
assertThat(zonedDateTime.toString()).isEqualTo("2016-02-29T11:22:33.168Z");
}
@Test
void testSuccess_toJodaDateTime_preservesTimeZone() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-09-06T10:59:36.283-07:00"); // PDT
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2019-09-06T10:59:36.283-07:00"); // still PDT
}
@Test
void testSuccess_toJodaDateTime_fromStringZulu() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2015-10-13T11:22:33.168Z");
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2015-10-13T11:22:33.168Z");
}
@Test
void testSuccess_toJodaDateTime_leapYear() {
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2016-02-29T11:22:33.168Z");
DateTime dateTime = toJodaDateTime(zonedDateTime);
assertThat(dateTime.toString()).isEqualTo("2016-02-29T11:22:33.168Z");
}
@Test
void testSuccess_toSqlDate() {
LocalDate localDate = LocalDate.parse("2020-02-29");