[英]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.0
和2018-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. 我怀疑这种情况是否会发生,但我只是想确保没有意外。
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: 例子:
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中的值后注入时区会产生反作用,从而错误地表示了实际存储的内容。
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. 但是,如果您对日期时间处理头脑清醒并受过良好教育,并且在您的业务逻辑中进行这种比较是明智的,那么让我们继续吧。
You are using lousy, terrible, awful date-time classes ( Date
, SimpleDateFormat
, etc.) that were supplanted years ago by the java.time classes. 您正在使用糟糕的,可怕的,糟糕的日期时间类( Date
, SimpleDateFormat
等),这些类早在几年前就被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/Montreal
, Africa/Casablanca
或Pacific/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字母的缩写,例如EST
或IST
因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。
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() ;
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开始,我们可以通过getObject
和setObject
方法直接与数据库交换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 ) ;
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的当前默认时区不会改变代码的行为。
If you want the current moment use any of these: 如果您需要当前时间,请使用以下任一方法:
Instant.now()
OffsetDateTime.now( someZoneOffset )
ZonedDateTime.now( someZoneId )
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 310和java.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
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
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.Date
, Calendar
和SimpleDateFormat
。
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 . 您可以在这里找到一些有用的类,比如Interval
, YearWeek
, YearQuarter
,和更多 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.