clojure.java.jdbc:如何在非 utc 时区中将 postgres 日期读取为本地日期?

clojure.java.jdbc: How to read postgres dates as localdate when in a non-utc timezone?

当我阅读 postgres 日期类型时,我希望将它们强制转换为 joda 时间 LocalDates(或新的 java.time localdates)。 这是因为 postgres 日期没有时区信息,所以我不希望我的 java 或 clojure 对象以某种方式添加它。

这是我为此设置的 IResultSetReadColumn 协议扩展:

(extend-protocol clojure.java.jdbc/IResultSetReadColumn
  java.sql.Date
  (result-set-read-column [v _rsmeta _idx]
    (tc/to-local-date v)))

然而,当我尝试使用它时,我得到了错误的结果:

(DateTimeZone/getDefault)
=> #object[org.joda.time.tz.CachedDateTimeZone 0x60470ff "Europe/Stockholm"]

(jdbc/query db/db ["SHOW TIMEZONE"])
=> ({:timezone "Europe/Stockholm"})

(jdbc/query db/db ["SELECT '2020-01-01'::date"])
=> ({:date #object[org.joda.time.LocalDate 0x75081795 "2019-12-31"]}) ; ARGH!! It changed date!

我怀疑这是由于日期被转换为 java.sql.date 造成的,其中不知何故包含时区信息。有没有办法直接读取 postgres 日期来避免这种情况?我找到了 documentation on the postgres JDBC driver that seems promising 但我不知道如何在 clojure.java.jdbc 中实现它。

基本上: 如果我的默认时区不是 UTC,有没有什么方法可以从数据库中获取我的 postgres 日期而不弄乱它们?

我现在知道怎么做了:

(defn sql-date->LocalDate [v]
      ; Convert a java.sql.Date into a LocalDate.
      ; LocalDates does NOT have any timezone info - bit un-intuitive perhaps.
      ; Neither does the postgres date type.
      ;
      ; Unfortunately, java.sql.Date has a time component, 
      ; which would causes lots of issues if we were to pass it along as is.
      ; How it works is:
      ; 1. The postgres JDBC driver reads the postgres date as a java.sql.Date,
      ;    by setting the jav.sql.Date timestamp to 00.00
      ;    the date of the postgres date in the 
      ;    JVM default timezone (DateTimeZone/getDefault).
      ;
      ; 2. .toLocalDate converts the java.sql.Date to a java.time.LocalDate 
      ;     in the JVM default timezone (DateTimeZone/getDefault).
      ;
      ; 3. Then we convert the java.time.LocalDate to a joda-time LocalDate, 
      ;    as that is what clj-time uses.
      ;
      ; So because we convert both date -> timestamp -> date in the same
      ; timezone, it all evens out.
      ;
      (let [java-time-localdate (.toLocalDate v)]
        (time/local-date (.getYear java-time-localdate)
                         (.getValue (.getMonth java-time-localdate))
                         (.getDayOfMonth java-time-localdate))))

最重要的是不要使用 clj-time.coerce 的 to-localtime 函数。我不确定它的作用,但无论如何,当涉及到时区时,它会把一天搞得一团糟。