簡體   English   中英

Apache POI 無法從 Excel 單元格中正確讀取秒數

[英]Apache POI doesn't correctly read seconds from Excel cells

我目前正在調試別人的代碼庫。 目的是將數據從 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 將每個日期值解釋為雙精度值。 在 Excel 中,您可以通過將日期時間值(例如,轉換為文本或數字)來查看此雙精度值; 例如“20.03.2019 08:36:39”變成“43544,3587847222”。 因此,由於某些值的數據類型,四舍五入是不准確的,因此 Java 不能正確解析該值。

我的問題是,如何快速准確地解決此類問題。 基本上,我想到了兩種可能性:

  1. 我可以通過 DateFormatter 解析日期,而不是使用 Apache POI。 但是,這有一個缺點,即用戶定義的日期始終需要具有相同的格式,而目前並非如此。
  2. 在代碼中,我可以嘗試四舍五入以更正第二個值並切割納秒偽影。 但是,我不確定,如果我總是需要舍入到上面的下一個第二個值(如上面顯示的示例中),或者是否存在我需要舍入到下一個的情況(例如低於 0.5 的納秒值)下面的第二個值。 我的問題是,我不完全理解程序行為背后的關於舍入誤差的確切系統。

有人有建議嗎? 非常感謝幫助!

編輯:問題的原因是,納秒已經在 Excel 工作表中,但肉眼無法識別,因為相關的數據類型沒有顯示它們。

我無法重現該問題。 如果apache poi獲取LocalDateTime 2018-09-12T12:39:10.995 則Excel單元格已存儲該確切日期時間。 當然,由於日期格式已經四舍五入, Excel可能不會完全顯示它。 例如,日期格式DD.MM.YYYY hh:mm:ss將顯示 12.09.2018 12:39:11 為 2018-09-12T12:39:10.995。 但存儲的是確切的日期時間。

但是,如果需要僅以秒為單位獲取LocalDateTime ,則可以添加 0.5 秒(千LocalDateTime 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

代碼:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM