我的应用程序从包含美国/纽约时区(-4 UTC)中的这些日期的数据库中读取java.sql.Date。在获取数据之后,Hibernate创建对象java.sql.Date,并在本地时区中表示它们。因此,我需要从UTC的数据库直接转换数据。我怎么能这么做?
我需要这样的东西
Instant.ofEpochMilli(((java.sql.Date) value).getTime()).atOffset(offset);但是偏置没有做我想做的事情。例如:
数据库时间: 01-02-2020 22:00 (在美国/纽约->,它是UTC-4,我需要多加4小时)
我的应用时间: 01-02-2020 22:00 +4 (因为我的时区是UTC+4)。当我设置ZoneOffset.UTC时
Instant.ofEpochMilli(((java.sql.Date) value).getTime()).atOffset(ZoneOffset.UTC)它删除4小时的ans toString()结果= 01-02-2020T16:00Z。
如何在数据库中添加4小时至今(java.sql.Date),使其成为02-02-2020 02:00 UTC?
发布于 2020-04-22 04:56:57
对于一个时区的时间点,如2020-02-01T22:00-04:00 00America/New_York,请不要使用java.sql.Date。原因有二:
java.sql.Date是一个设计很差的类,实际上是一个真正的黑客,如果已经设计糟糕的java.util.Date类在上面的话。幸运的是,两个outdated.java.sql.Date类都是长的,Date是为一天中没有时间的日期而设计的。相反:
SQL数据库中的
timestamp with time zone,并在UTC中一致地存储时间。因此,存储在数据库中的时间应该是2020-02-02T02:00Z (Z )。OffsetDateTime中(因为JDBC4.2,我们可以完全绕过java.sql.Date和java.sql.Timestamp )。然后,如果需要,在您的时区转换为ZonedDateTime。使用区域/城市格式的适当时区ID (而不仅仅是您认为UTC偏移量是什么)。为了示威:
ZoneId zone = ZoneId.of("Asia/Tbilisi");
OffsetDateTime dateTimeFromDatabase
= OffsetDateTime.of(2020, 2, 2, 2, 0, 0, 0, ZoneOffset.UTC);
ZonedDateTime dateTimeInYourTimeZone
= dateTimeFromDatabase.atZoneSameInstant(zone);
System.out.println(dateTimeInYourTimeZone);输出:
2020-02-02T06:00+04:00亚洲/第比利斯
编辑1:你说过:
我知道使用过时的java.sql.Date是不好的,但我别无选择。"java.sql.Date是为一天中没有时间的日期而设计的。“--但我想我还是可以通过调用(java.sql.Date)值.getTime()(因为它返回时间戳)来获得一天中的时间。
从文件中:
为了符合SQL的定义,
java.sql.Date实例包装的毫秒值必须通过将实例关联的特定时区中的小时、分钟、秒和毫秒设置为零来“规范化”。
所以在我看来你违反了合同。后果如何我不知道。它们可能依赖于JDBC驱动程序。也就是说,行为可能会随着下一个版本的JDBC驱动程序而改变。
编辑2:我仔细观察了你的数据。我同意您的看法,它们是错误的;但问题不在于您所提供的代码,而是您似乎收到的java.sql.Date对象。
为了我的调查我做了:
// time in database: 01-02-2020 22:00
// (in America/New_York -> it's UTC-4 and I need to add extra 4 hours)
ZonedDateTime dateTimeInDatebase = ZonedDateTime
.of(2020, 2, 1, 22, 0, 0, 0, ZoneId.of("America/New_York"));
System.out.println("In database: " + dateTimeInDatebase);
long correctEpochMillis = dateTimeInDatebase.toInstant().toEpochMilli();
System.out.println("Correct millis: " + correctEpochMillis);
// toString() result = 01-02-2020T16:00Z
OffsetDateTime observedDateTime
= OffsetDateTime.of(2020, 2, 1, 16, 0, 0, 0, ZoneOffset.UTC);
long observedEpochMilli = observedDateTime.toInstant().toEpochMilli();
System.out.println("Observed millis: " + observedEpochMilli);
Duration error = Duration.between(dateTimeInDatebase, observedDateTime);
System.out.println("Error: " + error);产出如下:
数据库中的
:2020-02-01T22:00-05:00美国/纽约正确米利斯: 1580612400000观测米利斯: 1580572800000错误: PT-11H
意见:
time/DST).
java.sql.Date中检索到的毫秒值并不表示它应该的时间点。在您的代码中没有任何东西可以改变时间点。因此,您不仅得到了不正确的类型,还得到了一个不正确的值。java.sql.Date中的毫秒值早了11个小时。你已经解释了一些与时区差异的差异,我相信这是真的。我们还没有证实这是整个故事。所以我也不能告诉你解决方案是什么。除了向提供者提交不正确类型和值的票证外,您还可以得到正确的数据。一个可能的黑客是增加11个小时,当然,但你是否应该增加10个小时在夏季时间的一部分一年-我不是正确的人问。
编辑3:
,我刚刚想出了一个方法来修正时间戳的两倍值。类似于本地区域的第一次添加偏移量(修复jdbc驱动程序的影响),以及存储在数据库中的日期的第二个句柄偏移量。
如果我们愿意的话,我们可以这样做:
Instant observedResult = Instant.parse("2020-02-01T16:00:00Z");
Object receivedValue = new java.sql.Date(observedResult.toEpochMilli());
long receivedEpochMillis = ((java.sql.Date) receivedValue).getTime();
ZonedDateTime adjustedDateTime = Instant.ofEpochMilli(receivedEpochMillis)
.atZone(ZoneId.systemDefault())
.withZoneSameLocal(ZoneId.of("America/New_York"));
System.out.println(adjustedDateTime);在亚洲/第比利斯时区运行时输出(这是ZoneId.systemDefault()返回的输出;全年的偏移量为+04:00 ):
2020-02-01T20:00-05:00美国/纽约
它让我们更接近你所说的数据库,但还早了几个小时。我很抱歉。
链接
java.sql.Date一起使用
https://stackoverflow.com/questions/61349029
复制相似问题