在Java中使用Apache POI从Excel工作表读取数据

4
我有一份Excel表格中的数据需要在Java中读取。我能够读取普通的字符串,但当我尝试读取一个包含日期和时间(28/5/2018 10:00)的单元格时,我只得到了日期部分28-May-2018。
以下是Excel单元格的截图:
https://istack.dev59.com/yPMLn.webp 这段代码读取的是:28-May-2018
https://istack.dev59.com/hbJJ2.webp
row.getCell(6).toString()

这段代码会抛出一个异常:

row.getCell(6).getStringCellValue()

我如何读取时间?

我如何正确阅读日期?

4个回答

3

简而言之

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() ;

请在格式为大陆/地区正确时区名称中指定时区,例如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() ;

Table of date-time types in Java, both modern and legacy


关于java.time

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

要了解更多,请参见Oracle教程。并在Stack Overflow上搜索许多示例和说明。规范是JSR 310

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

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

从哪里获取java.time类?

ThreeTen-Extra 项目扩展了 java.time 的附加类。该项目是 java.time 可能未来添加内容的试验场所。您可能会在这里找到一些有用的类,例如 Interval, YearWeek, YearQuarter更多


1
当前的 apache poi 4.1.1 版本提供了 java.time.LocalDateTime getLocalDateTimeCellValue() 方法。同时,还有 org.apache.poi.ss.usermodel.DataFormatter 可以使用与 Excel 相同的格式将单元格内容作为格式化字符串获取。 - Axel Richter
谢谢!你的回答非常详细,帮了我很多。 - Mike

2

请尝试

row.getCell(6).getDateCellValue();

如果您仍需要将其转换为字符串,请尝试以下方法:

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

谢谢,它就像魔术一样奏效!只需要按我需要的格式解析它。当我在文档中搜索时,我简直不敢相信我没有看到这个函数。 - Mike
没问题!第二个代码块抛出异常的原因很清楚。 - dinonugs

0

希望这对你有所帮助

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()”完成了我所需的工作。 - Mike

0

如果要获取单元格的值,并以类似于 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);
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接