Java - 在没有区域偏移的情况下将 java.time.Instant 转换为 java.sql.Timestamp

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42766674/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:22:39  来源:igfitidea点击:

Java - Convert java.time.Instant to java.sql.Timestamp without Zone offset

javadatetimetimestampjava.time.instant

提问by Aleydin Karaimin

In the application I am developing, I need to convert java.time.Instantobject to java.sql.Timestamp. When I create Instantobject like:

在我正在开发的应用程序中,我需要将java.time.Instant对象转换为java.sql.Timestamp. 当我创建Instant对象时:

Instant now = Instant.now();

I receive something like 2017-03-13T14:28:59.970Z. And when I try to create Timestampobject like this:

我收到类似2017-03-13T14:28:59.970Z. 当我尝试创建这样的Timestamp对象时:

Timestamp current = Timestamp.from(now);

I receive something like 2017-03-13T16:28:59.970Z. The same result but with an additional 2 hour delay. Can someone explain why this is happening and provide me with an answer to fix this problem without this delay?

我收到类似2017-03-13T16:28:59.970Z. 相同的结果,但额外延迟了 2 小时。有人可以解释为什么会发生这种情况,并为我提供解决此问题的答案,而不会延迟吗?

When I created like this:

当我这样创建时:

LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);

Everything works well. But I try to avoid conversions. Is there a way to do this by only using Instantobject ?

一切正常。但我尽量避免转换。有没有办法只使用Instantobject来做到这一点?

采纳答案by Ole V.V.

I changed my computer's time zone to Europe/Bucharest for an experiment. This is UTC + 2 hours like your time zone.

为了进行实验,我将计算机的时区更改为 Europe/Bucharest。这是 UTC + 2 小时,就像您的时区一样。

Now when I copy your code I get a result similar to yours:

现在,当我复制您的代码时,得到与您类似的结果:

    Instant now = Instant.now();
    System.out.println(now); // prints 2017-03-14T06:16:32.621Z
    Timestamp current = Timestamp.from(now);
    System.out.println(current); // 2017-03-14 08:16:32.621

Output is given in comments. However, I go on:

输出在注释中给出。但是,我继续说:

    DateFormat df = DateFormat.getDateTimeInstance();
    df.setTimeZone(TimeZone.getTimeZone("UTC"));
    // the following prints: Timestamp in UTC: 14-03-2017 06:16:32
    System.out.println("Timestamp in UTC: " + df.format(current));

Now you can see that the Timestampreally agrees with the Instantwe started out from (only the milliseconds are not printed, but I trust they are in there too). So you have done everything correctly and only got confused because when we printed the Timestampwe were implicitly calling its toStringmethod, and this method in turn grabs the computer's time zone setting and displays the time in this zone. Only because of this, the displays are different.

现在你可以看到Timestamp真的与Instant我们开始的一致(只有毫秒没有打印,但我相信它们也在那里)。所以你已经正确地完成了所有事情,只是感到困惑,因为当我们打印时,Timestamp我们隐含地调用了它的toString方法,而这个方法反过来获取计算机的时区设置并显示该时区中的时间。正因为如此,显示方式不同。

The other thing you attempted, using LocalDateTime, appears to work, but it really does not give you what you want:

您尝试使用的另一件事LocalDateTime似乎有效,但它确实没有给您想要的东西:

    LocalDateTime ldt = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC);
    System.out.println(ldt); // 2017-03-14T06:16:32.819
    current = Timestamp.valueOf(ldt);
    System.out.println(current); // 2017-03-14 06:16:32.819
    System.out.println("Timestamp in UTC: " + df.format(current)); // 14-03-2017 04:16:32

Now when we print the Timestampusing our UTC DateFormat, we can see that it is 2 hours too early, 04:16:32 UTC when the Instantis 06:16:32 UTC. So this method is deceiving, it looks like it's working, but it doesn't.

现在,当我们Timestamp使用我们的 UTC打印时DateFormat,我们可以看到它早了 2 小时,即 06:16:32 UTC 时Instant04:16:32 UTC。所以这个方法是骗人的,看起来有效,但实际上并没有。

This shows the trouble that lead to the design of the Java 8 date and time classes to replace the old ones. So the real and good solution to your problem would probably be to get yourself a JDBC 4.2 driver that can accept an Instantobject readily so you can avoid converting to Timestampaltogether. I don't know if that's available for you just yet, but I'm convinced it will be.

这显示了导致设计 Java 8 日期和时间类以替换旧的类的麻烦。因此,解决您的问题的真正和好的解决方案可能是让自己拥有一个可以轻松接受Instant对象的 JDBC 4.2 驱动程序,这样您就可以避免Timestamp完全转换为。我不知道这是否适合您,但我相信它会。

回答by DazstaV3

If you want the current timestamp why not use the following function, I have used this in various projects and works perfectly:

如果您想要当前时间戳,为什么不使用以下功能,我已经在各种项目中使用了它并且运行良好:

public static Timestamp getTimeStamp()
{
    // Calendar information
    Calendar calendar       = Calendar.getInstance();
    java.util.Date now      = calendar.getTime();
    Timestamp dbStamp       = new Timestamp(now.getTime());
    return dbStamp;
}   

Example:

例子:

System.out.println( getTimeStamp() );

Output: 2017-03-13 15:01:34.027

输出:2017-03-13 15:01:34.027

EDIT

编辑

Using Java 8 LocalDateTime:

使用 Java 8 LocalDateTime:

public static Timestamp getTimeStamp()
{
    return Timestamp.valueOf(LocalDateTime.now());
}   

回答by Taras Melnyk

During saving a record to SQL Server DB I faced with the same problem. I've used java.sql.Timestamp.valueOf(String s)to get Timestampin UTC:

在将记录保存到 SQL Server DB 期间,我遇到了同样的问题。我曾经 在UTC 中java.sql.Timestamp.valueOf(String s)获取时间戳

import java.time.Instant; import java.time.format.DateTimeFormatter; .... .... DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(UTC); String dateTime = dateTimeFormatter.format(Instant date); Timestamp timestamp = Timestamp.valueOf(dateTime);

import java.time.Instant; import java.time.format.DateTimeFormatter; .... .... DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(UTC); String dateTime = dateTimeFormatter.format(Instant date); Timestamp timestamp = Timestamp.valueOf(dateTime);

It works for me.

这个对我有用。

回答by Basil Bourque

Wrong classes to use

使用错误的类

LocalDateTime ldt = LocalDateTime.ofInstant(Instnant.now(), ZoneOffset.UTC);
Timestamp current = Timestamp.valueOf(ldt);

Two problems with that code.

该代码有两个问题。

Firstly, never mix the modern java.timeclasses (LocalDateTimehere) with the terrible old legacy date-time classes (java.sql.Timestamphere). The java.timeframework supplants entirely the terrible old classes, as of the adoption of JSR 310. You need never use Timestampagain: As of JDBC 4.2we can directly exchange java.timeobjects with the database.

首先,永远不要将现代java.time类(LocalDateTime此处)与糟糕的旧式日期时间类(java.sql.Timestamp此处)混合在一起。该java.time框架完全取代了旧的可怕类,采用的JSR 310。您再也不需要使用Timestamp了:从JDBC 4.2 开始,我们可以直接与数据库交换java.time对象。

The other problem, is that the LocalDateTimeclass cannot, by definition, represent a moment. It purposely lacks a time zone or offset-from-UTC. Use LocalDateTimeonly when you mean a date with time-of-day everywhereor anywhere, in other words, any/all of many more moments across a range of about 26-27 hours (the current extremes of time zones around the globe).

另一个问题是LocalDateTime,根据定义,类不能代表片刻。它故意缺少时区或 UTC 偏移量。LocalDateTime仅当您的意思是任何地方任何地方都有时间的日期时才使用,换句话说,在大约 26-27 小时(当前全球时区的当前极端)范围内的任何/所有更多时刻。

Do notuse LocalDateTimewhen you mean a specific moment, a specific point on the timeline. Instead use:

千万不能使用 LocalDateTime时,你的意思是一个特定的时刻,在时间轴上的特定点。而是使用:

Then I try to create Timestamp object

然后我尝试创建 Timestamp 对象

Don't.

别。

Never use java.sql.Timestamp. Replaced by java.time.Instant. Read on for more info.

永远不要使用java.sql.Timestamp. 替换为java.time.Instant。继续阅读以获取更多信息。

Table of date-time types in Java (both modern and legacy) and in standard SQL.

Table of date-time types in Java (both modern and legacy) and in standard SQL.

Current moment

当前时刻

To capture the current moment in UTC use either of these:

要捕获 UTC 中的当前时刻,请使用以下任一方法:

Instant instant = Instant.now() ;

…or…

…或者…

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC );

The both represent the very same thing, a moment in UTC.

两者都代表相同的事物,UTC 时间。

Database

数据库

Here is some example SQLand the Java code to pass the current moment into the database.

下面是一些示例SQL和将当前时刻传递到数据库的 Java 代码。

The example uses the H2 Database Engine, built in Java.

该示例使用用 Java 构建的H2 数据库引擎

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();
}

Here is a complete example app using that code.

这是使用该代码的完整示例应用程序。

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();
        }
    }
}

Parsing string

解析字符串

Regarding a related comment by Melnyk, here is another example based on the example code above. Rather than capturing the current moment, this code parses a string.

关于 Melnyk 的相关评论,这里是基于上述示例代码的另一个示例。这段代码不是捕获当前时刻,而是解析一个字符串。

The input string lacks any indicator of time zoneor offset-from-UTC. So we parse as a LocalDateTime, keeping in mind that this does notrepresent a moment, is nota point on the timeline.

输入字符串缺少时区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

ldt.toString(): 2018-11-22T00:00

But we have been informed the string was meant to represent a moment in UTC, but the sender screwed up and failed to include that information (such as a Zor +00:00on the end to mean UTC). So we can apply an offset-from-UTC of zero hours-minutes-seconds to determine an actual moment, a specific point on the timeline. The result as a OffsetDateTimeobject.

但是我们已被告知该字符串旨在表示 UTC 中的某个时刻,但发件人搞砸了并且未能包含该信息(例如 aZ+00:00末尾表示 UTC)。因此,我们可以应用零时分秒的 UTC 偏移量来确定实际时刻,即时间线上的特定点。结果作为OffsetDateTime对象。

OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );

odt.toString(): 2018-11-22T00:00Z

odt.toString(): 2018-11-22T00:00Z

The Zon the end means UTC and is pronounced “Zulu”. Defined in ISO 8601 standard.

Z上月底表示UTC发音为“祖鲁”。在 ISO 8601 标准中定义。

Now that we have a moment in hand, we can send it to the database in a column of SQL-standard type TIMESTAMP WITH TIME ZONE.

现在我们有时间了,我们可以将它发送到 SQL 标准类型列中的数据库TIMESTAMP WITH TIME ZONE

preparedStatement.setObject( 2 , odt );

When then retrieve that stored value.

然后检索该存储的值。

 OffsetDateTime odt = rs.getObject( "when_" , OffsetDateTime.class );

2018-11-22T00:00Z

2018-11-22T00:00Z

Here is the complete for this example app.

这是此示例应用程序的完整内容。

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();
        }
    }
}

Converting

转换

If you must interoperate with old code not yet updated for java.time, you can convert back-and-forth. Look to new methods to…/from…added to the old classes.

如果必须与尚未为java.time更新的旧代码进行互操作,则可以来回转换。寻找新方法to…/from…添加到旧类中。

To get a legacy java.sql.Timestampobject, call Timestamp.from( Instant ). To get an Instantfrom our OffsetDateTimeobject seen above, simply call OffsetDateTime::toInstant.

要获取遗留java.sql.Timestamp对象,请调用Timestamp.from( Instant )。要从上面看到InstantOffsetDateTime对象中获取,只需调用OffsetDateTime::toInstant

java.sql.Timestamp ts = Timestamp.from( odt.toInstant() ) ;

Going the other direction.

走向另一个方向。

OffsetDateTime odt = OffsetDateTime.ofInstant( ts.toInstant() , ZoneOffset.UTC ) ;

If using the ThreeTen-Backportlibrary for Java 6 & 7 projects, look to the DateTimeUtilsclass for the to…/from…conversion methods.

如果将ThreeTen-Backport库用于 Java 6 和 7 项目,请查看/转换方法的DateTimeUtils类。to…from…



About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

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

You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*classes.

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

Where to obtain the java.time classes?

从哪里获得 java.time 类?

Table of which java.time library to use with which version of Java or Android

Table of which java.time library to use with which version of Java or Android

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多