Clojure.java.jdbc: как читать даты postgres как локальную дату, когда вы находитесь в часовом поясе, отличном от utc?

Когда я читаю типы дат 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?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
323
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я понял, как это сделать сейчас:

(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. Я не уверен, что он делает, но в любом случае он портит день, когда задействованы часовые пояса.

Другие вопросы по теме