Java中将日期转换为带时区的时间戳

4

我有一个PostgreSQL数据库,其中一个列被定义为时间戳(timestamp)。

我收到的日期带时区格式是yyyy-MM-ddTHH:mm:ss.SSSX,例如2020-12-16T15: 05:26.507Z。如果我想将其插入到一个时间戳列中,它会抛出“时间戳格式必须为yyyy-mm-dd hh:mm:ss”的错误。

我正在执行以下操作:

Timestamp.valueOf("2020-12-16T15:05:26.507")

现在时区日期来自JSON,因此我暂时将其作为字符串处理。

我该如何将其转换为简单的时间戳格式,如2020-12-16 15:05:26?


我建议您不要使用 java.sql.Timestamp。那个类设计得很差,而且已经过时了。相反,使用来自 java.time,现代Java日期和时间APILocalDateTimeOffsetDateTime - Ole V.V.
1
抱歉...日期是2020-12-16T15:05:26.507Z。 - user955165
3个回答

4
下表总结了PostgreSQL列类型与Java SE 8日期时间类型的映射:
--------------------------------------------------
PostgreSQL                          Java SE 8
==================================================
DATE                                LocalDate
--------------------------------------------------
TIME [ WITHOUT TIMEZONE ]           LocalTime
--------------------------------------------------
TIMESTAMP [ WITHOUT TIMEZONE ]      LocalDateTime
--------------------------------------------------
TIMESTAMP WITH TIMEZONE             OffsetDateTime
--------------------------------------------------

请注意,ZonedDateTimeInstantOffsetTime / TIME [ WITHOUT TIMEZONE ]不受支持。此外,请注意所有OffsetDateTime实例都必须位于UTC(具有+00:00小时的时区偏移量)。这是因为后端将它们存储为UTC
因此,有两种选择。
选项1(推荐):
将列类型更改为TIMESTAMP WITH TIMEZONE。这是推荐的,因为您的日期时间字符串具有代表Zulu日期时间或UTC日期时间的Z。使用OffsetDateTime,可以在不需要任何显式DateTimeFormatter的情况下解析此日期时间字符串。
演示:
import java.time.OffsetDateTime;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime odt = OffsetDateTime.parse("2020-12-16T15:05:26.507Z");
        System.out.println(odt);
    }
}

输出:

2020-12-16T15:05:26.507Z

以下是如何在数据库CRUD操作中使用OffsetDateTime的示例:
OffsetDateTime odt = OffsetDateTime.parse("2020-12-16T15:05:26.507Z");
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, odt);
st.executeUpdate();
st.close();

选项 - 2:

如果您仍希望保持列类型为 TIMESTAMP [ WITHOUT TIMEZONE ],您可以从 OffsetDateTime 获取 LocalDateTime,并按如下所示使用:

OffsetDateTime odt = OffsetDateTime.parse("2020-12-16T15:05:26.507Z");
LocalDateTime ldt = odt.toLocalDateTime();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, ldt);
st.executeUpdate();
st.close();

1

两个建议:

  1. 由于您收到的字符串包含UTC指示器(Z),请将时间保存为UTC格式到您的数据库中。如果允许,为此更改PostgreSQL数据库中的数据类型为带时区的时间戳
  2. 使用现代Java日期和时间API java.time。不要将日期和时间保存为TimestampString到数据库中。 java.sql.Timestamp类设计较差,是对已经设计不佳的java.util.Date类的真正破解,并且已经过时。

您从JSON中获取的字符串采用ISO 8601格式。这很棒,因为它是国际标准,并且java.time类通常可以本地解析ISO 8601格式,即无需任何显式格式设置器。要解析它:

    String fromJson = "2020-12-16T15:05:26.507Z";
    OffsetDateTime dateTime = OffsetDateTime.parse(fromJson);
    System.out.println(dateTime);

迄今为止的输出:

2020-12-16T15:05:26.507Z

OffsetDateTime 还可以以 ISO 8601 格式输出。插入到 Postgress 中:

    String sql = "insert into your_table(your_timestamp_with_time_zone_col)"
            + " values(?);";
    PreparedStatement ps = yourDatabaseConnection.prepareStatement(sql);
    ps.setObject(1, dateTime);
    int rowsInserted = ps.executeUpdate();

如果您无法更改数据库中的数据类型,则需要将LocalDateTime插入到您的timestamp(不带时区)列中:
    LocalDateTime dateTime = OffsetDateTime.parse(fromJson)
            .atZoneSameInstant(ZoneOffset.UTC)
            .toLocalDateTime();
    
    String sql = "insert into your_table(your_timestamp_col) values(?);";

其余部分与以前相同。

我选择转换为UTC以防万一。在您的示例中,字符串已经在UTC中,因此转换是无操作的;但它可能并不总是如此,所以最好这样做。

链接

Oracle教程:日期时间解释如何使用java.time。


0
我认为你可以使用另一个Timestamp API:

static Timestamp valueOf(LocalDateTime dateTime)

并将您的代码更改为:
    LocalDateTime localDateTime = LocalDateTime.parse("2020-12-16T15:05:26.507");
    Timestamp.valueOf(localDateTime);

如果你想使用不同于系统时区的时区,你也可以使用:ZonedDateTime。

然后使用 ZonedDateTime 获取 LocalDateTime,并将其传递给 Timestamp.valueOf()。


抱歉,我的意思是要添加时区信息,因此我接收到的日期和时间都带有Z -> "2020-12-16T15:05:26.507Z"。 上述代码将无法运行,因为LocalDateTime无法解析该格式。 - user955165

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