mirror of
https://github.com/penpot/penpot.git
synced 2025-05-14 12:16:38 +02:00
✨ Improve time related functions (frontend).
This commit is contained in:
parent
e4d4245b6c
commit
88eb6abdd6
4 changed files with 227 additions and 42 deletions
|
@ -35,11 +35,11 @@
|
||||||
"shadow-cljs": "^2.11.20"
|
"shadow-cljs": "^2.11.20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"date-fns": "^2.19.0",
|
"date-fns": "^2.21.1",
|
||||||
"draft-js": "^0.11.7",
|
"draft-js": "^0.11.7",
|
||||||
"highlight.js": "^10.6.0",
|
"highlight.js": "^10.6.0",
|
||||||
"humanize-duration": "~3.25.0",
|
|
||||||
"js-beautify": "^1.13.5",
|
"js-beautify": "^1.13.5",
|
||||||
|
"luxon": "^1.26.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"randomcolor": "^0.6.2",
|
"randomcolor": "^0.6.2",
|
||||||
"react": "~17.0.1",
|
"react": "~17.0.1",
|
||||||
|
|
|
@ -6,47 +6,211 @@
|
||||||
|
|
||||||
(ns app.util.time
|
(ns app.util.time
|
||||||
(:require
|
(:require
|
||||||
["date-fns/parseISO" :as dateFnsParseISO]
|
[cuerdas.core :as str]
|
||||||
["date-fns/formatISO" :as dateFnsFormatISO]
|
["luxon" :as lxn]
|
||||||
["date-fns/format" :as dateFnsFormat]
|
["date-fns/formatDistanceToNowStrict" :default dateFnsFormatDistanceToNowStrict]
|
||||||
["date-fns/formatDistanceToNowStrict" :as dateFnsFormatDistanceToNowStrict]
|
["date-fns/locale/fr" :default dateFnsLocalesFr]
|
||||||
["date-fns/locale/fr" :as dateFnsLocalesFr]
|
["date-fns/locale/ca" :default dateFnsLocalesCa]
|
||||||
["date-fns/locale/en-US" :as dateFnsLocalesEnUs]
|
["date-fns/locale/en-US" :default dateFnsLocalesEnUs]
|
||||||
["date-fns/locale/zh-CN" :as dateFnsLocalesZhCn]
|
["date-fns/locale/zh-CN" :default dateFnsLocalesZhCn]
|
||||||
["date-fns/locale/es" :as dateFnsLocalesEs]
|
["date-fns/locale/es" :default dateFnsLocalesEs]
|
||||||
["date-fns/locale/ru" :as dateFnsLocalesRu]
|
["date-fns/locale/tr" :default dateFnsLocalesTr]
|
||||||
|
["date-fns/locale/ru" :default dateFnsLocalesRu]
|
||||||
[app.util.object :as obj]))
|
[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
|
(def ^:private locales
|
||||||
#js {:en dateFnsLocalesEnUs
|
#js {:en dateFnsLocalesEnUs
|
||||||
:fr dateFnsLocalesFr
|
:fr dateFnsLocalesFr
|
||||||
|
:tr dateFnsLocalesTr
|
||||||
:es dateFnsLocalesEs
|
:es dateFnsLocalesEs
|
||||||
|
:ca dateFnsLocalesCa
|
||||||
:ru dateFnsLocalesRu
|
:ru dateFnsLocalesRu
|
||||||
:zh_cn dateFnsLocalesZhCn})
|
: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
|
(defn timeago
|
||||||
([v] (timeago v nil))
|
([v] (timeago v nil))
|
||||||
([v {:keys [locale] :or {locale "en"}}]
|
([v {:keys [locale] :or {locale "en"}}]
|
||||||
(when v
|
(when v
|
||||||
|
(let [v (if (datetime? v) (format v :date) v)]
|
||||||
(->> #js {:includeSeconds true
|
(->> #js {:includeSeconds true
|
||||||
:addSuffix true
|
:addSuffix true
|
||||||
:locale (obj/get locales locale)}
|
:locale (obj/get locales locale)}
|
||||||
(dateFnsFormatDistanceToNowStrict v)))))
|
(dateFnsFormatDistanceToNowStrict v))))))
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[cognitect.transit :as t]
|
[cognitect.transit :as t]
|
||||||
[linked.core :as lk]
|
[linked.core :as lk]
|
||||||
[linked.set :as lks]
|
[linked.set :as lks]
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.util.time :as dt]))
|
[app.util.time :as dt]))
|
||||||
|
@ -67,6 +68,22 @@
|
||||||
(def ordered-set-read-handler
|
(def ordered-set-read-handler
|
||||||
(t/read-handler #(into (lk/set) %)))
|
(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
|
;; --- Transit Handlers
|
||||||
|
|
||||||
(def ^:privare +read-handlers+
|
(def ^:privare +read-handlers+
|
||||||
|
@ -75,11 +92,15 @@
|
||||||
"ordered-set" ordered-set-read-handler
|
"ordered-set" ordered-set-read-handler
|
||||||
"jsonblob" blob-read-handler
|
"jsonblob" blob-read-handler
|
||||||
"matrix" matrix-read-handler
|
"matrix" matrix-read-handler
|
||||||
|
"m" date-read-handler
|
||||||
|
"duration" duration-read-handler
|
||||||
"point" point-read-handler})
|
"point" point-read-handler})
|
||||||
|
|
||||||
(def ^:privare +write-handlers+
|
(def ^:privare +write-handlers+
|
||||||
{gmt/Matrix matrix-write-handler
|
{gmt/Matrix matrix-write-handler
|
||||||
Blob blob-write-handler
|
Blob blob-write-handler
|
||||||
|
dt/DateTime date-write-handler
|
||||||
|
dt/Duration duration-write-handler
|
||||||
lks/LinkedSet ordered-set-write-handler
|
lks/LinkedSet ordered-set-write-handler
|
||||||
gpt/Point point-write-handler})
|
gpt/Point point-write-handler})
|
||||||
|
|
||||||
|
|
|
@ -1189,10 +1189,10 @@ dashdash@^1.12.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
assert-plus "^1.0.0"
|
assert-plus "^1.0.0"
|
||||||
|
|
||||||
date-fns@^2.19.0:
|
date-fns@^2.21.1:
|
||||||
version "2.19.0"
|
version "2.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.21.1.tgz#679a4ccaa584c0706ea70b3fa92262ac3009d2b0"
|
||||||
integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==
|
integrity sha512-m1WR0xGiC6j6jNFAyW4Nvh4WxAi4JF4w9jRJwSI8nBmNcyZXPcP9VUQG+6gHQXAmqaGEKDKhOqAtENDC941UkA==
|
||||||
|
|
||||||
dateformat@^3.0.3:
|
dateformat@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
|
@ -2368,11 +2368,6 @@ https-browserify@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||||
|
|
||||||
humanize-duration@~3.25.0:
|
|
||||||
version "3.25.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.25.1.tgz#50e12bf4b3f515ec91106107ee981e8cfe955d6f"
|
|
||||||
integrity sha512-P+dRo48gpLgc2R9tMRgiDRNULPKCmqFYgguwqOO2C0fjO35TgdURDQDANSR1Nt92iHlbHGMxOTnsB8H8xnMa2Q==
|
|
||||||
|
|
||||||
iconv-lite@^0.6.2:
|
iconv-lite@^0.6.2:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
|
||||||
|
@ -3129,6 +3124,11 @@ lru-queue@^0.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es5-ext "~0.10.2"
|
es5-ext "~0.10.2"
|
||||||
|
|
||||||
|
luxon@^1.26.0:
|
||||||
|
version "1.26.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578"
|
||||||
|
integrity sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A==
|
||||||
|
|
||||||
make-iterator@^1.0.0:
|
make-iterator@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
|
resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue