FINERACT-1724: Enhance external event datetime precision

This commit is contained in:
Adam Saghy
2023-05-08 12:54:30 +02:00
committed by Arnold Gálovics
parent 0e6f5eab9d
commit f24c9f8084
5 changed files with 98 additions and 7 deletions
@@ -66,9 +66,9 @@ public final class DateUtils {
return OffsetDateTime.now(zone).truncatedTo(ChronoUnit.SECONDS); return OffsetDateTime.now(zone).truncatedTo(ChronoUnit.SECONDS);
} }
public static OffsetDateTime getOffsetDateTimeOfTenantWithMilliseconds() { public static OffsetDateTime getOffsetDateTimeOfTenantWithMostPrecision() {
final ZoneId zone = getDateTimeZoneOfTenant(); final ZoneId zone = getDateTimeZoneOfTenant();
return OffsetDateTime.now(zone).truncatedTo(ChronoUnit.MILLIS); return OffsetDateTime.now(zone);
} }
public static LocalDateTime getLocalDateTimeOfSystem() { public static LocalDateTime getLocalDateTimeOfSystem() {
@@ -98,7 +98,7 @@ public class SendAsynchronousEventsTasklet implements Tasklet {
} }
private void markEventsAsSent(List<Long> eventIds) { private void markEventsAsSent(List<Long> eventIds) {
OffsetDateTime sentAt = DateUtils.getOffsetDateTimeOfTenantWithMilliseconds(); OffsetDateTime sentAt = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision();
// Partitioning dataset to avoid exception: PreparedStatement can have at most 65,535 parameters // Partitioning dataset to avoid exception: PreparedStatement can have at most 65,535 parameters
List<List<Long>> partitions = Lists.partition(eventIds, 5_000); List<List<Long>> partitions = Lists.partition(eventIds, 5_000);
@@ -80,7 +80,7 @@ public class ExternalEvent extends AbstractPersistableCustom {
this.data = data; this.data = data;
this.idempotencyKey = idempotencyKey; this.idempotencyKey = idempotencyKey;
this.aggregateRootId = aggregateRootId; this.aggregateRootId = aggregateRootId;
this.createdAt = DateUtils.getOffsetDateTimeOfTenantWithMilliseconds(); this.createdAt = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision();
this.status = ExternalEventStatus.TO_BE_SENT; this.status = ExternalEventStatus.TO_BE_SENT;
this.businessDate = DateUtils.getBusinessLocalDate(); this.businessDate = DateUtils.getBusinessLocalDate();
} }
@@ -18,10 +18,14 @@
*/ */
package org.apache.fineract.infrastructure.event.external.service.message; package org.apache.fineract.infrastructure.event.external.service.message;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -46,9 +50,21 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class MessageFactory implements InitializingBean { public class MessageFactory implements InitializingBean {
private final ByteBufferConverter byteBufferConverter; public static final DateTimeFormatter CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER;
private static final String SOURCE_UUID = UUID.randomUUID().toString(); private static final String SOURCE_UUID = UUID.randomUUID().toString();
private static final DateTimeFormatter CUSTOM_ISO_LOCAL_TIME_FORMATTER;
static {
CUSTOM_ISO_LOCAL_TIME_FORMATTER = new DateTimeFormatterBuilder().appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2).optionalStart().appendLiteral(':').appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 6, 9, true).toFormatter();
CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder().parseCaseInsensitive().append(ISO_LOCAL_DATE)
.appendLiteral('T').append(CUSTOM_ISO_LOCAL_TIME_FORMATTER).toFormatter();
}
private final ByteBufferConverter byteBufferConverter;
public MessageV1 createMessage(MessageId id, MessageSource source, MessageType type, MessageCategory category, public MessageV1 createMessage(MessageId id, MessageSource source, MessageType type, MessageCategory category,
MessageCreatedAt createdAt, MessageBusinessDate businessDate, MessageIdempotencyKey idempotencyKey, MessageCreatedAt createdAt, MessageBusinessDate businessDate, MessageIdempotencyKey idempotencyKey,
MessageDataSchema dataSchema, MessageData data) { MessageDataSchema dataSchema, MessageData data) {
@@ -84,11 +100,11 @@ public class MessageFactory implements InitializingBean {
} }
private String getMessageCreatedAt(OffsetDateTime createdAt) { private String getMessageCreatedAt(OffsetDateTime createdAt) {
return createdAt.withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); return createdAt.withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime().format(CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER);
} }
private String getMessageBusinessDate(LocalDate businessDate) { private String getMessageBusinessDate(LocalDate businessDate) {
return businessDate.format(DateTimeFormatter.ISO_LOCAL_DATE); return businessDate.format(ISO_LOCAL_DATE);
} }
@Override @Override
@@ -0,0 +1,75 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.fineract.infrastructure.event.external.service.message;
import static org.junit.Assert.assertEquals;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.junit.Test;
public class MessageFactoryTest {
private final LocalDateTime localDateTime1 = LocalDateTime.of(2023, 5, 4, 10, 54, 0, 0);
private final LocalDateTime localDateTime2 = LocalDateTime.of(2023, 5, 4, 10, 54, 1, 0);
private final LocalDateTime localDateTime3 = LocalDateTime.of(2023, 5, 4, 10, 54, 1, 1000);
private final LocalDateTime localDateTime4 = LocalDateTime.of(2023, 5, 4, 10, 0, 0, 1000);
private final LocalDateTime localDateTime5 = LocalDateTime.of(2023, 5, 4, 10, 0, 0, 1000000);
private final LocalDateTime localDateTime6 = LocalDateTime.of(2023, 5, 4, 10, 0, 0, 1234567);
/**
* Test whether the tailing zeros are always part of the formatter date time.
*/
@Test
public void formatterTest() {
DateTimeFormatter dateTimeFormatter = MessageFactory.CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER;
assertEquals("2023-05-04T10:54:00.000000", localDateTime1.format(dateTimeFormatter));
assertEquals("2023-05-04T10:54:01.000000", localDateTime2.format(dateTimeFormatter));
// 1 microsecond
assertEquals("2023-05-04T10:54:01.000001", localDateTime3.format(dateTimeFormatter));
assertEquals("2023-05-04T10:00:00.000001", localDateTime4.format(dateTimeFormatter));
// 1 millisecond
assertEquals("2023-05-04T10:00:00.001000", localDateTime5.format(dateTimeFormatter));
assertEquals("2023-05-04T10:00:00.001234567", localDateTime6.format(dateTimeFormatter));
}
/**
* Test whether the formatted datetime by CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER is still parseable by the
* (non-custom) ISO_LOCAL_DATE_TIME_FORMATTER (compatibility check)
*/
@Test
public void backParsingTest() {
DateTimeFormatter customDateTimeFormatter = MessageFactory.CUSTOM_ISO_LOCAL_DATE_TIME_FORMATTER;
assertEquals(localDateTime1,
LocalDateTime.parse(localDateTime1.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
assertEquals(localDateTime2,
LocalDateTime.parse(localDateTime2.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
assertEquals(localDateTime3,
LocalDateTime.parse(localDateTime3.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
assertEquals(localDateTime4,
LocalDateTime.parse(localDateTime4.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
assertEquals(localDateTime5,
LocalDateTime.parse(localDateTime5.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
assertEquals(localDateTime6,
LocalDateTime.parse(localDateTime6.format(customDateTimeFormatter), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}