I used next.jdbc library to persist a record to a PostgreSQL table. As you can see below, the values are a LocalDate instance and an Instant instance. The SQL columns are of type DATE and TIMESTAMPTZ respectively.
Required the next.jdbc.date-time
namespace to map from Java -> SQL, as mentioned in the docs.
{:id 7, :trade_date #object[java.time.LocalDate 0x6b751cb1 2020-12-22], :created_at #object[java.time.Instant 0x3add5e17 2020-12-22T11:41:07.557790Z]}
When I read the record back (SQL -> Clojure), the representation is as follows
{:id 7, :trade_date #inst "2020-12-21T18:30:00.000-00:00", :created_at #inst "2020-12-22T11:41:07.557790000-00:00"}
So they are read back as #inst literals? I'd like them back as java.time types. Also note that the LocalDate seems to be converted to a UTC timestamp. Here's what I dug up so far..
; inst -> Instant
=> (.toInstant #inst "2020-12-22T11:41:07.557790000-00:00")
#object[java.time.Instant 0x312cc79e "2020-12-22T11:41:07.557Z"]
; inst -> LocalDate
=> (-> #inst "2020-12-21T18:30:00.000-00:00"
#_=> .toInstant
#_=> (ZonedDateTime/ofInstant (ZoneId/of "UTC"))
#_=> (.withZoneSameInstant (ZoneId/systemDefault))
#_=> (.toLocalDate))
#object[java.time.LocalDate 0x44048462 "2020-12-22"]
Doesn't seem simple / the Clojure way. Is there a better way?
References: Pieced from the answers by Alan Thompson and Basil Bourque
1 hr later. Well that didn't work.
Turns out next.jdbc reconstructs the record with java.sql.* types, which happen to be printed out as #inst literals at the REPL.
Calling (.toInstant sql-date-instance)
blows up with an Exception (this is as designed). However armed with this bit of info, it was easy to achieve the end result with. Run the returned vector of maps through this mapper.
(defn mapSqlToTimeTypes [map-with-sql-types]
(reduce (fn [return-map [k, v]]
(cond
(instance? java.sql.Timestamp v) (assoc return-map k (.toInstant v))
(instance? java.sql.Date v) (assoc return-map k (.toLocalDate v))
:else (assoc return-map k v)))
{}
map-with-sql-types))
So #inst is just a tagged literal, by default its underlying type is java.util.Date.
(def my-inst #inst "2018-03-28T10:48:00.000")
(= java.util.Date (class my-inst))
;=> true
But you or any library can change the default reader for #inst, since data-readers is a dynamic var. So at any point in time, depending on the lib you are using #inst may produce different underlying type.
I haven't used nex.jdbc yet myself, but I think that what you need is to call read-as-instant or read-as-local function. See here
Also checkout clj-time Clojure library. Also checkout clojure.java-time wrapper around java.time
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With