简体   繁体   English

JDBC / Postgres如何将无时区的java.util.Date与时间戳进行比较?

[英]How does JDBC/Postgres compare a timezone-less java.util.Date with a Timestamp?

We have a Postgres table that has two TIMESTAMP WITHOUT TIME ZONE columns, prc_sta_dt and prc_end_dt. 我们有一个Postgres表,其中有两个TIMESTAMP WITHOUT TIME ZONE WITHTIME TIMESTAMP WITHOUT TIME ZONE列,即prc_sta_dt和prc_end_dt。 We check to see whether a java.util.Date falls in between the start and end dates. 我们检查一下java.util.Date是否在开始日期和结束日期之间。

Here is some of the Java code, which is simplified but gets the point across. 这是一些Java代码,虽然经过简化,但很重要。

// This format expects a String such as 2018-12-03T10:00:00
// With a date and a time, but no time zone

String timestamp = "2018-12-03T10:00:00";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date searchDate = formatter.parse(timestamp);

// Here's the Postgres query

String query = "select promotion_cd from promotions " + 
               "where prc_sta_dt <= :srch_dt and prc_end_dt >= :srch_dt";

Map<String, Object> map = new HashMap<String, Object>();
map.put("srch_dt", searchDate);

List<Promotion> promotions = jdbcTemplate.query(query, map, promotionMapper);

In our Postgres table, we have promotions that start at 9am on 12/3/2018 and end at 3pm on the same day. 在我们的Postgres表中,我们的促销活动于2018年12月3日上午9点开始,到当天的下午3点结束。 The prc_sta_dt and prc_end_dt colums in our database for these rows are 2018-12-03 09:00:00.0 and 2018-12-03 15:00:00.0 我们数据库中这些行的prc_sta_dt和prc_end_dt列为2018-12-03 09:00:00.02018-12-03 15:00:00.0

Question: When JDBC /Postgres accepts our searchDate and compares it to these timestamps, will it accept the given search date of 10am (2018-12-03T10:00:00) or will it treat this time as being under the time zone that the server is running on, and then convert that to UTC? 问题:JDBC / Postgres接受我们的searchDate并将其与这些时间戳进行比较时,它将接受给定的10am(2018-12-03T10:00:00)的搜索日期,还是将这段时间视为在服务器正在运行,然后将其转换为UTC?

For example, if the server is running in Chicago, then will it interpret 10 am as 10am CST and then convert that to 4pm UTC before doing the comparison in the database? 例如,如果服务器在芝加哥运行,那么它将在数据库中进行比较之前将上午10点解释为CST上午10点,然后将其转换为UTC下午4点吗? If so then we're out of luck! 如果是这样,那我们就不走运了!

I doubt this would happen, but I just want to make sure so there are no surprises. 我怀疑这种情况是否会发生,但我只是想确保没有意外。

Wrong data type, Date is not a date 数据类型错误, Date不是日期

A java.util.Date object represents a moment in UTC, a specific point on the timeline. 一个java.util.Date对象表示UTC,在时间轴上的特定时间点的时刻 So it represents the combination of a date, a time-of-day, and an offset-from-UTC of zero (for UTC itself). 因此,它表示日期,一天中的时间以及与UTC的偏移量为零(对于UTC本身)的组合。 Among the many poor design choices in this terrible class is its misleading name that has confused countless Java programmers. 在这个可怕的类中,许多糟糕的设计选择中都有一个令人误解的名称,它使无数的Java程序员感到困惑。

TIMESTAMP WITHOUT TIME ZONE

If you care about moments, then your database column should not be of type TIMESTAMP WITHOUT TIME ZONE . 如果你关心的时刻,那么你的数据库列应该是类型TIMESTAMP WITHOUT TIME ZONE That data type represents a date and a time-of-day without any concept of time zone or offset-from-UTC. 该数据类型表示日期和时间,没有任何时区或UTC偏移量的概念。 As such, by definition, that type cannot represent a moment, is not a point on the timeline. 因此,根据定义,该类型不能表示一个时刻, 也不是时间轴上的一点。 This type should only be used when you mean a date-with-time anywhere or everywhere . 仅当您在任何地方任何 地方表示带日期的日期时,才应使用此类型。

Examples: 例子:

  • “Christmas starts after stroke of midnight at beginning of December 25, 2018” where Christmas in Kiribati comes first, India later, and Africa even later. “圣诞节于2018年12月25日午夜后开始”, 基里巴斯的圣诞节首先出现,印度随后是非洲,甚至更晚。
  • “Company-wide memo: Each of our factories in Delhi, Düsseldorf, and Detroit will close one hour early at 16:00 on January 21st” where 4 PM at each factory is three different moments, each several hours apart. “公司范围内的备忘录:我们在德里,杜塞尔多夫和底特律的每个工厂都将于1月21日16:00提前1个小时关闭”,其中每个工厂的下午4点是三个不同的时刻,每个时刻间隔几个小时。

TIMESTAMP WITH TIME ZONE

When tracking specific a specific moment, a single point on the timeline, use a column of type TIMESTAMP WITH TIME ZONE . 当跟踪特定的特定时刻(时间轴上的单个点)时,请使用TIMESTAMP WITH TIME ZONE类型的列。 In Postgres, such values are stored in UTC. 在Postgres中,这些值存储在UTC中。 Any time zone or offset info submitted with an input is used to adjust into UTC, then the zone/offset info is discarded. 使用输入提交的任何时区或偏移量信息都可用于调整为UTC,然后将时区/偏移量信息丢弃。

BEWARE: Some tools may have the well-intentioned but unfortunate anti-feature of injecting a time zone after retrieving the value in UTC, thereby misrepresenting what was actually stored. 注意:某些工具可能具有很好的意图,但不幸的是,它在检索UTC中的值后注入时区会产生反作用,从而错误地表示了实际存储的内容。

Comparing a moment to values of TIMESTAMP WITHOUT TIME ZONE 将时刻与TIMESTAMP WITHOUT TIME ZONE值进行比较

As for comparing a moment to values in your column of type TIMESTAMP WITHOUT TIME ZONE , doing so would generally not make sense . 至于将时刻与TIMESTAMP WITHOUT TIME ZONE类型的列中的值进行比较,这样做通常是没有意义的

But if you are clear-headed and educated about date-time handling, and making this comparison is sensible in your business logic, let's forge on. 但是,如果您对日期时间处理头脑清醒并受过良好教育,并且在您的业务逻辑中进行这种比较是明智的,那么让我们继续吧。

Wrong classes 班级错误

You are using lousy, terrible, awful date-time classes ( Date , SimpleDateFormat , etc.) that were supplanted years ago by the java.time classes. 您正在使用糟糕的,可怕的,糟糕的日期时间类( DateSimpleDateFormat等),这些类早在几年前就被java.time类所取代。 Do yourself a favor: Stop using the legacy date-time classes. 帮自己一个忙:停止使用旧的日期时间类。 Use only java.time . 仅使用java.time

If given a moment as a java.util.Date , use the new methods added to the old classes to convert. 如果给定时间为java.util.Date ,请使用添加到旧类中的新方法进行转换。 In particular, java.util.Date is replaced by Instant . 特别是, java.util.Date替换为Instant

Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy class to modern class.

Specify the time zone in which you want to adjust your Instant moment in UTC for comparison. 指定您要在UTC中调整Instant时刻以进行比较的时区。 For example, if your database was built by someone who did not understand proper date-time handling, and has been using the TIMESTAMP WITHOUT TIME ZONE column to store date-with-time values that were taken from the wall-clock time of Québec, then use the time zone America/Montreal . 例如,如果您的数据库是由不了解正确的日期时间处理方式的人建立的,并且一直在使用TIMESTAMP WITHOUT TIME ZONE列来存储从魁北克的壁钟时间获取的日期-时间值,然后使用时区America/Montreal

Specify a proper time zone name in the format of continent/region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . continent/region的格式指定正确的时区名称 ,例如America/MontrealAfrica/CasablancaPacific/Auckland Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!). 切勿使用2-4字母的缩写,例如ESTIST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;

Apply that zone to our Instant to get a ZonedDateTime object. 将该区域应用于我们的Instant以获得ZonedDateTime对象。

ZonedDateTime zdt = instant.atZone( z ) ;

Our resulting ZonedDateTime object represents the same moment as the Instant object, same point on the timeline, but viewed with a different wall-clock time. 我们生成的ZonedDateTime对象表示与Instant对象相同的时刻,时间轴上的同一点,但是使用不同的挂钟时间查看。

To hammer a square-peg into a round-hole , let's convert that ZonedDateTime object to a LocalDateTime object, thereby stripping away the time zone information and leaving only a date-with-time-of-day value. 要将方形钉锤成一个圆孔 ,让我们将该ZonedDateTime对象转换为LocalDateTime对象,从而除去时区信息,仅保留带日期的日期值。

LocalDateTime ldt = zdt.toLocalDateTime() ;

Half-Open 半开

where prc_sta_dt <= :srch_dt and prc_end_dt >= :srch_dt

That logic is prone to failure. 这种逻辑很容易失败。 Generally, the best practice in date-time handling when defining a span-of-time to use Half-Open, where the beginning is inclusive while the ending is exclusive . 通常,定义时间跨度以使用Half-Open时,日期时间处理的最佳做法是,其中开始是包括在内的,而结尾是排除的

So use this: 所以使用这个:

WHERE instant >= start_col AND instant < stop_col ;

For a PreparedStatement , we would have placeholders. 对于PreparedStatement ,我们将有占位符。

WHERE ? >= start_col AND ? < stop_col ;

On the Java side, as of JDBC 4.2 we can directly exchange java.time objects with the database via getObject and setObject methods. 在Java方面,从JDBC 4.2开始,我们可以通过getObjectsetObject方法直接与数据库交换java.time对象。

You might be able to pass an Instant depending on your JDBC driver. 根据您的JDBC驱动程序,您也许可以传递Instant Support for Instant is not required by the JDBC spec. JDBC规范不需要对Instant支持。 So try it, or read the doc for your driver. 因此,请尝试一下,或阅读驱动程序的文档。

myPreparedStatement.setObject( 1 , instant ) ;
myPreparedStatement.setObject( 2 , instant ) ;

If Instant is not supported, convert from Instant to an OffsetDateTime set to UTC. 如果不支持Instant ,请从Instant转换为设置为UTC的OffsetDateTime Support for OffsetDateTime is required by the spec. 支持OffsetDateTime 规范所要求的。

myPreparedStatement.setObject( 1 , instant.atOffset( ZoneOffset.UTC ) ) ;
myPreparedStatement.setObject( 2 , instant.atOffset( ZoneOffset.UTC ) ) ;

Retrieval. 恢复。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Always specify time zone 始终指定时区

For example, if the server is running in Chicago, then will it interpret 10 am as 10am CST and then convert that to 4pm UTC before doing the comparison in the database? 例如,如果服务器在芝加哥运行,那么它将在数据库中进行比较之前将上午10点解释为CST上午10点,然后将其转换为UTC下午4点吗?

A programmer should never depend on the time zone (or locale, by the way) currently set as the default on the host OS or JVM. 程序员切勿依赖当前在主机OS或JVM上设置为默认值的时区(或地区)。 Both are out of your control. 两者都无法控制。 And both can change at any moment during runtime! 而且两者可以同时运行过程中随时改变!

Always specify the time zone by passing the optional argument to various date-time methods. 始终通过将可选参数传递给各种日期时间方法来指定时区。 Making those optional was a design flaw in java.time in my opinion, as programmers all too often ignore the issue of time zone, at their own peril. 在我看来,将这些设置为可选是java.time的一个设计缺陷,因为程序员常常无视自己的时区问题。 But that is one of very few design flaws in an amazingly useful and elegant framework. 但这是一个非常有用且优雅的框架中极少数设计缺陷之一。

Notice in our code above we specified the desired/expected time zone. 请注意,在上面的代码中,我们指定了所需/预期的时区。 The current default time zone of our host OS, our Postgres database connection, and our JVM will not alter the behavior of our code. 主机操作系统,Postgres数据库连接和JVM的当前默认时区不会改变代码的行为。

Current moment 当前时刻

If you want the current moment use any of these: 如果您需要当前时间,请使用以下任一方法:

  • Instant.now()
    Always in UTC, by definition. 根据定义,始终使用UTC。
  • OffsetDateTime.now( someZoneOffset )
    Current moment as seen in the wall-clock time of a particular offset-from-UTC. 从特定UTC偏移量的挂钟时间中看到的当前时刻。
  • ZonedDateTime.now( someZoneId )
    Current moment as seen in the wall-clock time used by the people living in a particular region. 从居住在特定区域的人们使用的挂钟时间中可以看到的当前时刻。

Java 7 and ThreeTen-Backport Java 7和ThreeTen-Backport

If you are using Java 7, then you have no java.time classes built-in. 如果使用Java 7,则没有内置的java.time类。 Fortunately, the inventor of JSR 310 and java.time , Stephen Colebourne, also led the ThreeTen-Backport project to produce a library providing most of the java.time functionality to Java 6 & 7. 幸运的是, JSR 310java.time的发明者Stephen Colebourne还领导了ThreeTen-Backport项目,以产生一个提供Java 6和7大部分java.time功能的库。

Here is a complete example app in a single .java file showing the use of back-port in Java 7 with the H2 Database Engine . 这是单个.java文件中的完整示例应用程序,显示了Java 7中H2数据库引擎对back-port的使用。

In Java 7 , JDBC 4.2 is not available, so we cannot directly use the modern classes. Java 7中JDBC 4.2不可用,因此我们不能直接使用现代类。 We fall back to using java.sql.Timestamp which actually represents a moment in UTC, but which H2 stores into a column of TIMESTAMP WITHOUT TIME ZONE taking the date and the time-of-day as-is (using the wall-clock time of UTC) while ignoring the UTC aspect. 我们回过头来使用java.sql.Timestamp ,它实际上代表UTC的一个时刻,但是H2会将日期和时间按原样(使用壁钟时间)存储到TIMESTAMP WITHOUT TIME ZONE的列中而不考虑UTC方面。 I have not tried this in Postgres, but I expect you will see the same behavior. 我没有在Postgres中尝试过此方法,但是我希望您会看到相同的行为。

package com.basilbourque.example;

import java.sql.*;

import org.threeten.bp.*;

public class App {
    static final public String databaseConnectionString = "jdbc:h2:mem:localdatetime_example;DB_CLOSE_DELAY=-1";  // The `DB_CLOSE_DELAY=-1` keeps the in-memory database around for multiple connections.

    public static void main ( String[] args ) {
        App app = new App();
        app.doIt();
    }

    private void doIt () {
        System.out.println( "Bonjour tout le monde!" );

//        java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
//        System.out.println( ts );

        this.makeDatabase();

        java.util.Date d = new java.util.Date(); // Capture the current moment using terrible old date-time class that is now legacy, supplanted years ago by the class `java.time.Instant`.
        this.fetchRowsContainingMoment( d );
    }

    private void makeDatabase () {
        try {
            Class.forName( "org.h2.Driver" );
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
        }

        try (
            Connection conn = DriverManager.getConnection( databaseConnectionString ) ;  // The `mem` means “In-Memory”, as in “Not persisted to disk”, good for a demo.
            Statement stmt = conn.createStatement() ;
        ) {
            String sql = "CREATE TABLE event_ ( \n" +
                             "  pkey_ IDENTITY NOT NULL PRIMARY KEY , \n" +
                             "  name_ VARCHAR NOT NULL , \n" +
                             "  start_ TIMESTAMP WITHOUT TIME ZONE NOT NULL , \n" +
                             "  stop_ TIMESTAMP WITHOUT TIME ZONE NOT NULL \n" +
                             ");";
            stmt.execute( sql );

            // Insert row.
            sql = "INSERT INTO event_ ( name_ , start_ , stop_ ) VALUES ( ? , ? , ? ) ;";
            try (
                PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
            ) {
                preparedStatement.setObject( 1 , "Alpha" );
                // We have to “fake it until we make it”, using a `java.sql.Timestamp` with its value in UTC while pretending it is not in a zone or offset.
                // The legacy date-time classes lack a way to represent a date with time-of-day without any time zone or offset-from-UTC.
                // The legacy classes have no counterpart to `TIMESTAMP WITHOUT TIME ZONE` in SQL, and have no counterpart to `java.time.LocalDateTime` in Java.
                preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 2 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.executeUpdate();

                preparedStatement.setString( 1 , "Beta" );
                preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 4 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 5 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.executeUpdate();

                preparedStatement.setString( 1 , "Gamma" );
                preparedStatement.setObject( 2 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 11 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.setObject( 3 , DateTimeUtils.toSqlTimestamp( ZonedDateTime.of( 2018 , 12 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() ) );
                preparedStatement.executeUpdate();
            }
        } catch ( SQLException e ) {
            e.printStackTrace();
        }
    }

    private void fetchRowsContainingMoment ( java.util.Date moment ) {
        // Immediately convert the legacy class `java.util.Date` to a modern `java.time.Instant`.
        Instant instant = DateTimeUtils.toInstant( moment );
        System.out.println( "instant.toString(): " + instant );
        String sql = "SELECT * FROM event_ WHERE ? >= start_ AND ? < stop_ ORDER BY start_ ;";

        try (
            Connection conn = DriverManager.getConnection( databaseConnectionString ) ;
            PreparedStatement pstmt = conn.prepareStatement( sql ) ;
        ) {
            java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( instant );
            pstmt.setTimestamp( 1 , ts );
            pstmt.setTimestamp( 2 , ts );

            try ( ResultSet rs = pstmt.executeQuery() ; ) {
                while ( rs.next() ) {
                    //Retrieve by column name
                    Integer pkey = rs.getInt( "pkey_" );
                    String name = rs.getString( "name_" );
                    java.sql.Timestamp start = rs.getTimestamp( "start_" );
                    java.sql.Timestamp stop = rs.getTimestamp( "stop_" );

                    // Instantiate a `Course` object for this data.
                    System.out.println( "Event pkey: " + pkey + " | name: " + name + " | start: " + start + " | stop: " + stop );
                }
            }
        } catch ( SQLException e ) {
            e.printStackTrace();
        }
    }
}

When run. 运行时。

instant.toString(): 2018-12-04T05:06:02.573Z Instant.toString():2018-12-04T05:06:02.573Z

Event pkey: 3 | 事件pkey:3 | name: Gamma | 名称:Gamma | start: 2018-11-23 16:30:00.0 | 开始:2018-11-23 16:30:00.0 | stop: 2018-12-23 16:30:00.0 停止:2018-12-23 16:30:00.0

Java 8 without ThreeTen-Backport 没有ThreeTen-Backport的 Java 8

And here is that same example, conceptually, but in Java 8 or later where we can use the java.time classes built-in without the ThreeTen-Backport library. 从概念上讲,这里是相同的示例,但是在Java 8或更高版本中,我们可以使用内置的java.time类,而无需ThreeTen-Backport库。

package com.basilbourque.example;

import java.sql.*;

import java.time.*;

public class App {
    static final public String databaseConnectionString = "jdbc:h2:mem:localdatetime_example;DB_CLOSE_DELAY=-1";  // The `DB_CLOSE_DELAY=-1` keeps the in-memory database around for multiple connections.

    public static void main ( String[] args ) {
        App app = new App();
        app.doIt();
    }

    private void doIt ( ) {
        System.out.println( "Bonjour tout le monde!" );

        this.makeDatabase();

        java.util.Date d = new java.util.Date(); // Capture the current moment using terrible old date-time class that is now legacy, supplanted years ago by the class `java.time.Instant`.
        this.fetchRowsContainingMoment( d );
    }

    private void makeDatabase ( ) {
        try {
            Class.forName( "org.h2.Driver" );
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
        }

        try (
                Connection conn = DriverManager.getConnection( databaseConnectionString ) ;  // The `mem` means “In-Memory”, as in “Not persisted to disk”, good for a demo.
                Statement stmt = conn.createStatement() ;
        ) {
            String sql = "CREATE TABLE event_ ( \n" +
                    "  pkey_ IDENTITY NOT NULL PRIMARY KEY , \n" +
                    "  name_ VARCHAR NOT NULL , \n" +
                    "  start_ TIMESTAMP WITHOUT TIME ZONE NOT NULL , \n" +
                    "  stop_ TIMESTAMP WITHOUT TIME ZONE NOT NULL \n" +
                    ");";
            stmt.execute( sql );

            // Insert row.
            sql = "INSERT INTO event_ ( name_ , start_ , stop_ ) VALUES ( ? , ? , ? ) ;";
            try (
                    PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
            ) {
                preparedStatement.setObject( 1 , "Alpha" );
                // We have to “fake it until we make it”, using a `java.sql.Timestamp` with its value in UTC while pretending it is not in a zone or offset.
                // The legacy date-time classes lack a way to represent a date with time-of-day without any time zone or offset-from-UTC.
                // The legacy classes have no counterpart to `TIMESTAMP WITHOUT TIME ZONE` in SQL, and have no counterpart to `java.time.LocalDateTime` in Java.
                preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 1 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                ;
                preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 2 , 23 , 12 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                preparedStatement.executeUpdate();

                preparedStatement.setString( 1 , "Beta" );
                preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 4 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 5 , 23 , 14 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                preparedStatement.executeUpdate();

                preparedStatement.setString( 1 , "Gamma" );
                preparedStatement.setObject( 2 , ZonedDateTime.of( 2018 , 11 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                preparedStatement.setObject( 3 , ZonedDateTime.of( 2018 , 12 , 23 , 16 , 30 , 0 , 0 , ZoneId.of( "America/Montreal" ) ).toLocalDateTime() );
                preparedStatement.executeUpdate();
            }
        } catch ( SQLException e ) {
            e.printStackTrace();
        }
    }

    private void fetchRowsContainingMoment ( java.util.Date moment ) {
        // Immediately convert the legacy class `java.util.Date` to a modern `java.time.Instant`.
        Instant instant = moment.toInstant();
        System.out.println( "instant.toString(): " + instant );
        String sql = "SELECT * FROM event_ WHERE ? >= start_ AND ? < stop_ ORDER BY start_ ;";

        try (
                Connection conn = DriverManager.getConnection( databaseConnectionString ) ;
                PreparedStatement pstmt = conn.prepareStatement( sql ) ;
        ) {
            pstmt.setObject( 1 , instant );
            pstmt.setObject( 2 , instant );

            try ( ResultSet rs = pstmt.executeQuery() ; ) {
                while ( rs.next() ) {
                    //Retrieve by column name
                    Integer pkey = rs.getInt( "pkey_" );
                    String name = rs.getString( "name_" );
                    Instant start = rs.getObject( "start_" , OffsetDateTime.class ).toInstant();
                    Instant stop = rs.getObject( "stop_" , OffsetDateTime.class ).toInstant();

                    // Instantiate a `Course` object for this data.
                    System.out.println( "Event pkey: " + pkey + " | name: " + name + " | start: " + start + " | stop: " + stop );
                }
            }
        } catch ( SQLException e ) {
            e.printStackTrace();
        }
    }
}

When run. 运行时。

instant.toString(): 2018-12-04T05:10:54.635Z Instant.toString():2018-12-04T05:10:54.635Z

Event pkey: 3 | 事件pkey:3 | name: Gamma | 名称:Gamma | start: 2018-11-24T00:30:00Z | 开始:2018-11-24T00:30:00Z | stop: 2018-12-24T00:30:00Z 停止:2018-12-24T00:30:00Z


About java.time 关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于Java 8及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat . 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes. 现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial . 要了解更多信息,请参见Oracle教程 And search Stack Overflow for many examples and explanations. 并在Stack Overflow中搜索许多示例和说明。 Specification is JSR 310 . 规格为JSR 310

You may exchange java.time objects directly with your database. 您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later. 使用与JDBC 4.2或更高版本兼容的JDBC驱动程序 No need for strings, no need for java.sql.* classes. 不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes? 在哪里获取java.time类?

The ThreeTen-Extra project extends java.time with additional classes. ThreeTen-Extra项目使用其他类扩展了java.time。 This project is a proving ground for possible future additions to java.time. 该项目为将来可能在java.time中添加内容提供了一个试验场。 You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more . 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM