【问题标题】:How to convert string to get java.sql.Timestamp using DateTimeFormatter如何使用 DateTimeFormatter 转换字符串以获取 java.sql.Timestamp
【发布时间】:2021-11-14 01:15:05
【问题描述】:

我在字符串中有一个时间戳,我正在使用 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,因为我还需要执行其他检查只是强制转换),因此尝试转换为 Timestamp 以将此列作为 Timestamp 应用 spark 模式

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

【问题讨论】:

标签: java apache-spark-sql timestamp datetime-format java-time


【解决方案1】:

什么是时间戳?

维基百科将时间戳定义为

一个字符序列或编码信息,用于识别何时 发生了某些事件……

因此,如果我们知道它包含的日期和时间假定为哪个时区(或 UTC 偏移量),那么您的字符串不是时间戳,或者至少只能被视为一个时间戳。如今,大多数 IT 系统并不局限于一个时区,因此通常建议在您的时间戳中包含 UTC 偏移信息和/或将它们保持为 UTC。我假设您要求一个老式的java.sql.Timestamp 用于您的 SQL 数据库。根据您对建议的遵守情况,您将需要 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 方法接受 java.time 类型,包括 OffsetDateTime

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

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

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

    Instant inst = odt.toInstant();

现在您可以将inst 传递给setObject(),就像我们之前传递odt 一样。

如果您在 SQL 中使用不带时区的 timestamp 在 Java 中使用 LocalDateTime,Arvind Kumar Avinash 的回答已经显示了解析字符串的简单方法。 LocalDateTime 也可以通过与上述相同的方式传递给 setObject()

顺便说一句,我们通常不需要也不想使用TemporalAccessor 接口。它的文档说:

这个接口是框架级接口,不应该 广泛用于应用程序代码。 …

【讨论】:

    【解决方案2】:

    您不需要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
    

    ONLINE DEMO

    注意:java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。由于java.sql.Timestamp 扩展了java.util.Date,它继承了相同的缺点。建议完全停止使用它们并切换到modern Date-Time API*。查看this answerthis answer 了解如何将java.time API 与JDBC 结合使用。

    Trail: Date Time 了解有关现代日期时间 API 的更多信息。


    * 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

    【讨论】:

      【解决方案3】:

      您的问题不是您无法创建Timestamp:而是您无法创建Instant

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-20
        • 1970-01-01
        • 1970-01-01
        • 2014-10-03
        相关资源
        最近更新 更多