java - Apache POI 无法从 Excel 单元格中正确读取秒数

标签 java excel apache-poi localdate

我目前正在调试别人的代码库。目的是将数据从 Excel 文件导入数据库。 excel 文件中的每一行在第 0 列中包含一个时间戳,在其他列中包含一些标签值。
时间戳包含年、月、day_of_month、小时、分钟和秒。要解析 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”。问题直接转化为数据库:文件中的第二个 11 被保存为数据库中的第二个 10(因此纳秒工件消失了)。
为了了解问题的原因,我阅读了以下文档:https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
由此我得出结论,我的问题的原因是舍入错误:API 将每个日期值解释为 double 值。在 Excel 中,您可以通过转换日期时间值来查看此 double 值,例如转换成文本或数字;例如“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 秒(千分之 500 秒),然后截断为秒。该方法将舍入 LocalDateTime到秒。

    LocalDateTime dateCellValue = cell.getLocalDateTimeCellValue(); //got directly from Excel
    dateCellValue  = dateCellValue.plusNanos(500000000).truncatedTo(ChronoUnit.SECONDS); //round to seconds
    
    完整示例:
    Excel 工作表如下所示:
    enter image description hereB 列中的单元格值这是日期时间值。单元格编号格式为 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
    

    关于java - Apache POI 无法从 Excel 单元格中正确读取秒数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63613873/

    相关文章:

    java - 如何在 JAVA 中使用 apache poi 删除 Excel 中的警告?

    excel - 宏未显示在宏表中

    JAVA - Apache POI批量修改Excel文件

    java - 在 AES 中收缩一些密文 block

    java - 读取 .jar 文件中的图像时程序崩溃

    mysql - 有没有像 JSON Builder 这样的东西?

    excel - PowerPivot 未将基本身份验证中的授权 header 发送到 OData Svc

    java - 使用 poi 自动调整列宽

    java - 从文件创建二叉树

    java - 创建jpa后自动填充表格