问题描述
我目前正在调试其他人的代码库。目的是将数据从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有时会将时间戳的秒数延长了十亿分之一秒。例如,Java将时间戳“ 12.09.2018 12:39:11”解释为“ 2018-09-12T12:39:10.995”。问题直接转化为db:文件中的秒11被保存为db中的秒10(因此,纳秒级工件消失了)。
要了解问题的原因,我阅读了以下文档:https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
由此我得出结论,我的问题的原因是舍入错误:API将每个日期值解释为双精度。在Excel中,您可以通过转换日期时间值(例如,转换为文本或数字;例如“ 20.03.2019 08:36:39”变为“ 43544,3587847222”。因此,由于某些值的数据类型,四舍五入是不准确的,因此Java无法正确解析该值。
我的问题是,如何快速准确地解决此类问题。基本上,我想到了两种可能性:
- 我可以使用DateFormatter解析日期,而不是使用Apache POI。但是,这样做的缺点是,用户定义的日期始终必须使用与当前不同的相同格式。
- 在代码中,我可以尝试舍入以校正第二个值并削减纳秒级伪像。但是,我不确定是否总是需要四舍五入到上面的下一个第二个值(如上述示例中所示),或者在某些情况下(例如,小于0.5的纳秒级值)需要四舍五入到下一个下面的第二个值。我的问题是,对于舍入错误,我不完全了解程序行为背后的确切系统。
有人有建议吗?非常感谢您的帮助!
编辑: 问题的原因是Excel表格中已经存在纳秒,但肉眼无法识别,因为相关的数据类型没有显示它们。
解决方法
我无法重现该问题。如果apache poi
收到LocalDateTime
2018-09-12T12:39:10.995,则Excel
单元格已存储该确切的日期时间。诅咒Excel
可能无法完全显示出来,因为日期格式已经四舍五入了。例如,日期格式DD.MM.YYYY hh:mm:ss
将显示2018-09-12T12:39:10.995的12.09.2018 12:39:11。但是存储的是确切的日期时间。
但是,如果只需要以秒为单位获取LocalDateTime
,则可以增加0.5秒(50万分之一秒),然后将其截断为秒。这种方法会将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
。
代码:
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.io.FileInputStream;
class ExcelReadLocalDateTime {
public static void main(String[] args) throws Exception {
//Workbook workbook = WorkbookFactory.create(new FileInputStream("Workbook.xls")); String filePath = "WorkbookNew.xls";
Workbook workbook = WorkbookFactory.create(new FileInputStream("Workbook.xlsx")); String filePath = "WorkbookNew.xlsx";
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING:
System.out.println(cell.getRichStringCellValue().getString());
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
LocalDateTime dateCellValue = cell.getLocalDateTimeCellValue(); //got directly from Excel
System.out.println(dateCellValue);
dateCellValue = dateCellValue.plusNanos(500000000).truncatedTo(ChronoUnit.SECONDS); //round to seconds
System.out.println(dateCellValue);
} else {
System.out.println(cell.getNumericCellValue());
}
break;
default:
System.out.println();
}
}
}
workbook.close();
}
}
结果:
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