-
Notifications
You must be signed in to change notification settings - Fork 204
/
transform.cljc
101 lines (90 loc) · 3.7 KB
/
transform.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
(ns malli.experimental.time.transform
(:require [malli.transform :as mt :refer [-safe]]
[malli.core :as m]
#?(:cljs [malli.experimental.time :as time
:refer [Duration LocalDate LocalDateTime LocalTime Instant OffsetTime ZonedDateTime OffsetDateTime ZoneId ZoneOffset
TemporalAccessor TemporalQuery DateTimeFormatter createTemporalQuery]]))
#?(:clj
(:import (java.time Duration LocalDate LocalDateTime LocalTime Instant ZonedDateTime OffsetDateTime ZoneId OffsetTime ZoneOffset)
(java.time.temporal TemporalAccessor TemporalQuery)
(java.time.format DateTimeFormatter))))
#?(:clj (set! *warn-on-reflection* true))
#?(:clj
(defn ->temporal-query ^TemporalQuery [f]
(reify TemporalQuery
(queryFrom [_ t]
(f t))))
:cljs
(defn ->temporal-query ^TemporalQuery [f]
(createTemporalQuery f)))
(defn ->parser [formatter qf]
(let [query (->temporal-query qf)]
(fn [#?(:clj ^CharSequence s :cljs s)]
(if #?(:clj (instance? CharSequence s) :cljs (string? s))
(.parse ^DateTimeFormatter formatter s query)
s))))
(defn ->formatter [x]
(cond
(instance? DateTimeFormatter x) x
#?(:clj (instance? String x)
:cljs (string? x)) (. DateTimeFormatter ofPattern x)
:else (throw (ex-info "Invalid formatter" {:formatter x :type (type x)}))))
(def default-formats
{:time/instant (. DateTimeFormatter -ISO_INSTANT)
:time/local-date (. DateTimeFormatter -ISO_LOCAL_DATE)
:time/local-date-time (. DateTimeFormatter -ISO_LOCAL_DATE_TIME)
:time/local-time (. DateTimeFormatter -ISO_LOCAL_TIME)
:time/offset-time (. DateTimeFormatter -ISO_OFFSET_TIME)
:time/offset-date-time (. DateTimeFormatter -ISO_OFFSET_DATE_TIME)
:time/zoned-date-time (. DateTimeFormatter -ISO_ZONED_DATE_TIME)})
(def queries
{:time/instant #(. Instant from %)
:time/local-time #(. LocalTime from %)
:time/local-date #(. LocalDate from %)
:time/local-date-time #(. LocalDateTime from %)
:time/offset-date-time #(. OffsetDateTime from %)
:time/offset-time #(. OffsetTime from %)
:time/zoned-date-time #(. ZonedDateTime from %)})
(def default-parsers
(reduce-kv
(fn [m k v] (assoc m k (-safe (->parser v (get queries k)))))
{:time/duration (-safe #(. Duration parse %))
:time/zone-offset (-safe #(. ZoneOffset of ^String %))
:time/zone-id (-safe #(. ZoneId of %))}
default-formats))
(defn compile-parser [type formatter pattern]
(when-let [formatter (when-let [x (or formatter pattern)]
(->formatter x))]
(-safe (->parser formatter (get queries type)))))
(defn time-decoders [formats]
(into
default-parsers
(for [k (keys formats)]
[k {:compile
(fn [schema opts]
(let [t (m/type schema opts)
{:keys [formatter pattern]} (m/properties schema)]
(or (compile-parser t formatter pattern)
(get default-parsers t))))}])))
(defn time-encoders [formats]
(into
{:time/duration str
:time/zone-id str}
(for [k (keys formats)]
[k {:compile
(fn [schema opts]
(let [t (m/type schema opts)
{:keys [formatter pattern]} (m/properties schema)
formatter (->formatter (or formatter pattern (get default-formats t)))]
(-safe
(fn [^TemporalAccessor ta]
(if (instance? TemporalAccessor ta)
(.format ^DateTimeFormatter formatter ta)
ta)))))}])))
(defn time-transformer
([] (time-transformer default-formats))
([formats]
(mt/transformer
{:name :time
:decoders (time-decoders formats)
:encoders (time-encoders formats)})))