问题描述
假设我在Java应用程序中有一个类型为Instant
的字段,并将其以类型存储在PostgreSQL数据库中。
如果我救例如21.02.2019 14:03:59
它会被存储为21.02.2019
,但如何将它重新编译,当我从数据库中读取?
那将会:
-
21.02.2019 00:00:00.000
或 -
21.02.2019 23:59:59.000
?
PS。
我知道我应该改用Timestamp
:)
1楼
这取决于您的阅读方式。
Instant
和SQL DATE
是不等效的,而且我不知道任何可靠的库甚至可以让您将DATE
读取为Instant
。
如果您编写自己的代码允许这样做,那么即使没有任何意义,也可以使其以小时,分钟,秒和毫秒的方式填充,即使这没有任何意义(因此,对您问题的真正回答是“除非您明确编写了将java.sql.Date
转换为java.time.Instant
代码,否则都不会。
如果您正在设计将瞬间存储为日期的软件,则您的设计存在缺陷。
在Java端使用LocalDate
,或者在数据库中使用TIMESTAMP
。
2楼
TL;博士
-
符合SQL标准的数据库(例如Postgres)中的
DATE
列仅存储日期,时间和区域。 -
使用
LocalDate
作为仅日期的值,不带时间和区域。 -
要从
Instant
获取LocalDate
,请指定您要通过其确定日期的特定区域(时区)的人们所使用的挂钟时间。 -
如果要存储时刻(由
Instant
对象表示),请使用SQL标准类型TIMESTAMP WITH TIME ZONE
而不是DATE
。
细节
Instant
使用UTC
Instant
对象始终以UTC表示的时刻。
换句话说,与的为零小时-分钟-秒的时刻。
确定日期需要时区
因此,从Instant
获取日期需要一个 ( )。
23日说,巴黎午夜过后的一刻是一个新的约会日期,而魁北克蒙特利尔则是“昨天” 22日 。
在任何给定时刻,日期都会在全球范围内变化。
因此,如果您要说的是魁北克省的日期,请指定时区“美国/蒙特利尔”。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
您说您的数据库列的类型为 。 在Postgres中,就像SQL标准一样,它表示仅日期的值,没有日期和时区。
要仅存储日期,没有日期和时区,请提取LocalDate
。
“本地”一词是指任何一个地区或所有地区,但不是任何一个特定的地区。
LocalDate ld = zdt.toLocalDate() ;
JDBC 4.2
在准备好的语句中通过占位符交换该LocalDate
。
myPreparedStatement.setObject( … , ld ) ;
恢复:
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
保存日期,检索日期
从数据库读取后如何重新翻译?
那将会:
21.02.2019 00:00:00.000或…
这个问题没有道理。
当您从符合SQL标准的数据库(如Postgres)中读取DATE
,您将获得一个日期,一个日期,没有日期,也没有时区。
如果在DATE
列中存储值2019-02-21
的LocalDate
,则返回相同的值:值2019-02-21
的LocalDate
。
一天的第一刻
如果要在一天的第一时刻添加时间,请让java.time为您确定。 在某些时区的某些日期,日期不是从00:00:00开始,而是可以从01:00:00这样的时间开始。
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ; // Returns a `ZonedDateTime` object set to the first moment of the day as seen on that date in that time zone. May or may not be the time 00:00:00.
世界标准时间
如果要使用UTC作为时区从Instant
提取日期, Instant
从UTC偏移零小时-分钟-秒,请使用OffsetDateTime
类和常量 。
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
LocalDate ld = odt.toLocalDate() ;
myPreparedStatement.setObject( … , ld ) ;
关于java.time
框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的日期时间类,例如 , 和 。
要了解更多信息,请参见 。 并在Stack Overflow中搜索许多示例和说明。 规格为 。
现在处于的项目建议迁移到类。
您可以直接与数据库交换java.time对象。
使用与或更高版本兼容的 。
不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
-
, , , 和更高版本-标准Java API的一部分,具有捆绑的实现。
- Java 9添加了一些次要功能和修复。
-
和
- 大多数java.time功能都在 Backport中反向移植到Java 6和7。
-
- 更高版本的Android捆绑了java.time类的实现。
- 对于早期的Android(<26), 项目改编了 (如上所述)。 请参阅 。
项目使用其他类扩展了java.time。 该项目为将来可能在java.time中添加内容提供了一个试验场。 您可以在这里找到一些有用的类,比如 , , ,和 。