Update DCDate to only used ZonedDateTime in order to get tests to pass

This commit is contained in:
Tim Donohue
2025-02-19 16:03:33 -06:00
parent b059cfd36b
commit 8ed24bd9b1
12 changed files with 115 additions and 72 deletions

View File

@@ -8,7 +8,9 @@
package org.dspace.content;
import java.text.DateFormatSymbols;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
@@ -54,7 +56,7 @@ public class DCDate {
private ZonedDateTime calendar = null;
// components of time in local zone
private LocalDateTime localCalendar = null;
private ZonedDateTime localCalendar = null;
private enum DateGran { YEAR, MONTH, DAY, TIME }
@@ -99,7 +101,7 @@ public class DCDate {
*
* @param date the Java <code>Instant</code> object.
*/
public DCDate(LocalDateTime date) {
public DCDate(ZonedDateTime date) {
if (date == null) {
return;
}
@@ -107,11 +109,11 @@ public class DCDate {
// By definition a Date has a time component so always set the granularity to TIME.
granularity = DateGran.TIME;
// Set the local calendar.
// Set the local calendar based on timezone of the passed in ZonedDateTime
localCalendar = date;
// Now set the UTC equivalent. This converts local system default to UTC time.
calendar = date.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC);
// Time is assumed to be in UTC timezone because DSpace stores all dates internally as UTC
calendar = date.withZoneSameInstant(ZoneOffset.UTC);
}
/**
@@ -159,13 +161,12 @@ public class DCDate {
granularity = DateGran.TIME;
}
// Set the local calendar.
localCalendar = LocalDateTime.of(lyear, lmonth, lday, lhours, lminutes, lseconds);
// Set the local calendar based on system default timezone
localCalendar = ZonedDateTime.of(lyear, lmonth, lday, lhours, lminutes, lseconds, 0, ZoneId.systemDefault());
if (granularity == DateGran.TIME) {
// Now set the UTC equivalent.
calendar = localCalendar.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC);
calendar = localCalendar.withZoneSameInstant(ZoneOffset.UTC);
} else {
// No Time component so just set the UTC date to be the same as the local Year, Month, and Day.
calendar = ZonedDateTime.of(localCalendar.getYear(),
@@ -188,7 +189,9 @@ public class DCDate {
// default granularity
granularity = DateGran.TIME;
LocalDateTime date = tryParse(fullIso, fromDC);
// Try to parse a full date/time using various formats
ZonedDateTime date = tryParse(fullIso, fromDC);
if (date == null) {
date = tryParse(fullIso2, fromDC);
}
@@ -201,21 +204,41 @@ public class DCDate {
if (date == null) {
date = tryParse(fullIsoWithMs, fromDC);
}
// Seems there is no time component to the date, so we'll need to use specialized java.time classes
// to parse out the day, month or year.
// Try to parse as just a date (no time) in UTC.
if (date == null) {
// Seems there is no time component to the date.
date = tryParse(dateIso, fromDC);
try {
date = LocalDate.parse(fromDC, dateIso).atStartOfDay(ZoneId.systemDefault());
} catch (DateTimeParseException e) {
date = null;
}
if (date != null) {
granularity = DateGran.DAY;
}
}
// Try to parse as just a month & year in UTC
if (date == null) {
date = tryParse(yearMonthIso, fromDC);
try {
date = YearMonth.parse(fromDC, yearMonthIso).atDay(1).atStartOfDay(ZoneId.systemDefault());
} catch (DateTimeParseException e) {
date = null;
}
if (date != null) {
granularity = DateGran.MONTH;
}
}
// Try to parse as just a year in UTC
if (date == null) {
date = tryParse(yearIso, fromDC);
try {
date = Year.parse(fromDC, yearIso).atMonth(1).atDay(1).atStartOfDay(ZoneId.systemDefault());
} catch (DateTimeParseException e) {
date = null;
}
if (date != null) {
granularity = DateGran.YEAR;
}
@@ -224,26 +247,25 @@ public class DCDate {
if (date == null) {
log.warn("Mangled date: " + fromDC + " ..failed all attempts to parse as date.");
} else {
// Set the UTC time.
calendar = date.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC);
// By default, we parse strings into UTC time. So the "date" object is already in UTC timezone
calendar = date;
// Now set the local equivalent.
// Now set the local equivalent based on system default timezone
if (granularity == DateGran.TIME) {
localCalendar = date;
localCalendar = date.withZoneSameInstant(ZoneId.systemDefault());
} else {
// No Time component so just set the local date to be the same as the UTC Year, Month, and Day.
localCalendar = LocalDateTime.of(calendar.getYear(),
calendar.getMonth(),
calendar.getDayOfMonth(), 0, 0);
localCalendar = ZonedDateTime.of(calendar.getYear(),
calendar.getMonth().getValue(),
calendar.getDayOfMonth(), 0, 0, 0, 0, ZoneOffset.UTC);
}
}
}
// Attempt to parse, swallowing errors; return null for failure.
private synchronized LocalDateTime tryParse(DateTimeFormatter formatter, String source) {
private synchronized ZonedDateTime tryParse(DateTimeFormatter formatter, String source) {
try {
return LocalDateTime.parse(source, formatter);
return ZonedDateTime.parse(source, formatter);
} catch (DateTimeParseException e) {
return null;
}
@@ -494,12 +516,12 @@ public class DCDate {
/************** Some utility methods ******************/
/**
* Get a date representing the current instant in time.
* Get a date representing the current instant in UTC time.
*
* @return a DSpaceDate object representing the current instant.
*/
public static DCDate getCurrent() {
return (new DCDate(LocalDateTime.now()));
return new DCDate(ZonedDateTime.now(ZoneOffset.UTC));
}
/**

View File

@@ -13,8 +13,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
@@ -425,7 +425,7 @@ public class PDFPackager
if (calValue != null) {
itemService.addMetadata(context, item, MetadataSchemaEnum.DC.getName(), "date", "created", null,
new DCDate(
LocalDateTime.ofInstant(calValue.toInstant(), ZoneOffset.UTC)
ZonedDateTime.ofInstant(calValue.toInstant(), ZoneOffset.UTC)
).toString());
}
itemService.update(context, item);

View File

@@ -9,8 +9,8 @@ package org.dspace.embargo;
import java.sql.SQLException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Properties;
import org.dspace.authorize.AuthorizeException;
@@ -65,7 +65,7 @@ public class DayTableEmbargoSetter extends DefaultEmbargoSetter {
if (days != null && days.length() > 0) {
long lift = Instant.now().toEpochMilli() +
(Long.parseLong(days) * 24 * 60 * 60 * 1000);
return new DCDate(LocalDateTime.ofEpochSecond(lift, 0, ZoneOffset.UTC));
return new DCDate(ZonedDateTime.ofInstant(Instant.ofEpochSecond(lift), ZoneOffset.UTC));
}
}
return null;

View File

@@ -9,6 +9,7 @@ package org.dspace.embargo;
import java.io.IOException;
import java.sql.SQLException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.List;
@@ -123,7 +124,7 @@ public class EmbargoCLITool {
try {
context = new Context(Context.Mode.BATCH_EDIT);
context.turnOffAuthorisationSystem();
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
// scan items under embargo
if (line.hasOption('i')) {

View File

@@ -1159,7 +1159,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
//We start with out current year
DCDate dcStart = new DCDate(count.getValue());
//Advance one year for the start of the next one !
DCDate dcEndDate = new DCDate(dcStart.toDate().plus(1, ChronoUnit.YEARS).toLocalDateTime());
DCDate dcEndDate = new DCDate(dcStart.toDate().plus(1, ChronoUnit.YEARS));
StringBuilder filterQuery = new StringBuilder();
filterQuery.append("time:([");

View File

@@ -13,9 +13,9 @@ import java.sql.SQLException;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -107,7 +107,7 @@ public class StatisticsDataWorkflow extends StatisticsData {
}
long monthDifference = 1;
if (getOldestWorkflowItemDate(facetMinCount) != null) {
monthDifference = getMonthsDifference(LocalDateTime.now(),
monthDifference = getMonthsDifference(ZonedDateTime.now(ZoneOffset.UTC),
getOldestWorkflowItemDate(facetMinCount));
}
@@ -160,7 +160,7 @@ public class StatisticsDataWorkflow extends StatisticsData {
return query;
}
private long getMonthsDifference(LocalDateTime date1, LocalDateTime date2) {
private long getMonthsDifference(ZonedDateTime date1, ZonedDateTime date2) {
LocalDate earlier = date1.toLocalDate();
LocalDate later = date2.toLocalDate();
return Period.between(earlier, later).toTotalMonths();
@@ -187,7 +187,7 @@ public class StatisticsDataWorkflow extends StatisticsData {
return result;
}
protected LocalDateTime getOldestWorkflowItemDate(int facetMinCount)
protected ZonedDateTime getOldestWorkflowItemDate(int facetMinCount)
throws SolrServerException, IOException {
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
String workflowStartDate = configurationService.getProperty("usage-statistics.workflow-start-date");
@@ -197,8 +197,8 @@ public class StatisticsDataWorkflow extends StatisticsData {
.query(getQuery(), null, null, 1, 0, null, null, null, null, "time", true, facetMinCount);
if (0 < oldestRecord.getResults().getNumFound()) {
SolrDocument solrDocument = oldestRecord.getResults().get(0);
LocalDateTime oldestDate = Instant.parse((String) solrDocument.getFieldValue("time"))
.atZone(ZoneOffset.UTC).toLocalDateTime();
ZonedDateTime oldestDate = Instant.parse((String) solrDocument.getFieldValue("time"))
.atZone(ZoneOffset.UTC);
//Store the date, we only need to retrieve this once !
try {
// Also store it in the solr-statics configuration file, the reason for this being that the sort
@@ -225,7 +225,7 @@ public class StatisticsDataWorkflow extends StatisticsData {
}
} else {
return new DCDate(workflowStartDate).toDate().toLocalDateTime();
return new DCDate(workflowStartDate).toDate();
}
}
}

View File

@@ -11,7 +11,8 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -156,7 +157,7 @@ public abstract class ExportEventProcessor {
* @return the current date as a string
*/
protected String getCurrentDateString() {
return new DCDate(LocalDateTime.now()).toString();
return new DCDate(ZonedDateTime.now(ZoneOffset.UTC)).toString();
}
/**

View File

@@ -9,6 +9,7 @@ package org.dspace.versioning;
import java.sql.SQLException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
@@ -75,7 +76,7 @@ public class VersioningServiceImpl implements VersioningService {
// get dc:date.accessioned to be set as first version date...
List<MetadataValue> values = itemService.getMetadata(item, "dc", "date", "accessioned", Item.ANY);
ZonedDateTime versionDate = ZonedDateTime.now();
ZonedDateTime versionDate = ZonedDateTime.now(ZoneOffset.UTC);
if (values != null && values.size() > 0) {
String date = values.get(0).getValue();
versionDate = new DCDate(date).toDate();
@@ -86,7 +87,7 @@ public class VersioningServiceImpl implements VersioningService {
Item itemNew = provider.createNewItemAndAddItInWorkspace(c, item);
// create new version
Version version = createVersion(c, vh, itemNew, summary, ZonedDateTime.now());
Version version = createVersion(c, vh, itemNew, summary, ZonedDateTime.now(ZoneOffset.UTC));
// Complete any update of the Item and new Identifier generation that needs to happen
provider.updateItemState(c, itemNew, item);

View File

@@ -13,7 +13,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Locale;
import java.util.TimeZone;
@@ -33,7 +35,7 @@ public class DCDateTest {
/**
* Object to use in the tests
*/
private LocalDateTime ldt;
private ZonedDateTime zdt;
/**
* This method will be run before every test as per @Before. It will
@@ -44,6 +46,8 @@ public class DCDateTest {
*/
@Before
public void init() {
// Set the default timezone for all tests to GMT-8
// This will also ensure ZoneId.systemDefault() returns GMT-8
TimeZone.setDefault(TimeZone.getTimeZone("GMT-8"));
}
@@ -57,7 +61,7 @@ public class DCDateTest {
@After
public void destroy() {
dc = null;
ldt = null;
zdt = null;
}
/**
@@ -80,8 +84,10 @@ public class DCDateTest {
assertThat("testDCDateDate 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateDate 12", dc.getSecondUTC(), equalTo(-1));
ldt = LocalDateTime.of(2010, 1, 1, 0, 0, 0);
dc = new DCDate(ldt);
// NOTE: In "init()" the default timezone is set to GMT-8 to ensure the getHour() and getHourUTC() tests
// below will result in an 8-hour time difference.
zdt = ZonedDateTime.of(2010, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
dc = new DCDate(zdt);
assertThat("testDCDateDate 1 ", dc.getYear(), equalTo(2010));
assertThat("testDCDateDate 2 ", dc.getMonth(), equalTo(1));
@@ -97,8 +103,10 @@ public class DCDateTest {
assertThat("testDCDateDate 11 ", dc.getMinuteUTC(), equalTo(0));
assertThat("testDCDateDate 12 ", dc.getSecondUTC(), equalTo(0));
ldt = LocalDateTime.of(2009, 12, 31, 18, 30, 0);
dc = new DCDate(ldt);
// NOTE: In "init()" the default timezone is set to GMT-8 to ensure the getHour() and getHourUTC() tests
// below will result in an 8-hour time difference.
zdt = ZonedDateTime.of(2009, 12, 31, 18, 30, 0, 0, ZoneId.systemDefault());
dc = new DCDate(zdt);
assertThat("testDCDateDate 13 ", dc.getYear(), equalTo(2009));
assertThat("testDCDateDate 14 ", dc.getMonth(), equalTo(12));
@@ -159,6 +167,7 @@ public class DCDateTest {
*/
@Test
public void testDCDateString() {
// Verify null returns empty date
dc = new DCDate((String) null);
assertThat("testDCDateString 1", dc.getYear(), equalTo(-1));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(-1));
@@ -174,6 +183,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(-1));
// Verify empty string returns empty date
dc = new DCDate("");
assertThat("testDCDateString 1", dc.getYear(), equalTo(-1));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(-1));
@@ -189,6 +199,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(-1));
// Verify only year is set when date is a year
dc = new DCDate("2010");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(-1));
@@ -204,6 +215,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(-1));
// Verify only month & year is set when date is a month.
dc = new DCDate("2010-04");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -219,6 +231,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(-1));
// Verify only month, day, and year is set when date is a day.
dc = new DCDate("2010-04-14");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -234,6 +247,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(-1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(-1));
// Verify hour is also set when date includes hour. Verify 8-hour difference with UTC
dc = new DCDate("2010-04-14T01");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -249,6 +263,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(0));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(0));
// Verify minute is also set when date includes minute. Verify 8-hour difference with UTC
dc = new DCDate("2010-04-14T00:01");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -264,6 +279,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(1));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(0));
// Verify full UTC time is parse correctly. Verify NO difference with UTC (as "Z" is a zero timezone)
dc = new DCDate("2010-04-14T00:00:01Z");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -279,7 +295,7 @@ public class DCDateTest {
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(0));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(1));
// test additional ISO format
// Verify millisecond can be parsed. Verify 8-hour difference with UTC
dc = new DCDate("2010-04-14T00:00:01.000");
assertThat("testDCDateString 1", dc.getYear(), equalTo(2010));
assertThat("testDCDateString 2", dc.getMonth(), equalTo(04));
@@ -294,7 +310,6 @@ public class DCDateTest {
assertThat("testDCDateString 10", dc.getHourUTC(), equalTo(0));
assertThat("testDCDateString 11", dc.getMinuteUTC(), equalTo(0));
assertThat("testDCDateString 12", dc.getSecondUTC(), equalTo(1));
}
@@ -335,24 +350,24 @@ public class DCDateTest {
*/
@Test
public void testToDate() {
dc = new DCDate((LocalDateTime) null);
dc = new DCDate((ZonedDateTime) null);
assertThat("testToDate 0", dc.toDate(), nullValue());
ldt = LocalDateTime.of(2010, 1, 1, 0, 0, 0);
dc = new DCDate(ldt);
assertThat("testToDate 1", dc.toDate().toLocalDateTime(), equalTo(ldt));
zdt = ZonedDateTime.of(2010, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC);
dc = new DCDate(zdt);
assertThat("testToDate 1", dc.toDate(), equalTo(zdt));
ldt = LocalDateTime.of(2010, 5, 1, 0, 0, 0);
dc = new DCDate(ldt);
assertThat("testToDate 2", dc.toDate().toLocalDateTime(), equalTo(ldt));
zdt = ZonedDateTime.of(2010, 5, 1, 0, 0, 0, 0, ZoneOffset.UTC);
dc = new DCDate(zdt);
assertThat("testToDate 2", dc.toDate(), equalTo(zdt));
ldt = LocalDateTime.of(2010, 5, 15, 0, 0, 0);
dc = new DCDate(ldt);
assertThat("testToDate 3", dc.toDate().toLocalDateTime(), equalTo(ldt));
zdt = ZonedDateTime.of(2010, 5, 15, 0, 0, 0, 0, ZoneOffset.UTC);
dc = new DCDate(zdt);
assertThat("testToDate 3", dc.toDate(), equalTo(zdt));
ldt = LocalDateTime.of(2010, 5, 15, 0, 0, 1);
dc = new DCDate(ldt);
assertThat("testToDate 4", dc.toDate().toLocalDateTime(), equalTo(ldt));
zdt = ZonedDateTime.of(2010, 5, 15, 0, 0, 1, 0, ZoneOffset.UTC);
dc = new DCDate(zdt);
assertThat("testToDate 4", dc.toDate(), equalTo(zdt));
}

View File

@@ -9,7 +9,8 @@ package org.dspace.sword;
import java.io.File;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.StringTokenizer;
import org.apache.logging.log4j.LogManager;
@@ -196,7 +197,7 @@ public class SWORDMETSIngester implements SWORDIngester {
try {
itemService.clearMetadata(context, item, dc.schema, dc.element,
dc.qualifier, Item.ANY);
DCDate date = new DCDate(LocalDateTime.now());
DCDate date = new DCDate(ZonedDateTime.now(ZoneOffset.UTC));
itemService.addMetadata(context, item, dc.schema, dc.element,
dc.qualifier, null, date.toString());
} catch (SQLException e) {

View File

@@ -8,7 +8,8 @@
package org.dspace.sword2;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.StringTokenizer;
@@ -128,7 +129,7 @@ public abstract class AbstractSwordContentIngester
try {
itemService.clearMetadata(context, item, info.schema, info.element,
info.qualifier, Item.ANY);
DCDate date = new DCDate(LocalDateTime.now());
DCDate date = new DCDate(ZonedDateTime.now(ZoneOffset.UTC));
itemService.addMetadata(context, item, info.schema, info.element,
info.qualifier, null, date.toString());
} catch (SQLException e) {

View File

@@ -8,7 +8,8 @@
package org.dspace.sword2;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
@@ -308,7 +309,7 @@ public class SimpleDCEntryIngester extends AbstractSimpleDC
try {
itemService.clearMetadata(context, item,
info.schema, info.element, info.qualifier, Item.ANY);
DCDate date = new DCDate(LocalDateTime.now());
DCDate date = new DCDate(ZonedDateTime.now(ZoneOffset.UTC));
itemService.addMetadata(context, item, info.schema,
info.element, info.qualifier, null, date.toString());
} catch (SQLException e) {