【问题标题】:Read data from excel sheet in java with Apache POI使用Apache POI从java中的excel表中读取数据
【发布时间】:2019-12-22 15:10:51
【问题描述】:

我有一个需要在 java 中读取的 Excel 表中的数据,我能够读取普通字符串,但是当我尝试读取包含日期和时间的单元格时 (28/5/2018 10:00)我只得到日期为 2018 年 5 月 28 日。

这是excel单元格

此代码为:2018 年 5 月 28 日

row.getCell(6).toString()

此代码引发异常:

row.getCell(6).getStringCellValue()

如何读取时间?

如何正确读取日期?

【问题讨论】:

    标签: java excel apache-poi


    【解决方案1】:

    tl;博士

    row                              // Starting with a Poi `Row` object.
    .getCell(6)                      // Extract a Poi `Cell` object.
    .getDateCellValue()              // Extract value from spreadsheet as a `java.util.Date` object. This terrible class is now legacy. Immediately convert to java.time.Instant as seen on next line.
    .toInstant()                     // Convert from legacy `java.util.Date` class to modern `java.time.Instant`. Both represent a moment in UTC.
    .atZone(                         // Adjusting from UTC to a time zone.
        ZoneId.of( "Asia/Tokyo" )    // Specify a time zone to adjust from UTC (an offset of zero hours-minutes-seconds). 
    )                                // Returns a `ZonedDateTime` object. Same moment, different wall-clock time.
    .toLocalDate()                   // Extract the date-only portion. Or call `toLocalTime` for the time-of-day portion.
    

    详情

    其他答案正确显示了如何获取 java.util.Date 对象。但是这个类 (a) 很糟糕,并且 (b) 现在是遗留的,几年前被现代的 java.time 类所取代。您需要更进一步才能找到合适的解决方案。

    得到你的java.util.Date 对象后:

    java.util.Date javaUtilDate = row.getCell(6).getDateCellValue() ;
    

    ...您在 UTC 中有一段时间。不幸的是,按照建议致电Date::toString

    row.getCell(6).getDateCellValue().toString() 
    

    …造成令人困惑的结果。该方法应用 JVM 当前的默认时区,从 UTC 调整到某个时区。这造成了这个时区是java.util.Date 的一部分的错觉,但事实并非如此。这种行为意味着您的结果在运行时会有所不同,具体取决于当前的默认时区。请注意,不仅用户可以更改默认时区,该 JVM 中运行的任何应用程序的任何线程中的任何代码也可以更改。

    为了获得可预测的结果,您应该指定所需/预期的时区。

    为此,请立即将您的 java.util.Date 转换为它的替代品 Instant 类。两者都代表 UTC 中的一个时刻。请注意添加到旧遗留类中的新转换方法。

    Instant instant = javaUtilDate.toInstant() ;
    

    Continent/Region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 2-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

    ZoneId zoneId = ZoneId.of( "Africa/Tunis" ) ;
    

    如果您想使用 JVM 当前的默认时区,请显式请求它,并作为参数传递。如果省略,代码会变得模糊,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。

    ZoneId zoneId = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.
    

    将该时区应用于您在 UTC 中的时刻。

    ZonedDateTime zdt = instant.atZone( zoneId ) ;
    

    生成的ZonedDateTime 对象代表同一时刻,时间轴上与Instant 相同的同时点。但它的挂钟时间已调整为特定地区(时区)人们使用的时间。

    你问:

    如何读取时间?

    如何正确读取日期?

    您可以使用DateTimeFormatter 以您想要的任何格式打印其中一个或两个,甚至可以自动本地化为Locale 对象中定义的人类语言和文化规范。

    或者您可以将日期和时间值提取为单独的对象。

    LocalDate localDate = zdt.toLocalDate() ;
    LocalTime localTime = zdt.toLocalTime() ;
    


    关于java.time

    java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

    要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

    Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

    您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

    从哪里获得 java.time 类?

    ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

    【讨论】:

    【解决方案2】:

    试试

    row.getCell(6).getDateCellValue();
    

    如果你仍然需要它是一个字符串,试试

    row.getCell(6).getDateCellValue().toString();
    

    【讨论】:

    • 谢谢,它就像魔术一样!只需要以我需要的格式解析它。我不敢相信当我在那里搜索时我没有在文档中看到这个功能。
    • 没问题!为什么你在第二个代码块上抛出异常是有道理的
    【解决方案3】:

    我希望这会有所帮助

    try (FileInputStream fis = new FileInputStream(filename)) {
            HSSFWorkbook workbook = new HSSFWorkbook(fis);
            HSSFSheet sheet = workbook.getSheetAt(0);
    
            // Read a cell the first row and sixth cell.
            HSSFCell cell = sheet.getRow(0).getCell(6);
    
    
            // Using HSSFDateUtil to check if a cell contains a date.
            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                System.out.println("The cell contains a date value: "
                    + cell.getDateCellValue());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    

    【讨论】:

    • 谢谢,函数“cell.getDateCellValue()”完成了我需要的工作。
    【解决方案4】:

    要将单元格值作为格式化字符串获取,如Excel 单元格中所示,请使用DataFormatter

    例子:

    ...
    DataFormatter formatter = new DataFormatter(); 
    ...
    Row row = ...;
    ...
    Cell cell = row.getCell(6);
    String value = formatter.formatCellValue(cell);
    System.out.println(value);
    ...
    

    由于该值可能是公式的结果,请使用DataFormatterFormulaEvaluator

    ...
    DataFormatter formatter = new DataFormatter(); 
    ...
    Workbook workbook = ...;
    FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); 
    ...
    Row row = ...;
    ...
    Cell cell = row.getCell(6);
    String value = formatter.formatCellValue(cell, evaluator);
    System.out.println(value);
    ...
    

    【讨论】: