将LocalDate转换为LocalDateTime或java.sql.Timestamp

157

我正在使用JodaTime 1.6.2。

我有一个LocalDate需要转换为(Joda)LocalDateTimejava.sqlTimestamp进行ORMapping。

这样做的原因是我已经弄清楚了如何在LocalDateTimejava.sql.Timestamp之间进行转换:

LocalDateTime ldt = new LocalDateTime();
DateTimeFormatter dtf = DateTimeFormatter.forPattern("yyyy-MM-dd HH:mm:ss");
Timestamp ts = Timestamp.valueOf(ldt.toString(dtf));

如果我能将LocalDateLocalDateTime相互转换,那么我就可以继续转换为java.sql.Timestamp。对于任何正确方向的指引,谢谢!


3
请注意,Joda-Time 项目现已进入维护模式,团队建议迁移到 java.time 类。请参阅 Oracle 的教程。此外,java.sql.Timestamp 类现已过时,被 java.time 类所取代,特别是 Instant - Basil Bourque
7个回答

317

JodaTime

要将JodaTime的org.joda.time.LocalDate转换为java.sql.Timestamp,只需执行以下操作:

Timestamp timestamp = new Timestamp(localDate.toDateTimeAtStartOfDay().getMillis());

要将JodaTime的org.joda.time.LocalDateTime转换为java.sql.Timestamp,只需执行以下操作:
Timestamp timestamp = new Timestamp(localDateTime.toDateTime().getMillis());

JavaTime

要将Java8的java.time.LocalDate转换为java.sql.Timestamp,只需执行以下操作:

Timestamp timestamp = Timestamp.valueOf(localDate.atStartOfDay());

要将Java8的java.time.LocalDateTime转换为java.sql.Timestamp,只需执行以下操作
Timestamp timestamp = Timestamp.valueOf(localDateTime);

在将 LocalDateTime 转换为 DateTime 时要注意,因为并不是所有的 LocalDateTime 都是有效的 DateTime。来源:http://joda-time.sourceforge.net/faq.html#illegalinstant,我也遇到过这个问题。 - Christophe De Troyer
1
在参数上出现编译错误,因为valueOf()只接受字符串。 - Patriotic
1
@Patriotic:只有在您实际上没有使用Java SE 1.8或更新版本时才会发生这种情况。正如答案中所示,并且在提供的Javadoc链接中(上面答案中的蓝色文本实际上是链接,您可以单击它们来深入了解Javadoc),Timestamp#valueOf()方法自Java SE 1.8以来也接受LocalDateTime - BalusC
java.lang.IllegalArgumentException: 时间戳格式必须为yyyy-mm-dd hh:mm:ss[.fffffffff];;;; 我执行了Timestamp.valueOf(Timestamp(longtimestampppp).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().toString()) - Dr.jacky
1
只需按照答案中的最后一个示例即可,而不是无缘无故地对其执行 toString() - BalusC
有人能帮我解决这个问题吗?它无法正常工作。@BalusC https://stackoverflow.com/questions/69533213/java-jdbctemplate-insert-localdatetime-to-sqlserver-datetime-column-not-working - mattsmith5

69

使用Java 8时间API的最佳方式:

  LocalDateTime ldt = timeStamp.toLocalDateTime();
  Timestamp ts = Timestamp.valueOf(ldt);

用于 JPA 的使用,请将其放在您的模型中 (https://weblogs.java.net/blog/montanajava/archive/2014/06/17/using-java-8-datetime-classes-jpa):

@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime ldt) {
        return Timestamp.valueOf(ldt);
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp ts) {
        return ts.toLocalDateTime();
    }
}

现在它是相对于时区无关的时间。此外,这很容易实现:

  LocalDate ld = ldt.toLocalDate();
  LocalTime lt = ldt.toLocalTime();

格式:

 DateTimeFormatter DATE_TME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
 String str = ldt.format(DATE_TME_FORMATTER);
 ldt = LocalDateTime.parse(str, DATE_TME_FORMATTER);

更新:postgres 9.4.1208、HSQLDB 2.4.0等已经无需进行任何转换即可理解Java 8的时间API!


有人能帮我解决这个问题吗?它无法正常工作。@BalusC https://stackoverflow.com/questions/69533213/java-jdbctemplate-insert-localdatetime-to-sqlserver-datetime-column-not-working - mattsmith5
耶稣基督...似乎Java仍然在处理日期方面出了问题...我只希望以经典的毫秒格式获取当前日期... - Magno C

32

简述

Joda-Time项目已进入维护模式,现已被java.time类取代。

  • 只需使用java.time.Instant类。
  • 不需要:
    • LocalDateTime
    • java.sql.Timestamp
    • 字符串

捕获当前时刻的UTC时间。

Instant.now()  

将该时刻存储在数据库中:
myPreparedStatement.setObject( … , Instant.now() )  // Writes an `Instant` to database.

从数据库中检索那个时刻:

myResultSet.getObject( … , Instant.class )  // Instantiates a `Instant`

调整挂钟时间以适应特定时区的时间。

instant.atZone( z )  // Instantiates a `ZonedDateTime`

LocalDateTime是错误的类

其他答案是正确的,但它们未指出LocalDateTime对您的目的来说是错误的类。

java.timeJoda-Time中,LocalDateTime故意缺乏任何时区或偏移量的概念。因此,它不代表一个瞬间,并且不是时间轴上的一个点。 LocalDateTime表示关于大约26-27小时范围内可能发生的瞬间的粗略想法。

当区域/偏移量未知(不是一个好的情况),或者当区域偏移量不确定时,请使用LocalDateTime。例如,“圣诞节在2018年12月25日的第一刻开始”将被表示为LocalDateTime

使用ZonedDateTime来表示特定时区的时间点。例如,圣诞节在任何特定时区(如Pacific/AucklandAmerica/Montreal)开始,都可以用ZonedDateTime对象表示。
对于始终处于UTC的时刻,请使用Instant
Instant instant = Instant.now() ;  // Capture the current moment in UTC.

应用时区。同一时刻,同一时间轴上的相同点,但使用不同的挂钟时间来查看。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

如果我能够在LocalDate和LocalDateTime之间进行转换,那么…… 不,这是错误的策略。如果您有一个仅包含日期的值,并且想要一个日期时间值,则必须指定一天中的时间。该时间可能在特定时区的该日期上无效-在这种情况下,ZonedDateTime类会自动根据需要调整时间。
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 14 , 0 ) ;  // 14:00 = 2 PM.
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

如果您想将一天的第一个时刻作为您的时间点,请让 java.time 确定该时刻。不要假设一天从 00:00:00 开始。夏令时(DST)等异常情况可能意味着一天的开始时间是另一个时间,例如 01:00:00。
ZonedDateTime zdt = ld.atStartOfDay( z ) ;

java.sql.Timestamp 是错误的类

java.sql.Timestamp 是麻烦的旧日期时间类的一部分,现在已经被完全取代,由java.time类取代。该类用于表示UTC中的一个时刻,分辨率为纳秒。现在可以使用java.time.Instant来实现此目的。

JDBC 4.2与getObject/setObject

JDBC 4.2及更高版本开始,您的JDBC驱动程序可以通过调用以下方法直接与数据库交换java.time对象:

例如:

myPreparedStatement.setObject( … , instant ) ;

...和...

Instant instant = myResultSet.getObject( … , Instant.class ) ;

将传统代码转换为现代代码

如果必须与尚未更新到java.time的旧代码进行接口交互,则可以使用添加到旧类中的新方法进行来回转换。

Instant instant = myJavaSqlTimestamp.toInstant() ;  // Going from legacy class to modern class.

…and…

java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( instant ) ;  // Going from modern class to legacy class.

关于 java.time

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

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

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

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

如何获取 java.time 类?

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


顺便提一下:在最新的JDBC规范[版本4.3](https://download.oracle.com/otn-pub/jcp/jdbc-4_3-mrel3-eval-spec/jdbc4.3-fr-spec.pdf?AuthParam=1649214958_4adf759a80a29816780b1ff20d1d6677)中,在“附录B”中介绍“数据类型转换表”时没有提到`java.time.Instant`。例如,PostgreSQL JDBC驱动程序文档明确指出不支持java.time.Instant - informatik01
[续] 此外,最新的MySQL JDBC驱动程序文档在相关的“Java - MySQL”类型映射页面上也没有提到Instant - informatik01
[续] 我正在实现一个DAO方法,最初想使用Instant作为Spring的NamedParameterJdbcTemplate的参数,但是在使用它时没有返回结果(使用最新的MySQL数据库和驱动程序)。与完美运行的LocalDateTime不同。无论如何,我们有一个全局约定,将所有日期和时间值存储在UTC中,因此我最终使用LocalDateTime查询数据(即将其作为参数传递给NamedParameterJdbcTemplate)- 它可以毫无问题地完成工作。 - informatik01
所有这些只是为了获取日期和时间...希望在我死之前变得简单。每当有人在Java中询问日期时,就会出现世界末日。 - Magno C
那么...如果有人想默认获取UTC时间,然后将其转换为其时区,这样做的目的是什么呢?如果我只想要将我的时区的Instant转换为UTC,那么更直观。代码更少。我认为这个类是由一个住在格林威治的人编写的。 - Magno C
@MagnoC 并不是很复杂。(a) 如果您想要在特定时区中看到当前时刻:ZonedDateTime.now( ZoneId.of( "America/Sao_Paulo" ) )。(b) UTC 时间(零小时-分钟-秒的偏移量)通常应用于日志记录、调试、数据存储、数据交换和大多数业务逻辑,因为它是精确、明确且不受任性政治家的控制。Instant.now() - Basil Bourque

7

java.time.LocalDate对象调用asStartOfDay()函数会返回一个java.time.LocalDateTime对象。


实际上应该使用 LocalDate.atStartOfDay() 而不是 asStartOfDay() - sxc731

5

根据您所处的时区,您可能会损失几分钟(1650-01-01 00:00:00 可能会变为 1649-12-31 23:52:58)。

使用以下代码可避免这种情况:

new Timestamp(localDateTime.getYear() - 1900, localDateTime.getMonthOfYear() - 1, localDateTime.getDayOfMonth(), localDateTime.getHourOfDay(), localDateTime.getMinuteOfHour(), localDateTime.getSecondOfMinute(), fractional);

3
自从Joda开始消失后,有人想在Java 8中将LocalDate转换为LocalDateTime。在Java 8的LocalDateTime中,可以使用LocalDateLocalTime创建一个LocalDateTime实例。点击此处查看更多信息。

public static LocalDateTime of(LocalDate date, LocalTime time)

示例:

    // just to create a sample LocalDate
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
    LocalDate ld = LocalDate.parse("20180306", dtf);

    // convert ld into a LocalDateTime
    // We need to specify the LocalTime component here as well, it can be any accepted value
    LocalDateTime ldt = LocalDateTime.of(ld, LocalTime.of(0,0)); // 2018-03-06T00:00

仅供参考,可以使用以下方法获取时代秒数:

    ZoneId zoneId = ZoneId.systemDefault();
    long epoch = ldt.atZone(zoneId).toEpochSecond(); 

    // If you only care about UTC
    long epochUTC = ldt.toEpochSecond(ZoneOffset.UTC);

不需要在这里使用LocalDateTime。该类故意缺乏任何时区或UTC偏移量的概念,因此在这里没有任何有用的目的。相反:LocalDate.parse(“20180306”,DateTimeFormatter.BASIC_ISO_DATE)。atStartOfDay(ZoneId.of(“Africa / Tunis”).toEpochSecond() 不要假设一天从00:00开始,情况并非总是如此。 - Basil Bourque
请问您的意思是不要假设一天从00:00开始吗? - prime
1
异常情况,例如夏令时(DST),意味着在某些地方的某些日期,一天可能会在另一个时间开始,例如01:00:00。因此,不要假设,让java.time通过调用java.time.LocalDate.atStartOfDay来确定第一个时刻,以获取ZonedDateTime。请参见我的答案中的示例。正如我所评论的,LocalDateTime类在所有这些方面都是无用和逆生产的。 - Basil Bourque
所以使用LocalDateTime时,我们必须使用ZoneId吗? - prime
我认为你没有理解我的观点。请重新阅读我在这个页面上的回答。在Stack Overflow上搜索,从更多的例子和讨论中学习更多。但是我在这里最后再说一次:LocalDateTime对于这个问题来说是不合适的。LocalDateTime 不代表 一个时刻。**LocalDateTime与任何特定的地区或时区都没有任何关系**,尽管名称可能会在第一眼看起来暗示另外的意思-了解更多关于这个类的意图和细节。LocalDateTime类确实有其用途,但不是在这个问题中所看到的用途。 - Basil Bourque
这正是我在寻找的东西。仅仅将LocalDateTime转换为带有00:00:00时间部分的LocalTime。太好了!而且它回答了OP的问题,没有“不要使用它”、“使用时区”等噪音。如果有人想要使用LocalDateTime,那是因为确实需要和想要使用。 - Fer B.

0

Java8 +

import java.time.Instant;
Instant.now().getEpochSecond(); //timestamp in seconds format (int)
Instant.now().toEpochMilli(); // timestamp in milliseconds format (long)

1
有用,但似乎并没有真正回答问题。 - Ole V.V.
谢谢你的代码,但它与问题无关。 - refaelio

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