Skip to content

Latest commit

 

History

History
95 lines (74 loc) · 3.47 KB

1-26_reader-literal.asciidoc

File metadata and controls

95 lines (74 loc) · 3.47 KB

Representing Dates as Literals

by Ryan Neufeld

Problem

You need to represent instances of time in a readable and serializable form.

Solution

Use Clojure’s #inst literals in source to represent fixed points in time:

(def ryans-birthday #inst "1987-02-18T18:00:00.000-00:00")

(println ryans-birthday)
;; *out*
;; #inst "1987-02-18T18:00:00.000-00:00"

When communicating with other Clojure processes (or anything else that speaks edn), use clojure.edn/read to reify instant literal strings into Date objects:

;; A faux communication channel that "receives" edn strings
(require 'clojure.edn)
(import '[java.io PushbackReader StringReader])

(defn remote-server-receive-date []
  (-> "#inst \"1987-02-18T18:00:00.000-00:00\""
      (StringReader.)
      (PushbackReader.)))

(clojure.edn/read (remote-server-receive-date))
;; -> #inst "1987-02-18T18:00:00.000-00:00"

In the preceding example, remote-server-receive-date emulates a communication channel upon which you may receive edn data.

Discussion

Since Clojure 1.4, instants in time have been represented via the #inst reader literal. This means dates are no longer represented by code that must be evaluated, but instead have a textual representation that is both consistent and serializable. This standard allows any process capable of communicating in extensible data notation to speak clearly about instants of time. See the edn implementations list for a list of languages that speak edn; the list includes Clojure, Ruby, and JavaScript so far, with many more implementations in the works.

clojure.core/read Versus clojure.edn/read

While it may seem convenient to read strings using Clojure’s built-in reader (clojure.core/read), it is never safe to parse input from an untrusted source using this reader. If you need to receive simple Clojure data from an external source, it is best to use the edn reader (clojure.edn/read).

clojure.core/read isn’t safe because it was only designed for reading Clojure data and strings from trusted sources (such as the source files you write). clojure.edn/read is designed specifically for use as part of a communication channel, and as such is built with security in mind.

It’s also possible to vary how the reader evaluates #inst literals by changing the binding of *data-readers*. By varying the binding of *data-readers*, it is possible to read #inst literals as java.util.Calendar or java.sql.Timestamp, if you so desire:

(def instant "#inst \"1987-02-18T18:00:00.000-00:00\"")

(binding [*data-readers* {'inst clojure.instant/read-instant-calendar}]
  (class (read-string instant)))
;; -> java.util.GregorianCalendar

(binding [*data-readers* {'inst clojure.instant/read-instant-timestamp}]
  (class (read-string instant)))
;; -> java.sql.Timestamp

See Also