有没有办法让 jpql/native-query 区域不可知

Posted

技术标签:

【中文标题】有没有办法让 jpql/native-query 区域不可知【英文标题】:Is there way to make jpql/native-query zone agnostic 【发布时间】:2022-01-15 07:33:45 【问题描述】:

我的 PostgreSQL 表中有一个日期字段,其中保存了日期,包括时间戳和区域(捕获时在 java 端使用“localdatetime”)。但要求是拉起只检查日期部分的记录(忽略时间和地区),这样全世界的所有用户都可以通过日期过滤看到相同的结果。

如果我提供更多解释,请告诉我。

【问题讨论】:

世界各地的日期永远不会相同。 2021-12-11 在日本和纽约意味着不同的东西。那么你期望得到同样的结果呢? 【参考方案1】:

错误的数据类型意味着丢失信息

保存日期的postgres表,包括时间戳和区域(捕获时在java端使用“localdatetime”)

这是类型上的矛盾。

PostgreSQL 中的TIMESTAMP WITH TIME ZONE 类型保存时区。仔细阅读the documentation。它解释说,与日期和时间一起提供的任何时区或偏移信息都用于调整与 UTC 零时分秒的偏移量。换句话说,日期和时间被调整为“UTC”,就像我们简写的那样。

您可能提供的区域或偏移信息随后将被丢弃。 Postgres 不记得原始的偏移量或区域。从 TIMESTAMP WITH TIME ZONE 类型的列中检索到的任何值始终采用 UTC。如果您关心原始区域或偏移量,则必须自己将其存储在第二列中。

但是你有问题。在将日期时间发送到数据库时,您没有提供 任何 时区或偏移指示符。您错误地使用了 LocalDateTime 类,该类故意缺少任何时区或偏移量指示符。 LocalDateTime 类表示带有时间的日期,仅此而已。于是就出现了上面提到的矛盾。您为需要三件事(日期、时间和区域/偏移)的列提供了两件事(日期、时间)。

我认为 Postgres 获取了您的 LocalDateTime 值并将其存储为 UTC 中的日期和时间。所以你丢失了信息。例如,如果一行的输入是日本东京的 2022 年 1 月 23 日中午??,而另一行的输入是法国图卢兹 23 日的中午??,而第三行的输入本来也打算在 23 日中午,但正如在美国俄亥俄州托莱多看到的那样见于 UTC,2022-02-23T12:00Z。

在记录了如此混杂的错误值之后,就没有回头路了。除非您有办法确定每行的预期区域,否则您的信息将丢失,并且您存储的列毫无价值。

查询一天

让我们搁置无效存储时刻的问题。现在让我们关注如何使用TIMESTAMP WITH TIME ZONE 类型的列查询一天的行。

您需要了解,对于任何特定时刻,日期会因时区而异。澳大利亚悉尼??随时可能是“明天”,而加拿大艾伯塔省埃德蒙顿则可能是“昨天”??。因此,您需要时区(或偏移)的上下文来感知日期。

如果您想查询埃德蒙顿的某一天,请指定时区。

ZoneId z = ZoneId.of( "America/Edmonton" ) ;

指定所需的日期。

LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 ) ;

确定代表当天所有时刻的时间跨度。定义这种跨度的最佳方法是半开放方法。在 Half-Open 中,开头是 inclusive 而结尾是 exclusive。因此,跨度从一天的第一时刻开始,一直到但不包括第二天的第一时刻。

要开始一天,让 java.time 确定。日子总是从 00:00 开始。

ZonedDateTime start = ld.atStartOfDay( z ) ;
ZonedDateTime end = ld.plusDays( 1 ).atStartOfDay( z ) ;

ZonedDateTime 不映射到 SQL 中的任何数据类型。所以转换成OffsetDateTime 对象与数据库进行交换。

OffsetDateTime odtStart = start.toOffsetDateTime() ; 
OffsetDateTime odtEnd = end.toOffsetDateTime() ; 

通过你准备好的语句传递这些。

myPreparedStatement.setObject( … , odtStart ) ;
myPreparedStatement.setObject( … , odtEnd ) ; 

在您的 SQL 中,不要使用BETWEEN。该命令已完全关闭。对于 Half-Open,编写 SQL 查询以查找 (a) 不早于开始的值(“not before”是“等于或晚于”的缩写), (b) 在结尾之前

【讨论】:

【参考方案2】:

假设 24.12.2021 15:00 Berlin(德国)在数据库中。

德国人将看到 15:00。

但来自伦敦的人必须看到 16:00 或“柏林的 15:00 挂钟”。

既然你说

from all over the world can see the same resut

15:00 和 16:00 之间会有区别:数字 5 和数字 6,它们是不同的。

您可以通过打印为每个人提供相同的结果

15:00 wall clock in Berlin

但这需要在日期时间旁边传输柏林时区。

【讨论】:

以上是关于有没有办法让 jpql/native-query 区域不可知的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法让bmps透明?

有没有办法让 webview 显示 flash 内容?

有没有办法让 setTooltip() 区分大小写

有没有办法让这个功能更好? [复制]

有没有办法让对话框自行打开?

有没有办法让异步/等待流程短路?