Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timezone support #5061

Merged
merged 40 commits into from
Dec 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f814800
Remove Joda time library
juanluisrp Sep 30, 2020
63b3dcc
Remove Joda time and legacy java.util.Date and Calendar uses
juanluisrp Oct 6, 2020
c4bf35d
Fix failing tests
juanluisrp Oct 7, 2020
f822500
Clean old commented code
juanluisrp Oct 7, 2020
e308825
Refactor formatter as constant field
juanluisrp Oct 8, 2020
f8037a1
Test / Fix
fxprunayre Oct 19, 2020
90289f5
Date / UI / Add timezone configuration.
fxprunayre Oct 19, 2020
12985e5
Date / UI / Editor / Add timezone basic support.
fxprunayre Oct 19, 2020
fd4c562
Test / XSL in core tests had dependencies on services.
fxprunayre Oct 20, 2020
1299a9e
Test / Tentative fix
fxprunayre Oct 20, 2020
3c822a1
Popularity / Use updater instead of which does not need flush. Fix i…
fxprunayre Oct 20, 2020
8cc040b
Use UTC when possible
juanluisrp Nov 23, 2020
23286d8
Remove Joda time references
juanluisrp Nov 23, 2020
9323854
Fix parsing string case with one or two milliseconds digits
juanluisrp Nov 23, 2020
f6a6042
Add missing code moved removed from MetadataUtils
juanluisrp Nov 30, 2020
e32722a
Fix gnDatePicker time check to select the timezone
josegar74 Nov 30, 2020
acc856a
Fix Link Status date time format
juanluisrp Dec 1, 2020
1320f3f
Fix exception in harvesters when calculating current date
juanluisrp Dec 1, 2020
04be87b
Interpret dates in format yyyy-mm-dd as local datetime
juanluisrp Dec 1, 2020
8ca90c3
Fix test for time and offset without date
juanluisrp Dec 1, 2020
90265aa
Use UTC to store date/times in the database
juanluisrp Dec 2, 2020
ea46890
Use the instant in equals method
juanluisrp Dec 2, 2020
92afa21
Use UTC in MetadataStatus' changedate column
juanluisrp Dec 2, 2020
6e5ec9c
Set the default timezone
juanluisrp Dec 3, 2020
37c5a64
Refactor ISODate and create a new DateUtils class
juanluisrp Dec 4, 2020
2f2d1f6
Trim string to parse
juanluisrp Dec 9, 2020
82682a9
Use Zulu time to index metadata
juanluisrp Dec 9, 2020
923a4b1
Timezone support / SQL / Add migration.
fxprunayre Dec 14, 2020
f7df7fe
Migrate existing date time columns to UTC timezone
juanluisrp Dec 16, 2020
ef21d19
Fix Source uuid type mapping
juanluisrp Dec 16, 2020
4d76a8c
Print the date in new metadata using server timezone setting
juanluisrp Dec 17, 2020
c39b7ca
Migration / In case we can't parse existing dates, assign now.
fxprunayre Dec 17, 2020
477023c
Migration / Keep this as an example. But timezone migration is handle…
fxprunayre Dec 17, 2020
a927d42
Timezone config and translations.
fxprunayre Dec 17, 2020
d057788
Workflow / Add missing field required for search results.
fxprunayre Dec 17, 2020
1931ac0
CSW / GetDomain operation not implemented in ES.
fxprunayre Dec 17, 2020
eeb10a5
History / Do not fail on unknown user (which should not happen).
fxprunayre Dec 17, 2020
6933735
Serialize dateAndTime property to JSON
juanluisrp Dec 17, 2020
8c4167d
Merge branch '4.0.x' into remove-joda-time
fxprunayre Dec 17, 2020
729afd2
Fix bad merge.
fxprunayre Dec 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
344 changes: 344 additions & 0 deletions common/src/main/java/org/fao/geonet/utils/DateUtil.java

Large diffs are not rendered by default.

135 changes: 135 additions & 0 deletions common/src/test/java/org/fao/geonet/utils/DateUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.fao.geonet.utils;

import org.junit.Test;

import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

import static org.junit.Assert.*;

public class DateUtilTest {

@Test
public void convertToISOZuluDateTime() {
// Format: yyyy-mm-ddThh.mm:ss[+hh:mm|=+hh:mm] Time zone
String datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2010-10-10T00:00:00+02:00");
assertEquals("2010-10-09T22:00:00.000Z", datetimeInIsoFormat);

// Format: yyyy-mm-ddThh.mm:ss.ms[+hh:mm|=+hh:mm] Time zone
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2010-10-10T00:00:00.000+02:00");
assertEquals("2010-10-09T22:00:00.000Z", datetimeInIsoFormat);

// Format: yyyy-mm-ddThh.mm:ssZ (UTC)
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2010-10-10T00:00:00Z");
assertEquals("2010-10-10T00:00:00.000Z", datetimeInIsoFormat);

// Format: yyyy-mm-ddThh.mm:ss.msZ (UTC)
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2010-10-10T00:00:00.000Z");
assertEquals("2010-10-10T00:00:00.000Z", datetimeInIsoFormat);

// Format: yyyy-mm-ddThh.mm:ss.msZ (UTC) (ms with less than 3 digits)
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2020-11-23T07:41:29.1Z");
assertEquals("2020-11-23T07:41:29.100Z", datetimeInIsoFormat);
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2020-11-23T07:41:29.12Z");
assertEquals("2020-11-23T07:41:29.120Z", datetimeInIsoFormat);
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2020-11-23T07:41:29.123Z");
assertEquals("2020-11-23T07:41:29.123Z", datetimeInIsoFormat);

datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2020-11-23");
LocalDate ld = LocalDate.parse("2020-11-23");
ZonedDateTime zdt = ld.atStartOfDay(ZoneId.systemDefault());
zdt = zdt.withZoneSameInstant(ZoneOffset.UTC);
assertEquals(DateUtil.ISO_OFFSET_DATE_TIME_NANOSECONDS.format(zdt), datetimeInIsoFormat);

datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime("2020-11");
ld = LocalDate.of(2020, 11, 1);
zdt = ld.atStartOfDay(ZoneId.systemDefault());
zdt = zdt.withZoneSameInstant(ZoneOffset.UTC);
assertEquals("Testing 2020-11", DateUtil.ISO_OFFSET_DATE_TIME_NANOSECONDS.format(zdt), datetimeInIsoFormat);



}

@Test
public void testParseYearMonthDateTime() throws Exception {
// xs:gYearMonth
for (int i = 0; i < 10; i++) {
String year = "20" + getRandom(1, 0) + getRandom(9, 0);
String month = "0" + getRandom(9, 1);
String hour = getRandom(1, 0) + "" + getRandom(9, 0);
String minutes = getRandom(5, 1) + "" + getRandom(9, 0);

// Format: yyyy-MM-hh:mm Time zone
String tmp = year + "-" + month + "-" + hour + ":" + minutes + "Z";
String dateTimeInIsoFormat = DateUtil.convertToISOZuluDateTime(tmp);
assertEquals("Testing " + tmp, dateTimeInIsoFormat, year + "-" + month + "-" + "01T" + hour + ":" + minutes + ":00.000Z");

tmp = year + "-" + month + "-" + hour + ":" + minutes;
dateTimeInIsoFormat = DateUtil.convertToISOZuluDateTime(tmp);
String expected = ZonedDateTime.of(Integer.parseInt(year), Integer.parseInt(month), 1, Integer.parseInt(hour), Integer.parseInt(minutes), 0, 0, ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).format(DateUtil.ISO_OFFSET_DATE_TIME_NANOSECONDS);
assertEquals("Testing " + tmp, expected, dateTimeInIsoFormat);

year = "20" + getRandom(1, 0) + getRandom(9, 0);
month = "0" + getRandom(9, 1);
// Format: yyyy-MM
tmp = year + "-" + month;
expected = ZonedDateTime.of(Integer.parseInt(year), Integer.parseInt(month), 1, 0, 0, 0, 0, ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).format(DateUtil.ISO_OFFSET_DATE_TIME_NANOSECONDS);
dateTimeInIsoFormat = DateUtil.convertToISOZuluDateTime(tmp);
assertEquals("Testing " + tmp, expected , dateTimeInIsoFormat);

}
}

@Test
public void testParseYearDateTime() throws Exception {
// xs:gYear
for (int i = 0; i < 10; i++) {
String year = "20" + getRandom(1, 0) + getRandom(9, 0);
String hour = getRandom(1, 0) + "" + getRandom(9, 0);
String minutes = getRandom(5, 1) + "" + getRandom(9, 0);

// Format: yyyy-hh:mm Time zone
String tmp = year + "-" + hour + ":" + minutes + "Z";
String datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime(tmp);
assertEquals("Testing " + tmp, year + "-01-01T" + hour + ":" + minutes + ":00.000Z", datetimeInIsoFormat);

year = "20" + getRandom(1, 0) + getRandom(9, 0);

// Format: yyyy
datetimeInIsoFormat = DateUtil.convertToISOZuluDateTime(year);
String expected = ZonedDateTime.of(Integer.parseInt(year), 1, 1 ,0, 0 , 0, 0, ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).format(DateUtil.ISO_OFFSET_DATE_TIME_NANOSECONDS);
assertEquals("Testing " + year, expected, datetimeInIsoFormat);
}
}

@Test
public void parseISODateTimes() {
}

@Test
public void parseBasicOrFullDateTime() {
}

@Test
public void parseTime() {
}

@Test
public void generateDate() {
}

@Test
public void testGenerateDate() {
}

private String getRandom(int max, int min) {
return Integer.toString(min + (int) (Math.random() * ((max - min) + 1)));
}
}
5 changes: 3 additions & 2 deletions core/src/main/java/org/fao/geonet/kernel/Thesaurus.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.fao.geonet.kernel.search.keyword.KeywordRelation;
import org.fao.geonet.languages.IsoLanguagesMapper;
import org.fao.geonet.util.LangUtils;
import org.fao.geonet.utils.DateUtil;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
import org.jdom.Element;
Expand Down Expand Up @@ -850,8 +851,8 @@ private void retrieveThesaurusTitle(Path thesaurusFile, String defaultTitle, boo
FileTime lastModifiedTime = null;
if (this.date != null) {
try {
lastModifiedTime = FileTime.fromMillis(ISODate.parseBasicOrFullDateTime(this.date).toDate().getTime());
} catch (IOException e) {
lastModifiedTime = FileTime.fromMillis(DateUtil.parseBasicOrFullDateTime(this.date).toInstant().toEpochMilli());
} catch (Exception e) {
Log.warning(Geonet.THESAURUS, "Unable to parse " + this.date + " into an actual java.util.Date object", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
import javax.transaction.Transactional;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -472,15 +473,14 @@ private void setMetadataTitle(String schema, Element xml, String language, boole
List<?> titleNodes = null;
try {
titleNodes = Xml.selectNodes(xml, path, new ArrayList<Namespace>(schemaPlugin.getNamespaces()));
// Use ISO 8601 GMT
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));

for (Object o : titleNodes) {
if (o instanceof Element) {
Element title = (Element) o;
// Use settings defined timezone to format the date/time
title.setText(String
.format(messages.getString("metadata.title.createdFrom" + (fromTemplate ? "Template" : "Record")),
title.getTextTrim(), simpleDateFormat.format(new Date())));
title.getTextTrim(), ZonedDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME)));
}
}
} catch (JDOMException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,7 @@ public boolean canEditorEdit(Integer metadataId) throws Exception {
draftCompatible.add(StatusValue.Status.APPROVED);
draftCompatible.add(StatusValue.Status.RETIRED);

if (draftCompatible.contains(currentState)) {
return true;
}

return false;
return draftCompatible.contains(currentState);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,14 @@ public void increasePopularity(ServiceContext srvContext, String id) throws Exce
// READONLYMODE
if (!srvContext.getBean(NodeInfo.class).isReadOnly()) {
int iId = Integer.parseInt(id);
metadataRepository.incrementPopularity(iId);
metadataRepository.update(iId, new Updater<Metadata>() {
@Override
public void apply(Metadata entity) {
entity.getDataInfo().setPopularity(
entity.getDataInfo().getPopularity() + 1
);
}
});
final java.util.Optional<Metadata> metadata = metadataRepository.findById(iId);

if (metadata.isPresent()) {
Expand All @@ -513,9 +520,6 @@ public void increasePopularity(ServiceContext srvContext, String id) throws Exce
metadata.get().getDataInfo().getPopularity());

}
// // And register the metadata to be indexed in the near future
// final IndexingList list = srvContext.getBean(IndexingList.class);
// list.add(iId);
} else {
if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) {
Log.debug(Geonet.DATA_MANAGER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,14 @@ public void onFailure(Exception e) {

public UpdateResponse updateField(String id, String field, Object value) throws Exception {
Map<String, Object> updates = new HashMap<>(2);
updates.put(field, value);
updates.put(getPropertyName(field), value);
updates.put("indexingDate", new Date());
return updateFields(id, updates);
}

public void updateFieldAsynch(String id, String field, Object value) throws Exception {
Map<String, Object> updates = new HashMap<>(2);
updates.put(field, value);
updates.put(getPropertyName(field), value);
updates.put("indexingDate", new Date());
updateFieldsAsynch(id, updates);
}
Expand Down Expand Up @@ -578,9 +578,7 @@ public ObjectNode documentToJson(Element xml) {
// Register list of already processed names
elementNames.add(name);

// Field starting with _ not supported in Kibana
// Those are usually GN internal fields
String propertyName = name.startsWith("_") ? name.substring(1) : name;
String propertyName = getPropertyName(name);
List<Element> nodeElements = xml.getChildren(name);

boolean isArray = nodeElements.size() > 1
Expand Down Expand Up @@ -643,6 +641,14 @@ public ObjectNode documentToJson(Element xml) {
return doc;
}


/** Field starting with _ not supported in Kibana
* Those are usually GN internal fields
*/
private String getPropertyName(String name) {
return name.startsWith("_") ? name.substring(1) : name;
}

/*
* Normalize various GN boolean value to only true/false allowed in boolean fields in ES
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//=============================================================================
//=== Copyright (C) 2001-2013 Food and Agriculture Organization of the
//=== Copyright (C) 2001-2020 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
Expand All @@ -23,6 +23,8 @@

package org.fao.geonet.kernel.setting;

import jeeves.server.context.ServiceContext;
import jeeves.server.sources.http.ServletPathFinder;
import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.NodeInfo;
Expand All @@ -31,7 +33,6 @@
import org.fao.geonet.domain.Setting;
import org.fao.geonet.domain.SettingDataType;
import org.fao.geonet.domain.Setting_;
import org.fao.geonet.domain.Source;
import org.fao.geonet.repository.LanguageRepository;
import org.fao.geonet.repository.SettingRepository;
import org.fao.geonet.repository.SortUtils;
Expand All @@ -40,17 +41,20 @@
import org.jdom.Element;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.SQLException;
import java.util.*;

import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.ServletContext;

import jeeves.server.context.ServiceContext;
import jeeves.server.sources.http.ServletPathFinder;
import java.sql.SQLException;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.TimeZone;

import static com.google.common.xml.XmlEscapers.xmlContentEscaper;
import static org.fao.geonet.kernel.setting.Settings.SYSTEM_SITE_NAME_PATH;
Expand All @@ -62,6 +66,13 @@
*/
public class SettingManager {

/**
* This value stores the original server timezone id had when it was started and can be used to
* recover the server timezone in case the value of the setting {@link Settings#SYSTEM_SERVER_TIMEZONE}
* is set to empty or {@code null}.
*/
public static final ZoneId DEFAULT_SERVER_TIMEZONE = ZoneId.systemDefault();

@PersistenceContext
private EntityManager _entityManager;

Expand All @@ -79,6 +90,10 @@ public class SettingManager {
@PostConstruct
private void init() {
this.pathFinder = new ServletPathFinder(servletContext);
// Set the default timezone
String zoneId = StringUtils.defaultIfBlank(getValue(Settings.SYSTEM_SERVER_TIMEZONE), DEFAULT_SERVER_TIMEZONE.getId());
TimeZone tzFromSettings = TimeZone.getTimeZone(zoneId);
TimeZone.setDefault(tzFromSettings);
}

public List<Setting> getAll() {
Expand Down
Loading