java.time
Answer by Saviour Self 看起来是正确的。但它使用的旧日期时间类已被 Java 8 及更高版本中内置的 java.time 框架所取代。
Apache Commons CSV
作为奖励,我将展示如何使用 Apache Commons CSV 库来处理读取/写入 CSV 文件的琐事。
首先我们通过创建StringReader来模拟一个CSV文件。
RFC 4180 规范
RFC 4180 规范正式定义了 CSV 格式。对此也存在变体。
RFC 4180 要求 回车 + 换行 (CRLF) 作为换行符(行终止符)。最后一行的终止符是可选的,我们在这里包含。
我们省略了可选的标题行(列标题)。
String newline = "\r\n";
StringBuilder input = new StringBuilder ();
input.append ( "2016-02-03,15:10:00,37" ).append ( newline );
input.append ( "2016-02-03,15:15:00,38" ).append ( newline ); // 5 minutes later.
input.append ( "2016-02-03,15:17:00,39" ).append ( newline ); // 2 minutes later.
Reader in = new StringReader ( input.toString () );
接下来,我们将整个 CSV 文件读入内存,Commons CSV 库在内存中创建 CSVRecord 对象来表示每一行传入数据。一行代码完成所有工作,CSVFormat::parse 生成一个 CSVParser 对象(Interable 的实现)。
Iterable<CSVRecord> records;
try {
records = CSVFormat.DEFAULT.parse ( in ); // 'records' is a CSVParser.
} catch ( IOException ex ) {
// FIXME: Handle exception.
System.out.println ( "[ERROR] " + ex );
return; // Bail-out.
}
现在我们分析CSVRecord 对象的集合。记住第一个作为我们的基线,在这里存储为Instant(下面讨论)。然后循环比较每个连续的CSVRecord 对象,检查每个字段作为String。
Instant firstInstant = null; // Track the baseline against which we calculate the increasing time
for ( CSVRecord record : records ) {
String dateInput = record.get ( 0 ); // Zero-based index.
String timeInput = record.get ( 1 );
String priceInput = record.get ( 2 );
//System.out.println ( dateInput + " | " + timeInput + " | " + priceInput ); // Dump input strings for debugging.
提取仅限日期和仅限时间的字符串,组合成LocalDateTime。
// Parse strings.
LocalDate date = LocalDate.parse ( dateInput );
LocalTime time = LocalTime.parse ( timeInput );
Integer price = Integer.parseInt ( priceInput );
// Combine date and time.
LocalDateTime ldt = LocalDateTime.of ( date , time ); // Not a specific moment on the timeline.
这个日期时间对象不是时间线上的一个点,因为我们不知道它的offset-from-UTC 或时区。如果您要使用这些值来计算 LocalDateTime 对象之间的增量,您将假设一般 24 小时没有异常,例如夏令时 (DST)。如果您的数据碰巧在任何异常期间没有发生,您可能会侥幸逃脱,但这是一个坏习惯。如果知道,最好指定一个时区。
我们知道数据的来源,因此我们可以假设预期的时区为ZoneId。通过分配假定的时区,我们可以在时间线上得到一个真实的时刻。
// Generally best to assign the time zone known to apply to this incoming data.
ZoneId zoneId = ZoneId.of ( "America/New_York" ); // Move this line somewhere else to eliminate needless repetition.
ZonedDateTime zdt = ldt.atZone ( zoneId ); // Now this becomes a specific moment on the timeline.
从 ZonedDateTime 我们可以提取 UTC 中的同一时刻(Instant)。通常Instant 是您应该用于数据存储、数据交换、序列化等的。您只需要 ZonedDateTime 在用户预期的时区向他们展示。
Instant instant = zdt.toInstant (); // Use Instant (moment on the timeline in UTC) for data storage, exchange, serialization, database, etc.
if ( null == firstInstant ) {
firstInstant = instant; // Capture the first instant.
}
目标是将每个CSVRecord 与原始基线日期时间进行比较。 Duration.between 方法就是这样做的。
Duration duration = Duration.between ( firstInstant , instant );
我们以总秒数计算增量。
Long deltaInSeconds = duration.getSeconds ();
将这些结果写入输出 CSV 文件留给读者作为练习。 Apache Commons CSV 库简化了工作,因为它可以写入和读取 CSV 格式。
// … output the deltaInSeconds & price to CSV. Apache Commons CSV can write as well as read CSV files.
System.out.println ( "deltaInSeconds: " + deltaInSeconds + " | price: " + price );
}
运行时。
deltaInSeconds: 0 | price: 37
deltaInSeconds: 300 | price: 38
deltaInSeconds: 420 | price: 39