Apache POI:从一个单元到另一个单元的传输时间

Apache POI: Transfer time from cell to cell

简而言之:我想将时间值从一个单元格复制到另一个单元格。

简而言之问题:POI 5.2.2(或更具体:DateUtil.internalGetExcelDate)将 08:00 点从输入单元格转换为数值-1.00.

更多详情:有一个 xlsx 文件(使用 LibreOffice 7.0.4.2 创建),其中包含时间“08:00:00”:

我可以用 sourceCell.getLocalDateTimeCellValue() 读取那个值,这很好。

但是当我尝试将该值转移到另一个单元格 (targetCell.setCellValue(sourceCell.getLocalDateTimeCellValue())) 时,在 targetCell 中存在值 -1 而不是预期的 08:00:00 o'时钟。

这是调试 setCellValue 调用时的屏幕截图:

下面是调试 DateUtil.internalGetExcelDate 调用时的屏幕截图:

可能的解决方法:我想它可以从 sourceCell 评估 LocalDateTime 并且如果它的年份 < 1904 那么我添加一些结果 LocalDateTime 没有被 DateUtil.internalGetExcelDate.

转换为 -1.00 的年数

这是我不想做的事情,因为这会在 targetCell 中设置与 sourceCell 中不同的值。

另一种解决方法:另一种解决方法是使用 LocalDateTime.now(),设置小时和分钟,调用 targetCell.setCellValue(...),然后像这样更改格式:

    short format = workbook.createDataFormat().getFormat("HH:MM:SS");
    CellStyle cellStyle = workbook.createCellStyle();
    cellStyle.setDataFormat(format);
    targetCell.setCellStyle(cellStyle);

不幸的是,我不知道 sourceCell 是只包含时间还是包含完整的时间戳。我只想复制单元格内容(适用于字符串、数字等)。

实际解决方法:作为当前(有效)解决方法,我检查年份,如果它小于 1900,那么我在该日期设置另一个年份,设置修改后的 LocalDateTime 到单元格中并设置数据格式(请参阅上面的解决方法描述)。

问题:如何将 LocalTime 值从一个单元格转移到另一个单元格(不处理年份)?我想我的(工作)解决方法不应该是答案......

Excel 单元格日期类型

Microsoft Excel 只有以下单元格数据类型:

  • 字符串(字母数字)
  • 数值(浮点数)
  • 布尔值(真或假)
  • Formula(公式字符串)
  • 错误(内部错误代码)
  • 空(空单元格)

没有特殊的日期单元格数据类型,也没有特殊的整数单元格日期类型。

Excel 如何存储日期或时间或 date-time

如果Excel存储date-time,它存储为浮点数。 1.00 是 1900-01-01 00:00:00.000。 (Excel设置1904-Date时有一个特例,但那只是1.00的意义的特例。)

加1.00就是加一天。增加 1/24 意味着增加一小时。增加 1/24/60 意味着增加一分钟。添加 1/24/60/60 意味着增加一秒。添加 1/24/60/60/10 表示添加十分之一秒,依此类推。

对于小于 1.00 的单元格值,Excel 将其解释为 1900 年第 1 个月第 0 天的时间(如果 Excel 已设置 1904 年日期,则为 1904 年)。 1/24 表示一小时。 1/24 + 1/24/60 表示一小时一分钟等等。所以你的 08:00:00 是单元格值 8*1/24 (8 小时) = 1/3.

读取 Excel 单元格值时,确定 Excel 将单元格值解释为日期或时间还是 date-time 的唯一方法是也获取单元格的数字格式。如果这是日期或时间或 date-time 格式,则 Excel 将单元格值 1.00 解释为 1900-01-01,将单元格值 1/24 解释为 01:00:00,单元格1/24 + 1/24/60 的值为 01:01:00 等等。

但你的观察是正确的。如果 Apache POI 从仅设置时间的 Excel 单元格读取 date-time,这是一个介于 0.00 和 1.00 之间的数字 (double) 值,那么它会读取 date-time 1899-12-31 日。但这不是 Excel 所做的。对于 Excel,time-only 值在 1900 年第 1 个月的第 0 天。如果 Apache POI 设置 date-time 值,例如 1899-12-31 08:00:00 , 然后它设置 -1 因为 Excel 在 1900 之前不能有 date-time 值。

因此,在 Excel 单元格中设置时间值的唯一方法是将数字 (double) 值设置在 0.00 和 1.00 之间,并设置数字格式为 [=13= 的单元格样式].不能仅从 Java date-time 值设置时间 Excel 单元格值,因为没有 Java date-time 值可以有一个月的第 0 天1 年 1900 年。

因此,如果 DateUtil.isCellDateFormatted 表明源单元格为日期格式且该单元格的数字 (double) 单元格值小于 1,则设置该数字 (double ) 单元格值到新单元格并将该单元格的格式设置为与源单元格相同。

要测试的完整示例:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;

public class ExcelSetCellValue {
    
 public static void main(String[] args) throws Exception {
     
  Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelWithTime.xlsx")); String filePath = "./ExcelWithTimeNew.xlsx";
  //Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelWithTime.xls")); String filePath = "./ExcelWithTimeNew.xls";

  Sheet sheet = workbook.getSheetAt(0);
  
  Cell sourceCell = sheet.getRow(0).getCell(0); // get cell value from A1: 08:00:00
  Row targetRow = sheet.getRow(5); if (targetRow==null) targetRow = sheet.createRow(5);
  Cell targetCell = targetRow.getCell(5); if (targetCell==null) targetCell = targetRow.createCell(5);
 
  if (sourceCell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(sourceCell)) {
   System.out.println(sourceCell.getNumericCellValue()); // 8*1/24 = 1/3
   System.out.println(sourceCell.getLocalDateTimeCellValue()); // 1899-12-31T08:00
   //targetCell.setCellValue(sourceCell.getLocalDateTimeCellValue()); // does not work because sourceCell.getLocalDateTimeCellValue() is in year 1899
   targetCell.setCellValue(sourceCell.getNumericCellValue()); 
   targetCell.setCellStyle(sourceCell.getCellStyle());
  }
  
  FileOutputStream out = new FileOutputStream(filePath);
  workbook.write(out);
  out.close();
  workbook.close();
 }
}