diff --git a/main/src/com/google/refine/importers/OdsImporter.java b/main/src/com/google/refine/importers/OdsImporter.java index ca68d6e1a..41fa4c55d 100644 --- a/main/src/com/google/refine/importers/OdsImporter.java +++ b/main/src/com/google/refine/importers/OdsImporter.java @@ -40,9 +40,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TimeZone; import org.odftoolkit.odfdom.doc.OdfDocument; import org.odftoolkit.odfdom.doc.table.OdfTable; diff --git a/main/src/com/google/refine/util/ParsingUtilities.java b/main/src/com/google/refine/util/ParsingUtilities.java index 5399822bd..d5844c16c 100644 --- a/main/src/com/google/refine/util/ParsingUtilities.java +++ b/main/src/com/google/refine/util/ParsingUtilities.java @@ -99,6 +99,7 @@ public class ParsingUtilities { public static final ObjectWriter defaultWriter = mapper.writerWithView(JsonViews.NonSaveMode.class).with(defaultFilters); public static final DateTimeFormatter ISO8601 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); + private static final ZoneOffset defaultTimeOffset = OffsetDateTime.now().getOffset(); static public Properties parseUrlParameters(HttpServletRequest request) { Properties options = new Properties(); @@ -245,12 +246,28 @@ public class ParsingUtilities { return o instanceof OffsetDateTime; } + /** + * Converts an old-style Java Date to an OffsetDateTime, + * assuming the date is represented in the current default system timezone + * (which is what you get if the date was parsed using `Calendar.getDefault()`). + * + * @param date + * @return + */ public static OffsetDateTime toDate(Date date) { - return date.toInstant().atOffset(ZoneOffset.UTC); + return date.toInstant().atOffset(defaultTimeOffset); } + /** + * Converts an old-style Java Calendar to an OffsetDateTime, + * assuming the date is represented in the current default system timezone + * (which is what you get if the date was parsed using `Calendar.getDefault()`). + * + * @param date + * @return + */ public static OffsetDateTime toDate(Calendar date) { - return date.toInstant().atOffset(ZoneOffset.UTC); + return date.toInstant().atOffset(defaultTimeOffset); } public static ObjectNode evaluateJsonStringToObjectNode(String optionsString) { diff --git a/main/tests/data/dates.xls b/main/tests/data/dates.xls new file mode 100644 index 000000000..e8f129b8a Binary files /dev/null and b/main/tests/data/dates.xls differ diff --git a/main/tests/server/src/com/google/refine/importers/ExcelImporterTests.java b/main/tests/server/src/com/google/refine/importers/ExcelImporterTests.java index 3388751fa..705b20c8c 100644 --- a/main/tests/server/src/com/google/refine/importers/ExcelImporterTests.java +++ b/main/tests/server/src/com/google/refine/importers/ExcelImporterTests.java @@ -45,6 +45,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.time.Month; +import java.time.OffsetDateTime; import java.util.Calendar; import java.util.Date; @@ -199,6 +201,39 @@ public class ExcelImporterTests extends ImporterTest { Assert.fail(e.getMessage()); } } + + @Test + public void readExcelDates() throws FileNotFoundException, IOException { + ArrayNode sheets = ParsingUtilities.mapper.createArrayNode(); + sheets.add(ParsingUtilities.mapper.readTree("{name: \"file-source#Test Sheet 0\", fileNameAndSheetIndex: \"file-source#0\", rows: 31, selected: true}")); + whenGetArrayOption("sheets", options, sheets); + + whenGetIntegerOption("ignoreLines", options, 0); + whenGetIntegerOption("headerLines", options, 0); + whenGetIntegerOption("skipDataLines", options, 0); + whenGetIntegerOption("limit", options, -1); + whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + + InputStream stream = ClassLoader.getSystemResourceAsStream("dates.xls"); + + parseOneFile(SUT, stream); + + // The original value reads 2021-04-18 in the Excel file. + // We make sure it is not shifted by a day because of timezone handling + Object cellValue = project.rows.get(0).getCellValue(0); + Assert.assertTrue(cellValue instanceof OffsetDateTime); + OffsetDateTime date = (OffsetDateTime) cellValue; + Assert.assertEquals(date.getYear(), 2021); + Assert.assertEquals(date.getMonth(), Month.APRIL); + Assert.assertEquals(date.getDayOfMonth(), 18); + // Same, with January 1st (in winter / no DST) + Object cellValue2 = project.rows.get(1).getCellValue(0); + Assert.assertTrue(cellValue instanceof OffsetDateTime); + OffsetDateTime date2 = (OffsetDateTime) cellValue2; + Assert.assertEquals(date2.getYear(), 2021); + Assert.assertEquals(date2.getMonth(), Month.JANUARY); + Assert.assertEquals(date2.getDayOfMonth(), 1); + } @Test public void readMultiSheetXls() throws FileNotFoundException, IOException{