diff --git a/main/src/com/google/refine/model/Cell.java b/main/src/com/google/refine/model/Cell.java index 59d91ec77..17a2f9ef1 100644 --- a/main/src/com/google/refine/model/Cell.java +++ b/main/src/com/google/refine/model/Cell.java @@ -35,8 +35,12 @@ package com.google.refine.model; import java.io.Serializable; import java.io.Writer; +import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Calendar; +import java.util.Date; import java.util.Properties; import org.json.JSONException; @@ -87,11 +91,19 @@ public class Cell implements HasFields, Jsonizable { } else { writer.key("v"); if (value != null) { - if (value instanceof LocalDateTime) { - writer.value(ParsingUtilities.localDateToString((LocalDateTime)value)); - writer.key("t"); writer.value("date"); + Instant instant = null; + if (value instanceof Calendar) { + instant = ((Calendar)value).toInstant(); + } else if (value instanceof Date) { + instant = ((Date)value).toInstant(); } else if (value instanceof OffsetDateTime) { - writer.value(ParsingUtilities.dateToString((OffsetDateTime) value)); + instant = ((OffsetDateTime)value).toInstant(); + } else if (value instanceof LocalDateTime) { + instant = ((LocalDateTime)value).toInstant(ZoneOffset.of("Z")); + } + + if (instant != null) { + writer.value(ParsingUtilities.instantToString(instant)); writer.key("t"); writer.value("date"); } else if (value instanceof Double && (((Double)value).isNaN() || ((Double)value).isInfinite())) { diff --git a/main/src/com/google/refine/model/medadata/ProjectMetadata.java b/main/src/com/google/refine/model/medadata/ProjectMetadata.java index eda957211..962a9832e 100644 --- a/main/src/com/google/refine/model/medadata/ProjectMetadata.java +++ b/main/src/com/google/refine/model/medadata/ProjectMetadata.java @@ -292,11 +292,8 @@ public class ProjectMetadata extends AbstractMetadata { private void extractModifiedLocalTime(JSONObject obj) { String modified = JSONUtilities.getString(obj, "modified", LocalDateTime.now().toString()); - if (modified.endsWith("Z")) { - this._modified = ParsingUtilities.stringToDate(modified).toLocalDateTime(); - } else { - this._modified = ParsingUtilities.stringToLocalDate(modified); - } + + this._modified = ParsingUtilities.stringToLocalDate(modified); } static protected void preparePreferenceStore(PreferenceStore ps) { diff --git a/main/src/com/google/refine/util/ParsingUtilities.java b/main/src/com/google/refine/util/ParsingUtilities.java index 1cca2b98b..b2f107a21 100644 --- a/main/src/com/google/refine/util/ParsingUtilities.java +++ b/main/src/com/google/refine/util/ParsingUtilities.java @@ -42,6 +42,7 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Properties; @@ -55,6 +56,8 @@ import org.json.JSONObject; import org.json.JSONTokener; public class ParsingUtilities { + public static final DateTimeFormatter ISO8601 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + static public Properties parseUrlParameters(HttpServletRequest request) { Properties options = new Properties(); @@ -166,28 +169,39 @@ public class ParsingUtilities { * @return string with ISO_LOCAL_DATE_TIME formatted date & time */ static public String dateToString(OffsetDateTime d) { - return d.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + return d.format(ISO8601); } static public String localDateToString(LocalDateTime d) { - return d.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + OffsetDateTime odt = OffsetDateTime.of(d, + OffsetDateTime.now().getOffset()); + + return odt.withOffsetSameInstant(ZoneOffset.of("Z")).format(ISO8601); } /** * Parse an ISO_LOCAL_DATE_TIME formatted string into a Java Date. - * + * For backward compatibility, to support the version <= 2.8, cannot use the DateTimeFormatter.ISO_OFFSET_DATE_TIME. Instead, use the ISO8601 below format: + * yyyy-MM-dd'T'HH:mm:ss'Z' + * * @param s the string to be parsed * @return LocalDateTime or null if the parse failed */ static public OffsetDateTime stringToDate(String s) { - return OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + Instant instant = Instant.parse(s); + return OffsetDateTime.ofInstant(instant, ZoneId.of("Z")); } static public LocalDateTime stringToLocalDate(String s) { - if (s.endsWith("Z")) { // UTC time - Instant instant = Instant.parse(s); - return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - } else - return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + Instant instant = Instant.parse(s); + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + static public String instantToString(Instant instant) { + return OffsetDateTime.ofInstant(instant, ZoneId.of("Z")).format(ISO8601); + } + + static public String instantToLocalDateTimeString(Instant instant) { + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).format(ISO8601); } } diff --git a/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java b/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java index 9fcf29fec..2fb77fbbb 100644 --- a/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/exporters/CsvExporterTests.java @@ -40,9 +40,8 @@ import static org.mockito.Mockito.when; import java.io.IOException; import java.io.StringWriter; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.OffsetDateTime; +import java.util.Calendar; +import java.util.Date; import java.util.Properties; import org.apache.commons.lang3.StringUtils; @@ -200,13 +199,13 @@ public class CsvExporterTests extends RefineTest { } @Test - public void exportDateColumns(){ + public void exportDateColumnsPreVersion28(){ CreateGrid(1,2); - LocalDateTime localDate = LocalDateTime.now(); - OffsetDateTime date = OffsetDateTime.now(ZoneId.of("Z")); + Calendar calendar = Calendar.getInstance(); + Date date = new Date(); when(options.getProperty("printColumnHeader")).thenReturn("false"); - project.rows.get(0).cells.set(0, new Cell(localDate, null)); + project.rows.get(0).cells.set(0, new Cell(calendar, null)); project.rows.get(0).cells.set(1, new Cell(date, null)); try { @@ -215,12 +214,11 @@ public class CsvExporterTests extends RefineTest { Assert.fail(); } - String expectedOutput = alignFractionalDigits(ParsingUtilities.localDateToString(localDate)) + "," + - alignFractionalDigits(ParsingUtilities.dateToString(date)) + "\n"; + String expectedOutput = ParsingUtilities.instantToLocalDateTimeString(calendar.toInstant()) + "," + + ParsingUtilities.instantToLocalDateTimeString(date.toInstant()) + "\n"; Assert.assertEquals(writer.toString(), expectedOutput); } - //helper methods protected void CreateColumns(int noOfColumns){ diff --git a/main/tests/server/src/com/google/refine/tests/expr/ExpressionUtilsTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/ExpressionUtilsTests.java similarity index 100% rename from main/tests/server/src/com/google/refine/tests/expr/ExpressionUtilsTests.java rename to main/tests/server/src/com/google/refine/tests/expr/functions/ExpressionUtilsTests.java