Java Apache POI无法正确读取Excel单元格中的秒数

Java Apache POI无法正确读取Excel单元格中的秒数,java,excel,apache-poi,localdate,Java,Excel,Apache Poi,Localdate,我目前正在调试其他人的代码库。目的是将数据从Excel文件导入数据库。excel文件中的每一行在第0列中包含一个时间戳,在后续列中包含一些标签值 时间戳包含年、月、月的日、小时、分钟和秒。要解析excel文件并读取单个单元格,请使用以下API和代码: import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.apache.poi.ss.user

我目前正在调试其他人的代码库。目的是将数据从Excel文件导入数据库。excel文件中的每一行在第0列中包含一个时间戳,在后续列中包含一些标签值

时间戳包含年、月、月的日、小时、分钟和秒。要解析excel文件并读取单个单元格,请使用以下API和代码:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;

...

// some loop

   LocalDateTime dateCellValue = cell.getLocalDateTimeCellValue();

对日期的解析在大多数情况下都能正常工作,但我发现,这并不适用于所有情况。问题是,Java有时会将时间戳的秒数延长纳秒。例如,时间戳12.09.2018 12:39:11被Java解释为2018-09-12T12:39:10.995。这个问题被直接转化为db:文件中的第二个11在db中被保存为第二个10,因此纳秒伪影消失了

为了了解问题的原因,我阅读了以下文档:

由此我得出结论,问题的原因是舍入错误:API将每个日期值解释为双精度。在Excel中,您可以通过将日期时间值转换为文本或数字来查看此双精度值;e、 g.20.03.2019 08:36:39变为4354435387847222。因此,由于某些值的数据类型,舍入是不准确的,因此Java无法正确解析该值

我的问题是,如何快速准确地解决这样的问题。基本上,我考虑了两种可能性:

我可以通过DateFormatter来解析日期,而不用ApachePOI。然而,这有一个缺点,即用户定义的日期总是需要具有相同的格式,而它们目前并非如此。 在代码中,我可以尝试四舍五入以更正秒值并剪切纳秒伪影。但是,我不确定是否总是需要舍入到上面所示示例中的下一秒值,或者是否存在需要舍入到下面的下一秒值的情况,例如纳秒值低于0.5。我这里的问题是,我不完全理解程序行为背后关于舍入误差的确切系统。 有人有什么建议吗?非常感谢您的帮助

编辑:
问题的原因是,纳秒已经出现在Excel表格中,但肉眼无法识别,因为相关的数据类型没有显示它们。

我无法重现该问题。如果apache poi获取LocalDateTime 2018-09-12T12:39:10.995,则Excel单元格已存储该确切日期时间。当然,Excel可能不会完全显示它,因为日期格式已经循环。例如,2018-09-12T12:39:10.995的日期格式DD.MM.YYYY hh:MM:ss将显示2018年9月12日12:39:11。但存储的是确切的日期和时间

但是,如果需要以秒的精度获取LocalDateTime,则可以添加0.5秒500千分之一秒,然后将其截断为秒。这种方法将LocalDateTime四舍五入到秒

LocalDateTime dateCellValue = cell.getLocalDateTimeCellValue(); //got directly from Excel
dateCellValue  = dateCellValue.plusNanos(500000000).truncatedTo(ChronoUnit.SECONDS); //round to seconds
完整示例:

Excel工作表如下所示:

此处B列中的单元格值是日期时间值。单元编号格式为TT.MM.YYYY hh:MM:ss.000

代码:

结果:

Text
DateTime
DT 1
2018-09-12T12:39:10
2018-09-12T12:39:10
DT 2
2018-09-12T12:39:10.123
2018-09-12T12:39:10
DT 3
2018-09-12T12:39:10.245
2018-09-12T12:39:10
DT 4
2018-09-12T12:39:10.370
2018-09-12T12:39:10
DT 5
2018-09-12T12:39:10.495
2018-09-12T12:39:10
DT 6
2018-09-12T12:39:10.500
2018-09-12T12:39:11
DT 7
2018-09-12T12:39:10.620
2018-09-12T12:39:11
DT 8
2018-09-12T12:39:10.745
2018-09-12T12:39:11
DT 9
2018-09-12T12:39:10.870
2018-09-12T12:39:11
DT 10
2018-09-12T12:39:10.995
2018-09-12T12:39:11

我无法复制这个问题。如果apache poi获取LocalDateTime 2018-09-12T12:39:10.995,则Excel单元格已存储该确切日期时间。当然,Excel可能不会完全显示它,因为日期格式已经循环。例如,2018-09-12T12:39:10.995的日期格式DD.MM.YYYY hh:MM:ss将显示2018年9月12日12:39:11。但存储的是确切的日期和时间

但是,如果需要以秒的精度获取LocalDateTime,则可以添加0.5秒500千分之一秒,然后将其截断为秒。这种方法将LocalDateTime四舍五入到秒

LocalDateTime dateCellValue = cell.getLocalDateTimeCellValue(); //got directly from Excel
dateCellValue  = dateCellValue.plusNanos(500000000).truncatedTo(ChronoUnit.SECONDS); //round to seconds
完整示例:

Excel工作表如下所示:

此处B列中的单元格值是日期时间值。单元编号格式为TT.MM.YYYY hh:MM:ss.000

代码:

结果:

Text
DateTime
DT 1
2018-09-12T12:39:10
2018-09-12T12:39:10
DT 2
2018-09-12T12:39:10.123
2018-09-12T12:39:10
DT 3
2018-09-12T12:39:10.245
2018-09-12T12:39:10
DT 4
2018-09-12T12:39:10.370
2018-09-12T12:39:10
DT 5
2018-09-12T12:39:10.495
2018-09-12T12:39:10
DT 6
2018-09-12T12:39:10.500
2018-09-12T12:39:11
DT 7
2018-09-12T12:39:10.620
2018-09-12T12:39:11
DT 8
2018-09-12T12:39:10.745
2018-09-12T12:39:11
DT 9
2018-09-12T12:39:10.870
2018-09-12T12:39:11
DT 10
2018-09-12T12:39:10.995
2018-09-12T12:39:11

谢谢你,阿克塞尔!这似乎是我正在寻找的解决方案。我将测试我的数据。有趣的是,你不能复制它。我会在这方面付出更多的努力,而且效果会很好!再次感谢!示例中的Excel单元格属于哪种数据类型?在我的例子中,它们是用户定义的类型。B列中的单元格值是日期时间值。单元格编号格式为TT.MM.YYYY hh:MM:ss.000.Ok,找到原因:问题的原因是Excel工作表中的时间戳有时包含这些工件。创建Excel表格的人在手动编写标签时插入了表格,这可能是偶然的。因此,API与代码一样工作正常。再次感谢!谢谢你,阿克塞尔!这似乎是我正在寻找的解决方案。我将测试我的数据。有趣的是,你不能复制它。我会在这方面付出更多的努力,而且效果会很好!再次感谢!示例中的Excel单元格属于哪种数据类型?就我而言,它们是
re为用户定义类型。B列中的单元格值为日期时间值。单元格编号格式为TT.MM.YYYY hh:MM:ss.000.Ok,找到原因:问题的原因是Excel工作表中的时间戳有时包含这些工件。创建Excel表格的人在手动编写标签时插入了表格,这可能是偶然的。因此,API与代码一样工作正常。再次感谢!