Когда я читаю типы дат postgres, я хочу, чтобы они были переведены в локальные даты времени joda (или новые локальные даты java.time). Это связано с тем, что даты 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 напрямую, чтобы избежать этого? Я нашел документацию по драйверу JDBC postgres, которая кажется многообещающей, но я не могу понять, как реализовать ее в clojure.java.jdbc.
По сути: Есть ли способ получить мои даты postgres из базы данных, не испортив их, если мой часовой пояс по умолчанию не UTC?
Я понял, как это сделать сейчас:
(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
. Я не уверен, что он делает, но в любом случае он портит день, когда задействованы часовые пояса.
Отвечает ли это на ваш вопрос? Вставка и извлечение объектов java.time.LocalDate в/из базы данных SQL, такой как H2