Improve time related functions (frontend).

This commit is contained in:
Andrey Antukh 2021-04-15 17:03:36 +02:00 committed by Andrés Moya
parent e4d4245b6c
commit 88eb6abdd6
4 changed files with 227 additions and 42 deletions

View file

@ -6,47 +6,211 @@
(ns app.util.time
(:require
["date-fns/parseISO" :as dateFnsParseISO]
["date-fns/formatISO" :as dateFnsFormatISO]
["date-fns/format" :as dateFnsFormat]
["date-fns/formatDistanceToNowStrict" :as dateFnsFormatDistanceToNowStrict]
["date-fns/locale/fr" :as dateFnsLocalesFr]
["date-fns/locale/en-US" :as dateFnsLocalesEnUs]
["date-fns/locale/zh-CN" :as dateFnsLocalesZhCn]
["date-fns/locale/es" :as dateFnsLocalesEs]
["date-fns/locale/ru" :as dateFnsLocalesRu]
[cuerdas.core :as str]
["luxon" :as lxn]
["date-fns/formatDistanceToNowStrict" :default dateFnsFormatDistanceToNowStrict]
["date-fns/locale/fr" :default dateFnsLocalesFr]
["date-fns/locale/ca" :default dateFnsLocalesCa]
["date-fns/locale/en-US" :default dateFnsLocalesEnUs]
["date-fns/locale/zh-CN" :default dateFnsLocalesZhCn]
["date-fns/locale/es" :default dateFnsLocalesEs]
["date-fns/locale/tr" :default dateFnsLocalesTr]
["date-fns/locale/ru" :default dateFnsLocalesRu]
[app.util.object :as obj]))
(def DateTime lxn/DateTime)
(def Duration lxn/Duration)
(defprotocol ITimeMath
(plus [_ o])
(minus [_ o]))
(defprotocol ITimeFormat
(format [_ fmt]))
(defn duration?
[o]
(instance? Duration o))
(defn datetime?
[o]
(instance? DateTime o))
(defn duration
[o]
(cond
(integer? o) (.fromMillis Duration o)
(duration? o) o
(string? o) (.fromISO Duration o)
(map? o) (.fromObject Duration (clj->js o))
:else (throw (js/Error. "unexpected arguments"))))
(defn datetime
([s] (datetime s nil))
([s {:keys [zone force-zone] :or {zone "local" force-zone false}}]
(cond
(integer? s)
(.fromMillis ^js DateTime s #js {:zone zone :setZone force-zone})
(map? s)
(.fromObject ^js DateTime (-> (clj->js s)
(obj/set! "zone" zone)
(obj/set! "setZone" force-zone)))
:else
(throw (js/Error. "invalid arguments")))))
(defn epoch->datetime
([seconds] (epoch->datetime seconds nil))
([seconds {:keys [zone force-zone] :or {zone "local" force-zone false}}]
(.fromSeconds ^js DateTime seconds #js {:zone zone :setZone force-zone})))
(defn iso->datetime
"A faster option for transit date parsing."
[s]
(.fromISO ^js DateTime s #js {:zone "local"}))
(defn parse-datetime
([s] (parse-datetime s :iso nil))
([s fmt] (parse-datetime s fmt nil))
([s fmt {:keys [zone force-zone] :or {zone "local" force-zone false}}]
(if (string? fmt)
(.fromFormat ^js DateTime s fmt #js {:zone zone :setZone force-zone})
(case fmt
:iso (.fromISO ^js DateTime s #js {:zone zone :setZone force-zone})
:rfc2822 (.fromRFC2822 ^js DateTime s #js {:zone zone :setZone force-zone})
:http (.fromHTTP ^js DateTime s #js {:zone zone :setZone force-zone})))))
(defn now
[]
(.local ^js DateTime))
(defn utc-now
[]
(.utc ^js DateTime))
(defn ->utc
[dt]
(.toUTC ^js dt))
(defn diff
[dt1 dt2]
(.diff ^js dt1 dt2))
(extend-protocol IEquiv
DateTime
(-equiv [it other]
(.equals it other))
Duration
(-equiv [it other]
(.equals it other)))
(extend-protocol Inst
DateTime
(inst-ms* [inst] (.toMillis ^js inst))
Duration
(inst-ms* [inst] (.toMillis ^js inst)))
(extend-protocol IComparable
DateTime
(-compare [it other]
(if ^boolean (.equals it other)
0
(if (< (inst-ms it) (inst-ms other)) -1 1)))
Duration
(-compare [it other]
(if ^boolean (.equals it other)
0
(if (< (inst-ms it) (inst-ms other)) -1 1))))
(extend-protocol ITimeMath
DateTime
(plus [it o]
(if (map? o)
(.plus ^js it (clj->js o))
(.plus ^js it o)))
(minus [it o]
(if (map? o)
(.minus ^js it (clj->js o))
(.minus ^js it o)))
Duration
(plus [it o]
(if (map? o)
(.plus ^js it (clj->js o))
(.plus ^js it o)))
(minus [it o]
(if (map? o)
(.minus ^js it (clj->js o))
(.minus ^js it o))))
(extend-protocol IPrintWithWriter
DateTime
(-pr-writer [p writer opts]
(-write writer (str/fmt "#stks/datetime \"%s\"" (format p :iso))))
Duration
(-pr-writer [p writer opts]
(-write writer (str/fmt "#stks/duration \"%s\"" (format p :iso)))))
(defn- resolve-format
[v]
(case v
:time-24-simple (.-TIME_24_SIMPLE ^js DateTime)
:datetime-short (.-DATETIME_SHORT ^js DateTime)
:datetime-med (.-DATETIME_MED ^js DateTime)
:datetime-full (.-DATETIME_FULL ^js DateTime)
:date-full (.-DATE_FULL ^js DateTime)
:date-med-with-weekday (.-DATE_MED_WITH_WEEKDAY ^js DateTime)
v))
(defn- format-datetime
[dt fmt]
(case fmt
:iso (.toISO ^js dt)
:rfc2822 (.toRFC2822 ^js dt)
:http (.toHTTP ^js dt)
:json (.toJSON ^js dt)
:date (.toJSDate ^js dt)
:epoch (js/Math.floor (.toSeconds ^js dt))
:millis (.toMillis ^js dt)
(let [f (resolve-format fmt)]
(if (string? f)
(.toFormat ^js dt f)
(.toLocaleString ^js dt f)))))
(extend-protocol ITimeFormat
DateTime
(format [it fmt]
(format-datetime it fmt))
Duration
(format [it fmt]
(case fmt
:iso (.toISO it)
:json (.toJSON it)
(.toFormat ^js it fmt))))
(def ^:private locales
#js {:en dateFnsLocalesEnUs
:fr dateFnsLocalesFr
:tr dateFnsLocalesTr
:es dateFnsLocalesEs
:ca dateFnsLocalesCa
:ru dateFnsLocalesRu
:zh_cn dateFnsLocalesZhCn})
(defn now
"Return the current Instant."
[]
(js/Date.))
(defn parse
[v]
(^js dateFnsParseISO v))
(defn format-iso
[v]
(^js dateFnsFormatISO v))
(defn format
([v fmt] (format v fmt nil))
([v fmt {:keys [locale] :or {locale "en"}}]
(dateFnsFormat v fmt #js {:locale (obj/get locales locale)})))
(defn timeago
([v] (timeago v nil))
([v {:keys [locale] :or {locale "en"}}]
(when v
(->> #js {:includeSeconds true
:addSuffix true
:locale (obj/get locales locale)}
(dateFnsFormatDistanceToNowStrict v)))))
(let [v (if (datetime? v) (format v :date) v)]
(->> #js {:includeSeconds true
:addSuffix true
:locale (obj/get locales locale)}
(dateFnsFormatDistanceToNowStrict v))))))

View file

@ -10,6 +10,7 @@
[cognitect.transit :as t]
[linked.core :as lk]
[linked.set :as lks]
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.matrix :as gmt]
[app.util.time :as dt]))
@ -67,6 +68,22 @@
(def ordered-set-read-handler
(t/read-handler #(into (lk/set) %)))
(def date-read-handler
(t/read-handler (fn [value] (-> value (js/parseInt 10) (dt/datetime)))))
(def duration-read-handler
(t/read-handler (fn [value] (dt/duration value))))
(def date-write-handler
(t/write-handler
(constantly "m")
(fn [v] (str (inst-ms v)))))
(def duration-write-handler
(t/write-handler
(constantly "duration")
(fn [v] (inst-ms v))))
;; --- Transit Handlers
(def ^:privare +read-handlers+
@ -75,11 +92,15 @@
"ordered-set" ordered-set-read-handler
"jsonblob" blob-read-handler
"matrix" matrix-read-handler
"m" date-read-handler
"duration" duration-read-handler
"point" point-read-handler})
(def ^:privare +write-handlers+
{gmt/Matrix matrix-write-handler
Blob blob-write-handler
dt/DateTime date-write-handler
dt/Duration duration-write-handler
lks/LinkedSet ordered-set-write-handler
gpt/Point point-write-handler})