如何使用 DateTimeFormatter 转换字符串以获取 java.sql.Timestamp



我在字符串中有一个时间戳,我正在使用DateTimeFormatter来解析字符串,如下所示并将其分配给时间戳类型变量

import java.sql.Timestamp
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAccessor
import java.time.Instant
String myTime = "2020-08-03T20:15:49"
String myTimeFormat = "yyyy-MM-dd'T'HH:mm:ss"
DateTimeFormatter timestampFormatter = DateTimeFormatter.ofPattern(myTimeFormat);
TemporalAccessor ta = timestampFormatter.parse(tempValue);

// getting error that Cannot create Instant from java.time.format.Parsed
Timestamp finalTime = Timestamp.from(Instant.from(ta));

如何将其转换为java.sql.Timestamp?

上下文:我正在尝试将 Spark 数据帧中的字符串列(使用时间戳格式)转换为时间戳列,并且我在我的 udf 中使用上述逻辑(使用 udf,因为我除了强制转换之外还需要执行其他检查),从而尝试转换为时间戳以应用具有此列作为时间戳的 Spark 架构

参考: https://spark.apache.org/docs/latest/sql-ref-datatypes.html

你不需要DateTimeFormatter

现代日期-时间 API 基于 ISO 8601,只要日期-时间字符串符合 ISO 8601 标准,就不需要显式使用DateTimeFormatter对象。

一旦解析为LocalDateTime,就可以使用Timestamp#valueOf获取java.sql.Timestamp

演示:

import java.sql.Timestamp;
import java.time.LocalDateTime;
public class Main {
public static void main(String[] args) {
String myTime = "2020-08-03T20:15:49";
LocalDateTime ldt = LocalDateTime.parse(myTime);
Timestamp finalTime = Timestamp.valueOf(ldt);
System.out.println(finalTime);
}
}

输出:

2020-08-03 20:15:49.0

在线演示

注意:java.util日期-时间 API 及其格式 APISimpleDateFormat已过时且容易出错。由于java.sql.Timestamp扩展了java.util.Date,它继承了同样的缺点。建议完全停止使用它们,并切换到现代日期-时间 API*。查看此答案和此答案以了解如何将java.timeAPI 与 JDBC 一起使用。

要了解有关现代日期-时间 API 的更多信息,请参阅跟踪:日期时间


* 出于任何原因,如果你必须坚持使用 Java 6 或 Java 7,你可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到 Java 6 和 7。如果您正在为 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请查看通过脱糖提供的 Java 8+ API 和如何在 Android Project 中使用 ThreeTenABP。

你的问题不是你不能创建一个Timestamp:而是你不能创建一个Instant

Instant标识时间轴上的单个点,通过与 Unix 纪元 (1970-1-1 00:00:00 UTC) 的偏移量标识。

输入的问题在于它没有标识时区。因此,它不能唯一标识单个时间点,因为 2020-08-03T20:15:49 与伦敦 vs 纽约 vs 上海 vs 德里(例如)的时刻不同。

因此:将字符串解析为LocalDateTime;然后指定时区;然后转换为即时;然后转换为时间戳:

LocalDateTime ldt = LocalDateTime.parse(myTime, timestampFormatter);
// Or whichever time zone.
Instant instant = ldt.atZone(ZoneId.of("UTC")).toInstant();
Timestamp finalTime = Timestamp.from(instant);

什么是时间戳?

维基百科将时间戳定义为

字符序列或编码信息,标识何时 发生了某些事件...

因此,您的字符串不是时间戳,或者至少只有在我们知道它包含的日期和时间假定哪个时区(或 UTC 偏移量)时才可以被视为时间戳。如今,大多数 IT 系统并不局限于一个时区,因此通常建议在时间戳中包含 UTC 偏移量信息和/或将其保留为 UTC。我假设您要求使用一种用于SQL数据库的老式java.sql.Timestamp。根据您对建议的遵守情况,您将在SQL和Java中需要不同的类型。

  1. 建议:使用显式偏移量,最好是 UTC。对于大多数数据库引擎,这意味着使用其timestamp with time zonetimestamptz数据类型。从 4.2 开始的 JDBC 标准说你应该在 Java 中使用OffsetDateTime。许多驱动程序也处理Instant,我们通常更喜欢Java中明确时间戳的类。您尝试创建Instant可能会暗示这与您的意图一致。
  2. 不建议:使用隐式偏移量,最好是 UTC。许多旧应用程序将使用自己的时区。在任何情况下,在SQL中使用timestamp(没有时区),在Java中使用LocalDateTime。字符串中缺少偏移量可能暗示这是您的方法。

你提到的java.sql.Timestamp类设计得很差,实际上是在已经设计不佳的java.util.Date类之上的真正黑客。因此,Timestamp不在我建议将 tiemstamp 值发送到数据库的类之列,无论是用于存储还是用于查询。

将时间戳保存到 SQL

下面是使用OffsetDateTime和自定义假定时区的代码示例。

String myTime = "2020-08-03T20:15:49";
OffsetDateTime odt = LocalDateTime.parse(myTime)
.atZone(ZoneId.of("Asia/Colombo"))
.toOffsetDateTime();

PreparedStatement ps = yourDatabaseConnection
.prepareStatement("insert into your_table(your_timestamptz_column) values (?);");
ps.setObject(1, odt);
ps.executeUpdate();

从 JDBC 4.2 开始,setObject方法接受包括OffsetDateTime的 java.time 类型。

如果您的时间字符串是 UTC,则转换为OffsetDateTime会更简单一些:

OffsetDateTime odt = LocalDateTime.parse(myTime).atOffset(ZoneOffset.UTC);

使用Instant:您可以简单地转换之前的OffsetDateTime

Instant inst = odt.toInstant();

现在,您可以按照我们之前传递odt相同的方式将inst传递给setObject()

如果你在SQL中使用没有时区timestamp,在Java中使用LocalDateTime,Arvind Kumar Avinash的答案已经显示了解析字符串的简单方法。也可以按照与上述相同的方式将LocalDateTime传递给setObject()

顺便说一句,我们通常既不需要也不想使用TemporalAccessor界面。其文档说:

此接口是框架级接口,不应 广泛用于应用程序代码。。