java.sql.Timestamp错误的时间解析

时间:2017-12-14 22:27:48

标签: java sql postgresql scala timestamp

有人可以解释为什么会这样吗?为什么那段时间有24分钟的补偿以及如何处理?

Scala 2.12和Java 8.

scala> java.sql.Timestamp.valueOf("1900-01-01 00:59:00")
res22: java.sql.Timestamp = 1900-01-01 00:59:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:00:00")
res23: java.sql.Timestamp = 1900-01-01 01:24:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:14:00")
res24: java.sql.Timestamp = 1900-01-01 01:38:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:20:00")
res25: java.sql.Timestamp = 1900-01-01 01:44:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:23:00")
res26: java.sql.Timestamp = 1900-01-01 01:47:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:24:00")
res27: java.sql.Timestamp = 1900-01-01 01:24:00.0

scala> java.sql.Timestamp.valueOf("1900-01-01 01:30:00")
res28: java.sql.Timestamp = 1900-01-01 01:30:00.0

2 个答案:

答案 0 :(得分:4)

查看IANA时区数据库中的时区定义:

# Zone  NAME            GMTOFF  RULES   FORMAT  [UNTIL]
Zone    Europe/Warsaw   1:24:00 -       LMT     1880
                        1:24:00 -       WMT     1915 Aug  5 # Warsaw Mean Time
                        1:00    C-Eur   CE%sT   1918 Sep 16  3:00
                        2:00    Poland  EE%sT   1922 Jun
                        1:00    Poland  CE%sT   1940 Jun 23  2:00
                        1:00    C-Eur   CE%sT   1944 Oct
                        1:00    Poland  CE%sT   1977
                        1:00    W-Eur   CE%sT   1988
                        1:00    EU  CE%sT

1900年,波兰的时区偏离UTC一小时24分钟,即他们使用的是当地平均太阳时。那是在1915年8月5日引入标准时区之前。

你必须向PostgreSQL提供一个timestamp without time zone,它在你当地的时区解释(偏移量为1:24)。

某人(scala?)然后将此时间戳转换回当地时区的时间戳,但错误地使用了一小时的偏移量。

我不知道如何解决这个问题,但要么全程使用timestamp without time zone,要么修复认为波兰时间与1900年UTC相差1小时的组件。

答案 1 :(得分:2)

据我所知,这里涉及两个错误。两个都是java.util.Date类中的{如果我是正确的),java.sql.Timestamp的超类。

首先,在1900年华沙没有时间抵消过渡。我的Java 8知道的最早的过渡是在1915年。所以华沙在我们关注的所有时间里都与格林尼治标准时间相差1:24

我试过了:

    TimeZone.setDefault(TimeZone.getTimeZone("Europe/Warsaw"));
    ZoneOffset offset0124 = ZoneOffset.ofHoursMinutes(1, 24);

    System.out.println("" + new Date(0, 0, 1, 0, 59) 
            + " -> " + new Date(0, 0, 1, 0, 59).toInstant().atOffset(offset0124));
    System.out.println("" + new Date(0, 0, 1, 1, 14) 
            + " -> " + new Date(0, 0, 1, 1, 14).toInstant().atOffset(offset0124));
    System.out.println("" + new Date(0, 0, 1, 1, 24) 
            + " -> " + new Date(0, 0, 1, 1, 24).toInstant().atOffset(offset0124));

打印:

Mon Jan 01 00:59:00 CET 1900 -> 1900-01-01T01:23+01:24
Mon Jan 01 01:38:00 CET 1900 -> 1900-01-01T01:38+01:24
Mon Jan 01 01:24:00 CET 1900 -> 1900-01-01T01:24+01:24

你使用的方法Timestamp.valueOf方法间接使用了一个弃用的Date构造函数,所以我(不是完全相同的构造函数,我使用没有秒的那个,信任它没有区别) 。我将评论以上三种情况落后

  • 1:24处理正确,我们从Date.toString()OffsetDateTime获得预期时间。
  • 24分钟后,
  • 1:14被认为是1:38。这看起来像是一个错误。
  • 0:59被认为是1:23,也是24分钟后。我们可以从OffsetDateTime看到这一点。同样的错误。但是,Date.toString()按预期生成00:59。在我看来,这是第二个以某种方式补偿第一个错误的错误。我没有检查过,但我怀疑这个bug的来源也会导致Timestamp.toString()行为不正确。

作为检查,我计算了0:59和1:24的Timestamp个对象之间的差异。期望的结果是25分钟或1 500 000毫秒。代码是:

    System.out.println(java.sql.Timestamp.valueOf("1900-01-01 01:24:00").getTime() 
            - java.sql.Timestamp.valueOf("1900-01-01 00:59:00").getTime());

打印

60000
60秒,与1分钟相同。因此,尽管这两个时间戳都以我们预期的方式打印,但仍然存在一个错误。