错误的数据类型意味着丢失信息
保存日期的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) 在结尾之前。