有没有办法让 jpql/native-query 区域不可知
Is there way to make jpql/native-query zone agnostic
我在我们的 PostgreSQL table 中有一个日期字段,其中保存了日期,包括时间戳和区域(捕获时在 java 端使用“localdatetime”)。但要求拉出记录只检查日期部分(忽略时间和时区),这样如果按日期过滤,来自世界各地的所有用户都可以看到相同的结果。
如果我提供更多解释,请告诉我。
假设 24.12.2021 15:00 柏林(德国)在数据库中。
来自德国的人会看到 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
但这需要在日期时间旁边传输柏林时区。
错误的数据类型意味着丢失信息
postgres table where date is being saved including time stamp and zone ( using "localdatetime" in java side when capturing )
这是类型上的矛盾。
PostgreSQL 中的 TIMESTAMP WITH TIME ZONE
类型不 保存时区。仔细阅读the documentation。它解释说,与日期和时间一起提供的任何时区或偏移量信息都用于调整与 UTC 的零小时-分钟-秒的偏移量。换句话说,日期和时间被调整为“UTC”,正如我们在 shorthand.
中所说的
您提供的区域或偏移量信息随后将被丢弃。 Postgres 不记得原来的偏移量或区域。从类型为 TIMESTAMP WITH TIME ZONE
的列中检索到的任何值始终采用 UTC。如果您关心原始区域或偏移量,则必须自己将其存储在第二列中。
但是你有一个问题。在将日期时间发送到数据库时,您没有提供 any 时区指示符或偏移量。您错误地使用了 LocalDateTime
class ,它故意缺少任何时区或偏移量指示符。 LocalDateTime
class 表示带有时间的日期,仅此而已。于是就出现了上面所说的矛盾。您向需要三件事(日期、时间和 zone/offset)的列提供了两件事(日期、时间)。
我假设 Postgres 获取了您的 LocalDateTime
值并将其存储为 UTC 日期和时间。所以你丢失了信息。例如,如果一行的输入本应是 2022 年 1 月 23 日在日本东京的中午,而另一行的输入本应是法国图卢兹同一日期的中午 23 日,而第三行的输入本应是中午同样在 23 日,但如美国俄亥俄州托莱多所见,那么您将错误地记录相隔数小时发生的三个不同时刻,就好像它们都发生在另一个时刻,即第四个时刻,即 2022 年 UTC 中所见的 23 日中午- 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) 不在开始 之前的值(“不在之前”是说“等于”的缩写方式to or later than"), and (b) 是before end.
我在我们的 PostgreSQL table 中有一个日期字段,其中保存了日期,包括时间戳和区域(捕获时在 java 端使用“localdatetime”)。但要求拉出记录只检查日期部分(忽略时间和时区),这样如果按日期过滤,来自世界各地的所有用户都可以看到相同的结果。
如果我提供更多解释,请告诉我。
假设 24.12.2021 15:00 柏林(德国)在数据库中。
来自德国的人会看到 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
但这需要在日期时间旁边传输柏林时区。
错误的数据类型意味着丢失信息
postgres table where date is being saved including time stamp and zone ( using "localdatetime" in java side when capturing )
这是类型上的矛盾。
PostgreSQL 中的 TIMESTAMP WITH TIME ZONE
类型不 保存时区。仔细阅读the documentation。它解释说,与日期和时间一起提供的任何时区或偏移量信息都用于调整与 UTC 的零小时-分钟-秒的偏移量。换句话说,日期和时间被调整为“UTC”,正如我们在 shorthand.
您提供的区域或偏移量信息随后将被丢弃。 Postgres 不记得原来的偏移量或区域。从类型为 TIMESTAMP WITH TIME ZONE
的列中检索到的任何值始终采用 UTC。如果您关心原始区域或偏移量,则必须自己将其存储在第二列中。
但是你有一个问题。在将日期时间发送到数据库时,您没有提供 any 时区指示符或偏移量。您错误地使用了 LocalDateTime
class ,它故意缺少任何时区或偏移量指示符。 LocalDateTime
class 表示带有时间的日期,仅此而已。于是就出现了上面所说的矛盾。您向需要三件事(日期、时间和 zone/offset)的列提供了两件事(日期、时间)。
我假设 Postgres 获取了您的 LocalDateTime
值并将其存储为 UTC 日期和时间。所以你丢失了信息。例如,如果一行的输入本应是 2022 年 1 月 23 日在日本东京的中午,而另一行的输入本应是法国图卢兹同一日期的中午 23 日,而第三行的输入本应是中午同样在 23 日,但如美国俄亥俄州托莱多所见,那么您将错误地记录相隔数小时发生的三个不同时刻,就好像它们都发生在另一个时刻,即第四个时刻,即 2022 年 UTC 中所见的 23 日中午- 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) 不在开始 之前的值(“不在之前”是说“等于”的缩写方式to or later than"), and (b) 是before end.