metosin / muuntaja

Clojure library for fast http api format negotiation, encoding and decoding.
https://cljdoc.org/d/metosin/muuntaja
Eclipse Public License 2.0
449 stars 51 forks source link

Support Java8 dates oob with EDN & Transit too #92

Open ikitommi opened 5 years ago

ikitommi commented 5 years ago

Muuntaja depends on Java8. Java8 has java.time package with the de facto new Classes for different date & time representations. Muuntaja should support serialization & deserialization of the classes for all default formats: JSON, EDN and Transit. Jsonista already has support, EDN and Transit should be verified & implemented.

relevant discussion: https://clojureverse.org/t/migrating-from-clj-time-to-java-time/3251/6

kanwei commented 5 years ago

I'd like to help with this, but it's unclear where it would best go. Is the right approach to extend a protocol with the JDK8 datetime classes?

kanwei commented 5 years ago

I think this is the general gist of it:

(def write-handler {java.time.OffsetDateTime  (cognitect.transit/write-handler "java.time.OffsetDateTime" (fn [dt] (str dt)))
                    java.time.Instant         (cognitect.transit/write-handler "java.time.Instant" (fn [dt] (str dt)))})

(cognitect.transit/write (cognitect.transit/writer out :json {:handlers write-handler})
                         (java-time/offset-date-time))

I guess it's not too hard to implement - the real question is what tagged value names are appropriate?

miikka commented 5 years ago

I think that we should use the default tags where possible: in EDN, java.time.Instant should be tagged as #inst and in Transit it should use one of the time tags (m/t). I suppose neither has a suitable default representation for OffsetDateTime.

holyjak commented 4 years ago

FYI This is what I did for Instant:

(def transit-write-handlers
  {java.time.Instant
   (cognitect.transit/write-handler
     "t"
     (fn [^java.time.Instant inst]
       (.format
         (com.cognitect.transit.impl.AbstractParser/getDateTimeFormat)
         (java.util.Date/from inst))))})

; => send `{:handlers transit-write-handlers}` to `writer`

it is a hack because it relies on implementation details of transit-java but it guarantees that any existing client will be able to read it as a Date.

deobald commented 3 years ago

In the interest of helping others who stumble on the workarounds described in this thread, here's a concrete implementation wired into reitit:

  1. Muuntaaja instance with custom writer
  2. Configuring a reitit router with that instance

I've used @holyjak 's hack because the default toString from java.time.Instant provides ISO_INSTANT formatting, which can differ from Transit's formatter. The latter only contains 3-digit sub-second precision. Folks who've moved to java.time probably don't care anyway, but it's possible this will save some parsing confusion.

This blog post is old, but was handy for context since I'm unfamiliar with all these libraries: https://increasinglyfunctional.com/2014/09/02/custom-transit-writers-clojure-joda-time.html