diff --git a/poi-ooxml/src/test/java/org/apache/poi/ss/tests/format/TestCellFormatPart.java b/poi-ooxml/src/test/java/org/apache/poi/ss/tests/format/TestCellFormatPart.java index 8514542080..e49021f578 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/ss/tests/format/TestCellFormatPart.java +++ b/poi-ooxml/src/test/java/org/apache/poi/ss/tests/format/TestCellFormatPart.java @@ -135,6 +135,17 @@ class TestCellFormatPart { } } + @Test + void testDateFormatNumbers() throws IOException { + TimeZone tz = LocaleUtil.getUserTimeZone(); + LocaleUtil.setUserTimeZone(TimeZone.getTimeZone("CET")); + try { + runFormatTests("DateFormatNumberTests.xlsx", Cell::getNumericCellValue); + } finally { + LocaleUtil.setUserTimeZone(tz); + } + } + @Test void testElapsedFormat() throws IOException { runFormatTests("ElapsedFormatTests.xlsx", Cell::getNumericCellValue); diff --git a/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java b/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java index 4a963d7d3d..08afdf4b33 100644 --- a/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java +++ b/poi/src/main/java/org/apache/poi/ss/format/CellDateFormatter.java @@ -39,9 +39,11 @@ public class CellDateFormatter extends CellFormatter { private final DateFormat dateFmt; private String sFmt; - private final Calendar EXCEL_EPOCH_CAL = + private static final Calendar EXCEL_EPOCH_CAL = LocaleUtil.getLocaleCalendar(1904, 0, 1); + private static final double NUM_MILLISECONDS_IN_DAY = 1000 * 60 * 60 * 24; + private static /* final */ CellDateFormatter SIMPLE_DATE; class DatePartHandler implements CellFormatPart.PartHandler { @@ -177,12 +179,15 @@ public class CellDateFormatter extends CellFormatter { value = 0.0; if (value instanceof Number) { Number num = (Number) value; - long v = num.longValue(); + // Convert from fractional days to milliseconds. Excel always rounds up. + double v = Math.round(num.doubleValue() * NUM_MILLISECONDS_IN_DAY); if (v == 0L) { value = EXCEL_EPOCH_CAL.getTime(); } else { Calendar c = (Calendar)EXCEL_EPOCH_CAL.clone(); - c.add(Calendar.SECOND, (int)(v / 1000)); + // If milliseconds were not requested in the format string, round the seconds. + int seconds = (int) (sFmt == null ? Math.round(v / 1000) : v / 1000); + c.add(Calendar.SECOND, seconds); c.add(Calendar.MILLISECOND, (int)(v % 1000)); value = c.getTime(); } @@ -201,6 +206,9 @@ public class CellDateFormatter extends CellFormatter { int pos = toAppendTo.length(); try (Formatter formatter = new Formatter(toAppendTo, Locale.ROOT)) { long msecs = dateObj.getTime() % 1000; + if (msecs < 0) { + msecs += 1000; + } formatter.format(locale, sFmt, msecs / 1000.0); } toAppendTo.delete(pos, pos + 2); diff --git a/test-data/spreadsheet/DateFormatNumberTests.xlsx b/test-data/spreadsheet/DateFormatNumberTests.xlsx new file mode 100644 index 0000000000..f585d9fde8 Binary files /dev/null and b/test-data/spreadsheet/DateFormatNumberTests.xlsx differ