使用java 1.7从postgres检索时的日期格式不一致

时间:2015-08-24 06:32:36

标签: java postgresql date

我在从postgres检索日期列值并使用JDK 1.7导出为CSV时遇到此独特问题以下是示例输出

ID,      Date Created,        Date Modified 
816271,  8/8/2013 14:35       2/2/2015 16:47
830322   13/08/2013 11:48 AM  2/2/2015 16:48
1128312  10/2/2015 16:53      10/2/2015 16:53
1129465  12/2/2015 16:23      12/2/2015 16:23
1130482  16/02/2015 4:28 PM   15/06/2015 7:01 AM
1019527  19/08/2014 4:40 AM   23/02/2015 12:14 PM
1134334  23/02/2015 8:38 AM   4/6/2015 5:16

问题是,我看到AM / PM附加了DAY部分大于12的日期值。当我查看数据库时,我看不到任何AM / PM。在我的DO中,我刚刚将变量声明为Date。

请让我知道为什么会出现这种不一致的格式化。

感谢

以下是我如何将日期设置到DO中。

public void setCreatedDate(Date createdDate) { 
  if (createdDate == null) { 
    this.mCreatedDate = createdDate; return; 
  }
  this.mCreatedDate = new Date(createdDate.getTime());
} 

我根本没有使用任何格式化代码。即使有一个,我也不确定为什么它不适用于所有记录

1 个答案:

答案 0 :(得分:1)

您需要了解使用日期时间数据类型存储在数据库中的日期时间值没有格式。您所看到的是为方便人类查看而生成的日期时间值的字符串表示。字符串日期时间。

因此,“AM / PM”的格式问题与Postgres之外生成该字符串的一些代码有关。您没有向我们展示该代码,因此我们无法直接解决该问题。但是如果你有意识地使用日期时间值/对象而不是字符串,你可以首先避免这个问题。

在Postgres中存储日期时间

在Postgres中,您通常应该使用TIMESTAMP WITH TIME ZONE数据类型。此类型实际上不保留时区。而是它尊重时区,使用伴随数据输入的任何传递的偏移或时区信息来调整到UTC。然后将结果存储在数据库中。调整后,Postgres会丢弃原始偏移或时区信息。

从Postgres中检索日期时间

检索数据(SELECT)时,您可能会得到一个日期时间值,或者您可能会获得一个字符串,具体取决于客户端应用程序(pgAdmin,psql,SQuirreL SQL Client等)或者您的数据库驱动程序(JDBC等)。如果获取字符串,则可能代表您对某个时区进行了调整,但该字符串日期时间值。如果获取日期时间值,请坚持使用该值进行工作,而不是转换为字符串。在JDBC中,这意味着使用java.sql.Timestamp个对象,例如。

Java日期时间框架

如果使用Java 8或更高版本的技术,则应使用新的java.time package。如果不可能,请使用Joda-Time库。尽量避免使用java.util.Date/.Calendar& java.text.SimpleDateFormat因为它们很麻烦而且令人困惑。

实施例

下面是从Postgres 9.4中提取java.sql.Timestamp,然后使用java.time或Joda-Time处理值的完整示例。

数据丢失与Joda-Time& java.util.Date

请注意,Joda-Time(如java.util.Date)仅限于小数秒的millisecond精度。 Postgres解析为microseconds。因此,从Postgres转换为Joda-Time / java.util.Date意味着可能会丢失数据。使用java.time,没有问题,因为它解析为nanoseconds

chart comparing usage of milliseconds vs microseconds vs nanoseconds as discussed in previous paragraph

代码

使用postgresql-9.4-1201.jdbc41.jar驱动程序和Mac OS X Mountain Lion上的Postgres 9.4.x编写的Java 8 Update 51。

String message = "Example of fetching Timestamp from Postgres.";
StringBuilder sql = new StringBuilder();
sql.append( "SELECT now() " + "\n" );
sql.append( ";" );
java.sql.Timestamp ts = null;
try ( Connection conn = DatabaseHelper.instance().connectionInAutoCommitMode() ; 
        PreparedStatement pstmt = conn.prepareStatement( sql.toString() ); ) {
    try ( ResultSet rs = pstmt.executeQuery(); ) {
        // Extract data from result set
        int count = 0;
        while ( rs.next() ) {
            count ++;
            ts = rs.getTimestamp( 1 );
        }
    }

} catch ( SQLException ex ) {
    logger.error( "SQLException during: " + message + "\n" + ex );
} catch ( Exception ex ) {
    logger.error( "Exception during: " + message + "\n" + ex );

}

的java.sql.Timestamp

请注意旧的Java日期时间类如何隐式应用JVM的当前默认时区。虽然旨在提供帮助,但它不会造成混乱。运行此代码时看到的时区为America/Los_Angeles,其偏移量为−07:00

String output_SqlTimestamp = ts.toString();  // Confusingly applies your JVM’s current default time zone.

java.time

在Java 8及更高版本中使用java.time。

// If you have Java 8 or later, use the built-in java.time package.

java.time.Instant instant = ts.toInstant();
java.time.ZoneId zoneId = ZoneId.of( "America/Montreal" );
java.time.ZonedDateTime zdt = java.time.ZonedDateTime.ofInstant( instant , zoneId );

String output_UTC = instant.toString();
String output_Montréal = zdt.toString();

System.out.println( "output_SqlTimestamp: " + output_SqlTimestamp );  
System.out.println( "output_UTC: " + output_UTC );
System.out.println( "output_Montréal: " + output_Montréal );

约达时间

在Java 8之前,使用Joda-Time。

// Before Java 8, use Joda-Time. (Joda-Time was the inspiration for java.time.)
// IMPORTANT: Joda-Time, like java.util.Date, is limited to milliseconds for fraction of a second. So you may experience data loss from a Postgres date-time value with microseconds.

org.joda.time.DateTime dateTimeMontréal = new org.joda.time.DateTime( ts.getTime() , DateTimeZone.forID( "America/Montreal" ) );  // WARNING: Data lost with microseconds truncated to milliseconds.
org.joda.time.DateTime dateTimeUtc = dateTimeMontréal.withZone( DateTimeZone.UTC );

String output_Joda_dateTimeMontréal = dateTimeMontréal.toString();
String output_Joda_dateTimeUtc = dateTimeUtc.toString();

System.out.println( "output_Joda_dateTimeMontréal: " + output_Joda_dateTimeMontréal );
System.out.println( "output_Joda_dateTimeUtc: " + output_Joda_dateTimeUtc );

跑步时。

output_SqlTimestamp: 2015-08-24 12:46:06.979144
output_UTC: 2015-08-24T18:46:06.979144Z
output_Montréal: 2015-08-24T14:46:06.979144-04:00[America/Montreal]
output_Joda_dateTimeMontréal: 2015-08-24T14:46:06.979-04:00
output_Joda_dateTimeUtc: 2015-08-24T18:46:06.979Z