Skip to content

Instantly share code, notes, and snippets.

@Deraen
Last active August 6, 2023 06:51
Show Gist options
  • Save Deraen/eb3f650c472fb1abe970 to your computer and use it in GitHub Desktop.
Save Deraen/eb3f650c472fb1abe970 to your computer and use it in GitHub Desktop.
Transit / edn date/datetime serialisers
(ns metosin.common.edn
#+clj
(:require [clj-time.format :as f])
#+cljs
(:require [cljs-time.format :as f]
cljs.reader)
#+clj (:import [org.joda.time DateTime LocalDate]))
;;
;; #DateTime tagging
;;
(def iso8601-formatter (f/formatters :date-time))
(def date-formatter (f/formatters :date))
(defn- datetime->reader-str [d]
(str "#DateTime \"" (f/unparse iso8601-formatter d) \"))
(defn- date->reader-str [d]
(str "#Date \"" (f/unparse-local-date date-formatter d) \"))
(defn- reader-str->datetime [s]
(f/parse iso8601-formatter s))
(defn- reader-str->date [s]
(f/parse-local-date date-formatter s))
#+clj
(do
(defmethod print-dup DateTime [^DateTime d out]
(.write out (datetime->reader-str d)))
(defmethod print-method DateTime [^DateTime d out]
(.write out (datetime->reader-str d)))
(defmethod print-dup LocalDate [^LocalDate d out]
(.write out (date->reader-str d)))
(defmethod print-method LocalDate [^LocalDate d out]
(.write out (date->reader-str d)))
(alter-var-root #'*data-readers* assoc
'DateTime #'sampo.edn/reader-str->datetime
'Date #'sampo.edn/reader-str->date))
#+cljs
(do
(cljs.reader/register-tag-parser! "DateTime" reader-str->datetime)
(cljs.reader/register-tag-parser! "Date" reader-str->date)
(extend-protocol IPrintWithWriter
goog.date.DateTime
(-pr-writer [d out opts]
(-write out (datetime->reader-str d)))
goog.date.Date
(-pr-writer [d out opts]
(-write out (date->reader-str d)))))
(ns metosin.transit.dates
"Transit readers and writers for JodaTime and goog.date.
Supports two types:
- DateTime (org.joda.time.DateTime, goog.date.UtcDateTime)
- LocalDate (org.joda.time.LocalDate, goog.date.Date)
Represents DateTimes in RFC 3339 format: yyyy-mm-ddTHH:MM:SS.sssZ.
RFC 3339 format is an specific profile of ISO 8601 DateTime format.
Some consideration has been made to provide performant read
implemenation for ClojureScript."
(:require [cognitect.transit :as transit]
[clojure.string :as string]
#?@(:cljs [[goog.string :as gs]
goog.date.UtcDateTime
goog.date.Date]))
#?(:clj (:import [org.joda.time])))
#?(:clj (set! *warn-on-reflection* true))
(def DateTime #?(:clj org.joda.time.DateTime, :cljs goog.date.UtcDateTime))
(def LocalDate #?(:clj org.joda.time.LocalDate, :cljs goog.date.Date))
(defn write-date-time
"Represent DateTime in RFC3339 format string."
[d]
#?(:clj (.toString (.withZone ^org.joda.time.DateTime d (org.joda.time.DateTimeZone/forID "UTC")))
:cljs (str (.getUTCFullYear d)
"-" (gs/padNumber (inc (.getUTCMonth d)) 2)
"-" (gs/padNumber (.getUTCDate d) 2)
"T" (gs/padNumber (.getUTCHours d) 2)
":" (gs/padNumber (.getUTCMinutes d) 2)
":" (gs/padNumber (.getUTCSeconds d) 2)
"." (gs/padNumber (.getUTCMilliseconds d) 3)
"Z")))
(defn read-date-time
"Read RFC3339 string to DateTime."
[s]
#?(:clj (org.joda.time.DateTime/parse s)
:cljs (goog.date.UtcDateTime.fromIsoString s)))
(defn write-local-date
"Represent Date in YYYY-MM-DD format."
[x]
#?(:clj (.toString ^org.joda.time.LocalDate x)
:cljs (.toIsoString x true false)))
(defn read-local-date
"Read Date in YYYY-MM-DD format."
[x]
#?(:clj (org.joda.time.LocalDate/parse x)
:cljs (let [[_ y m d] (re-find #"(\d{4})-(\d{2})-(\d{2})" x)]
(goog.date.Date. (long y) (dec (long m)) (long d)))))
(def writers
{DateTime (transit/write-handler (constantly "DateTime") write-date-time)
LocalDate (transit/write-handler (constantly "Date") write-local-date)})
(def readers
{"DateTime" (transit/read-handler read-date-time)
"Date" (transit/read-handler read-local-date)})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment