使用错误的类
LocalDateTime ldt = LocalDateTime.ofInstant(Instnant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);
该代码有两个问题。
首先,切勿将现代 java.time 类(此处为LocalDateTime)与可怕的旧旧日期时间类(此处为java.sql.Timestamp)混用。从采用JSR 310 开始,java.time 框架完全取代了糟糕的旧类。您再也不需要使用Timestamp:从JDBC 4.2 开始,我们可以直接与数据库交换java.time 对象。
另一个问题是LocalDateTime 类,根据定义,不能代表片刻。它故意缺少时区或与 UTC 的偏移量。 LocalDateTime 仅当您的意思是日期与时间everywhere 或anywhere,换句话说,任何/所有更多的时刻在大约的范围内26-27 小时(全球当前的极端时区)。
不要使用LocalDateTime 表示特定时刻,时间轴上的特定点。而是使用:
然后我尝试创建 Timestamp 对象
不要。
永远不要使用java.sql.Timestamp。替换为java.time.Instant。继续阅读以了解更多信息。
当前时刻
要捕捉 UTC 中的当前时刻,请使用以下任一方法:
Instant instant = Instant.now() ;
……或者……
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
两者都代表相同的事物,即 UTC 中的一个时刻。
数据库
这里有一些示例 SQL 和用于将当前时刻传递到数据库的 Java 代码。
该示例使用 Java 内置的 H2 Database Engine。
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
这是一个使用该代码的完整示例应用程序。
package com.basilbourque.example;
import java.sql.*;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
public class MomentIntoDatabase {
public static void main ( String[] args ) {
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
}
private void doIt ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ (\n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
" name_ VARCHAR NOT NULL ,\n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULL\n" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; ) {
while ( rs.next() ) {
//Retrieve by column name
UUID id = ( UUID ) rs.getObject( "id_" ); // Cast the `Object` object to UUID if your driver does not support JDBC 4.2 and its ability to pass the expected return type for type-safety.
String name = rs.getString( "name_" );
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
//Display values
System.out.println( "id: " + id + " | name: " + name + " | when: " + odt );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
解析字符串
关于 Melnyk 的相关评论,这里是基于上面示例代码的另一个示例。此代码不是捕获当前时刻,而是解析一个字符串。
输入字符串缺少time zone 或offset-from-UTC 的任何指示符。所以我们将其解析为LocalDateTime,请记住,这不代表一个时刻,不是时间轴上的一个点。
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
ldt.toString(): 2018-11-22T00:00
但我们已被告知该字符串旨在表示 UTC 中的一个时刻,但发件人搞砸了并且未能包含该信息(例如 Z 或 +00:00 在末尾表示 UTC)。因此,我们可以应用 0 小时分秒的 UTC 偏移量来确定实际时刻,即时间线上的特定点。结果为OffsetDateTime 对象。
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
odt.toString(): 2018-11-22T00:00Z
末尾的Z 表示UTC,发音为“Zulu”。在 ISO 8601 标准中定义。
现在我们有时间了,我们可以在 SQL 标准类型 TIMESTAMP WITH TIME ZONE 的列中将它发送到数据库。
preparedStatement.setObject( 2 , odt );
然后检索存储的值。
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
2018-11-22T00:00Z
这是此示例应用程序的完整内容。
package com.basilbourque.example;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
public class MomentIntoDatabase {
public static void main ( String[] args ) {
MomentIntoDatabase app = new MomentIntoDatabase();
app.doIt();
}
private void doIt ( ) {
try {
Class.forName( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
e.printStackTrace();
}
try (
Connection conn = DriverManager.getConnection( "jdbc:h2:mem:moment_into_db_example_" ) ;
Statement stmt = conn.createStatement() ;
) {
String sql = "CREATE TABLE event_ (\n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
" name_ VARCHAR NOT NULL ,\n" +
" when_ TIMESTAMP WITH TIME ZONE NOT NULL\n" +
") ; ";
System.out.println( sql );
stmt.execute( sql );
// Insert row.
sql = "INSERT INTO event_ ( name_ , when_ ) " + "VALUES ( ? , ? ) ;";
try ( PreparedStatement preparedStatement = conn.prepareStatement( sql ) ; ) {
String name = "whatever";
String input = "22.11.2018 00:00:00";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , f );
System.out.println( "ldt.toString(): " + ldt );
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
System.out.println( "odt.toString(): " + odt );
preparedStatement.setString( 1 , name );
preparedStatement.setObject( 2 , odt );
preparedStatement.executeUpdate();
}
// Query all.
sql = "SELECT * FROM event_ ;";
try ( ResultSet rs = stmt.executeQuery( sql ) ; ) {
while ( rs.next() ) {
//Retrieve by column name
UUID id = ( UUID ) rs.getObject( "id_" ); // Cast the `Object` object to UUID if your driver does not support JDBC 4.2 and its ability to pass the expected return type for type-safety.
String name = rs.getString( "name_" );
OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );
//Display values
System.out.println( "id: " + id + " | name: " + name + " | when: " + odt );
}
}
} catch ( SQLException e ) {
e.printStackTrace();
}
}
}
转换
如果您必须与 java.time 尚未更新的旧代码进行互操作,则可以来回转换。查看添加到旧类的新方法 to…/from…。
要获取旧版 java.sql.Timestamp 对象,请调用 Timestamp.from( Instant )。要从上面看到的OffsetDateTime 对象中获取Instant,只需调用OffsetDateTime::toInstant。
java.sql.Timestamp ts = Timestamp.from( odt.toInstant() ) ;
往另一个方向发展。
OffsetDateTime odt = OffsetDateTime.ofInstant( ts.toInstant() , ZoneOffset.UTC ) ;
如果将 ThreeTen-Backport 库用于 Java 6 和 7 项目,请查看 DateTimeUtils 类以了解 to…/from… 转换方法。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time。
从哪里获取 java.time 类?