将java.util.Date转换为PostgreSQL的Time Zone时间戳

时间:2017-12-17 14:30:13

标签: java

我正在尝试在PostgreSQL数据库中插入日期。以下是我在java应用程序中解析日期的方法:

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
Date parsedTimeStamp = dateFormat.parse(dateString);

在我的PostgreSQL数据库中。我把它作为Timestamp with Time Zone。处理和插入它的理想方法是什么?

db期望2017-09-09 07:15:33.451061+00的示例。

3 个答案:

答案 0 :(得分:1)

您在此处使用的SimpleDateFormat将字符串转换为日期,在您的示例中,这必须表示您使用的格式为“yyyyMMdd” - 例如20171217(今天)。

要为数据库创建字符串,您需要采取相反的方法。 从日期对象到字符串。

您可以为此创建自己的格式化程序(使用您的示例)

    Date date = new Date();
    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss.SSSSSSX");
    String text = formatter.format(date);

您可以阅读有关格式here

的更多信息

答案 1 :(得分:1)

TL;博士

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日期时间类,也不需要可怕的DateCalendar类。

迁延性:

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方法。

需要时区来为日期赋予意义。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;

最后提取Instant

Instant instant = zdt.toInstant() ; 

此时,您可以将Instant插入数据库,如上所述。

对象,而不是字符串

如果您在与Java数据库交换数据时学会用对象而不仅仅是字符串进行思考,那么您的工作会更容易。

TIMESTAMP WITH TIME ZONE

顺便说一下,明确Postgres uses the data type TIMESTAMP WITH TIME ZONE的方式。 SQL标准过于简单地定义了这种数据类型,并且数据库的实现也各不相同。以下描述了Postgres版本8,9和10的行为。

使用该区域/偏移量来处理使用时区或offset-from-UTC提交的任何日期时间值,以确定UTC中的值。然后将该UTC值存储在数据库中,其分辨率为microseconds(不是传统Java java.util.Date& Calendar类的毫秒,而不是java的nanoseconds .time课程)。在进行UTC调整之后,丢弃区域/偏移信息。如果您关心原始区域/偏移,请将其明确存储在单独的列中。

检索时,日期时间值以UTC格式从Postgres发出。 psqlpgAdmin等干预工具可能会混淆地应用默认时区。善意的,这样的特征创造了携带时区的储值的错觉,而事实上并非如此。相反,符合JDBC 4.2及更高版本的JDBC驱动程序将自动为您处理UTC和区域。通常我建议总是检索一个Instant对象,如上面的代码所示。然后根据需要应用区域来实例化您自己的ZonedDateTimeOffsetDateTime对象,如上面的代码所示。

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

根据我在Postgres文档中看到的JDBC映射类型,Postgres希望使用java.sql.Timestamp映射到具有时区类型的时间戳。如果您正在处理准备好的声明,可以使用:

java.sql.Timestamp ts = new Timestamp(parsedTimeStamp.getTime());
PreparedStatement stmt;
// define the statement using Postgres SQL with placeholders
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
// now set the timestamp at the particular timezone in the statement
stmt.setTimestamp(index, ts, cal);

GMT替换为您实际需要的任何时区,并将index替换为占位符的数字索引,其中时间戳应出现在实际SQL中。

请注意,除了LocalDateTime之外,符合Java 8标准的更新近期驱动程序应支持使用java.sql.Timestamp