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
函数。我不确定它的作用,但无论如何,当涉及到时区时,它会把一天搞得一团糟。
当我阅读 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
函数。我不确定它的作用,但无论如何,当涉及到时区时,它会把一天搞得一团糟。