简而言之
myPreparedStatement.setObject(
… ,
LocalDate.parse( "20170909" , DateTimeFormatter.BASIC_ISO_DATE ) // Parse string into a date-only object, a `LocalDate` object.
.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) // Apply a time zone to determine the first moment of the day on that date, producing a `ZonedDateTime` object.
.toInstant() // Extract an `Instant` object, a moment always in UTC. This last step is not technically required, as your JDBC driver with Postgres should effectively do this on your behalf. But this line completes the demo conceptually.
) ;
避免使用旧类
其他答案已经过时,使用麻烦的旧日期时间类,这些类现在已被java.time类所取代。
java.time
如果您使用支持JDBC 4.2及更高版本的JDBC驱动程序,您可以与Postgres服务器交换java.time对象。无需使用java.sql日期时间类或可怕的Date
和Calendar
类。
持久化:
Instant instant = Instant.now() ;
myPreparedStatement.setObject( … , instant ) ;
...和检索:
Instant instant = myResultSet( … , Instant.class ) ;
在标准SQL中,
TIMESTAMP WITH TIME ZONE
是一个时刻,时间线上的特定点。换句话说,它包含日期、时间和时区信息。相比之下,如果只有日期而没有时间和时区信息,你可能想要使用该日期在特定时区的第一时刻。
要将仅包含日期的值转换为时刻,请首先创建一个
LocalDate
对象。
LocalDate ld = LocalDate.parse( "20170909" , DateTimeFormatter.BASIC_ISO_DATE ) ;
接下来,调用该
LocalDate
对象的
atStartOfDay
方法,并传递一个
ZoneId
。
时区是为了赋予日期意义而必需的。对于任何给定时刻,由于时区的不同,日期在全球范围内会有所变化。例如,在
法国巴黎午夜过后几分钟就是新的一天,而在
加拿大魁北克省蒙特利尔仍然是“昨天”。
请用continent/region
的格式指定一个正确的时区名称,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。永远不要使用三到四个字母的缩写,例如EST
或IST
,因为它们不是真正的时区,没有标准化,甚至也不是唯一的!
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
最后提取一个
Instant
。
Instant instant = zdt.toInstant() ;
在此时,您可以像上面讨论的那样将该Instant
插入数据库。
对象,而不是字符串
如果您学会了在Java中与数据库交换数据时以对象而不仅仅是字符串的方式思考,您的工作将更加容易。
TIMESTAMP WITH TIME ZONE
顺便提一下,请明确Postgres如何使用数据类型TIMESTAMP WITH TIME ZONE
。 SQL标准对此数据类型定义过于简要,而各个数据库在其实现方面也有所不同。以下描述适用于Postgres 8、9和10版本的行为。
使用带有时区或
UTC偏移量的任何日期时间值都会使用该时区/偏移量来确定
UTC中的值。然后将该UTC值存储在数据库中,分辨率为
微秒(而不是旧版Java
java.util.Date
和
Calendar
类的毫秒,也不是java.time类的
纳秒)。进行UTC调整后,区域/偏移信息将被丢弃。如果您关心原始的区域/偏移量,请将其明确地存储在单独的列中。
当从Postgres中检索日期时间值时,它以UTC形式发送。诸如psql或pgAdmin之类的工具可能会混淆地应用默认时区。虽然出于善意,这种特性会产生存储值带有时区的错觉,而实际上并不是这样。相比之下,符合JDBC 4.2及更高版本的JDBC驱动程序将自动处理UTC和时区。通常建议始终检索Instant
对象,如上面的代码所示。然后,如果需要,可以像上面的代码一样应用时区来实例化您自己的ZonedDateTime
或OffsetDateTime
对象。
关于java.time
java.time框架内置于Java 8及以上版本。这些类取代了麻烦的旧遗留日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于维护模式,建议迁移到java.time类。
要了解更多,请参阅
Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是
JSR 310。
在哪里获取java.time类?
ThreeTen-Extra 项目通过添加额外的类来扩展 java.time。该项目是 java.time 可能的未来补充的试验场。您可能会在这里找到一些有用的类,例如 Interval
, YearWeek
, YearQuarter
和 更多。