swinging-needle-meter/resources/public/js/compiled/out/cljs_time/format.cljs
2020-10-20 14:44:11 +01:00

539 lines
19 KiB
Clojure

(ns cljs-time.format
"### Utilities for parsing and unparsing DateTimes as Strings.
Parsing and printing are controlled by formatters. You can either use one
of the built in ISO 8601 and a single RFC 822 formatters or define your own, e.g.:
(def built-in-formatter (formatters :basic-date-time))
(def custom-formatter (formatter \"yyyyMMdd\"))
To see a list of available built-in formatters and an example of a date-time
printed in their format:
(show-formatters)
Once you have a formatter, parsing and printing are strait-forward:
=> (parse custom-formatter \"20100311\")
#<DateTime 2010-03-11T00:00:00.000Z>
=> (unparse custom-formatter (date-time 2010 10 3))
\"20101003\"
By default the parse function always returns a DateTime instance with a UTC
time zone, and the unparse function always represents a given DateTime
instance in UTC. A formatter can be modified to different timezones, locales,
etc with the functions with-zone, with-locale, with-chronology, and
with-pivot-year."
(:require
[cljs-time.internal.core :refer [index-of valid-date? format zero-pad]]
[cljs-time.core :as time]
[clojure.set :refer [difference]]
[clojure.string :as string]
[goog.date :as date]
[goog.date.duration :as duration]
[goog.string :as gstring]
[goog.string.format]))
(def months
["January" "February" "March" "April" "May" "June" "July" "August"
"September" "October" "November" "December"])
(def days
["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"])
(defn abbreviate [n s]
(subs s 0 n))
(def ^{:doc "**Note: not all formatters have been implemented yet.**
The pattern syntax is mostly compatible with java.text.SimpleDateFormat -
time zone names cannot be parsed and a few more symbols are supported. All
ASCII letters are reserved as pattern letters, which are defined as follows:
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD
C century of era (>=0) number 20
Y year of era (>=0) year 1996
x weekyear year 1996
w week of weekyear number 27
e day of week number 2
E day of week text Tuesday; Tue
y year year 1996
D day of year number 189
M month of year month July; Jul; 07
d day of month number 10
a halfday of day text PM
K hour of halfday (0~11) number 0
h clockhour of halfday (1~12) number 12
H hour of day (0~23) number 0
k clockhour of day (1~24) number 24
m minute of hour number 30
s second of minute number 55
S fraction of second number 978
a meridiem text am; pm
A meridiem text AM; PM
z time zone text Pacific Standard Time; PST
Z time zone offset/id zone -0800; -08:00; America/Los_Angeles
' escape for text delimiter
'' single quote literal '
The count of pattern letters determine the format.
**Text:** If the number of pattern letters is 4 or more, the full form is used;
otherwise a short or abbreviated form is used if available.
**Number:** The minimum number of digits. Shorter numbers are zero-padded to this
amount.
**Year:** Numeric presentation for year and weekyear fields are handled
specially. For example, if the count of 'y' is 2, the year will be displayed
as the zero-based year of the century, which is two digits.
**Month:** 3 or over, use text, otherwise use number.
**Zone:** 'Z' outputs offset without a colon, 'ZZ' outputs the offset with a
colon, 'ZZZ' or more outputs the zone id.
**Zone names:** Time zone names ('z') cannot be parsed.
Any characters in the pattern that are not in the ranges of ['a'..'z'] and
['A'..'Z'] will be treated as quoted text. For instance, characters like ':',
'.', ' ', '#' and '?' will appear in the resulting time text even they are
not embraced within single quotes."}
date-formatters
(let [d #(.getDate %)
M #(inc (.getMonth %))
y #(.getYear %)
h #(let [hr (mod (.getHours %) 12)]
(if (zero? hr) 12 hr))
a #(if (< (.getHours %) 12) "am" "pm")
A #(if (< (.getHours %) 12) "AM" "PM")
H #(.getHours %)
m #(.getMinutes %)
s #(.getSeconds %)
S #(.getMilliseconds %)
Z #(.getTimezoneOffsetString %)
doy #(.getDayOfYear %)
dow #(.getDay %)]
{"d" d
"dd" #(zero-pad (d %))
"dth" #(let [d (d %)] (str d (case d 1 "st" 2 "nd" 3 "rd" 21 "st" 22 "nd" 23 "rd" 31 "st" "th")))
"dow" #(days (dow %))
"D" doy
"DD" doy
"DDD" doy
"EEE" #(abbreviate 3 (days (dow %)))
"EEEE" #(days (dow %))
"M" M
"MM" #(zero-pad (M %))
"MMM" #(abbreviate 3 (months (dec (M %))))
"MMMM" #(months (dec (M %)))
"yyyy" y
"YYYY" y
"yy" #(mod (y %) 100)
"YY" #(mod (y %) 100)
"xxxx" y
"a" a
"A" A
"h" h
"H" H
"m" m
"s" s
"S" S
"hh" #(zero-pad (h %))
"HH" #(zero-pad (H %))
"mm" #(zero-pad (m %))
"ss" #(zero-pad (s %))
"SSS" #(zero-pad (S %) 3)
"Z" Z
"ZZ" Z
"ww" #(zero-pad (.getWeekNumber %))
"e" dow}))
(defn timezone-adjustment [d timezone-string]
(let [[_ sign hh mm] (string/split timezone-string
#"Z|(?:([-+])(\d{2})(?::?(\d{2}))?)$")]
(when (and sign hh mm)
(let [sign (cond (= sign "-") time/plus
(= sign "+") time/minus)
[hh mm] (map #(js/parseInt % 10) [hh mm])
adjusted (-> d
(sign (time/hours hh))
(sign (time/minutes mm)))]
(.setTime d (.getTime adjusted))))
d))
(def date-parsers
(let [parse-int #(js/parseInt % 10)
assoc-fn (fn [kw] #(assoc %1 kw (parse-int %2)))
y (assoc-fn :years)
d (assoc-fn :days)
M #(assoc %1 :months (dec (parse-int %2)))
h #(assoc %1 :hours (mod (parse-int %2) 12))
a (fn [{:keys [hours] :as date} x]
(if (#{"pm" "p"} (string/lower-case x))
(assoc date :hours (let [hours (+ 12 hours)]
(if (= hours 24) 0 hours)))
date))
H (assoc-fn :hours)
m (assoc-fn :minutes)
s (assoc-fn :seconds)
S (assoc-fn :millis)
MMM #(let [full (first (filter (fn [m]
(re-seq (re-pattern (str "^" %2)) m))
months))]
(M %1 (str (inc (index-of months full)))))
MMMM #(M %1 (str (inc (index-of months %2))))
skip (fn [x & args] x)
tz #(assoc %1 :time-zone %2)]
{"d" ["(\\d{1,2})" d]
"dd" ["(\\d{2})" d]
"D" ["(\\d{1,3})" d]
"DD" ["(\\d{2,3})" d]
"DDD" ["(\\d{3})" d]
"dth" ["(\\d{1,2})(?:st|nd|rd|th)" d]
"M" ["(\\d{1,2})" M]
"MM" ["((?:\\d{2})|(?:\\b\\d{1,2}\\b))" M]
"y" ["(\\d{1,4})" y]
"yy" ["(\\d{2,4})" y]
"yyyy" ["(\\d{4})" y]
"Y" ["(\\d{1,4})" y]
"YY" ["(\\d{2,4})" y]
"YYYY" ["(\\d{4})" y]
"MMM" [(str \( (string/join \| (map (partial abbreviate 3) months)) \)) MMM]
"MMMM" [(str \( (string/join \| months) \)) MMMM]
"E" [(str \( (string/join \| (map (partial abbreviate 3) days)) \)) skip]
"EEE" [(str \( (string/join \| (map (partial abbreviate 3) days)) \)) skip]
"EEEE" [(str \( (string/join \| days) \)) skip]
"dow" [(str \( (string/join \| days) \)) skip]
"a" ["(am|pm|a|p|AM|PM|A|P)" a]
"A" ["(am|pm|a|p|AM|PM|A|P)" a]
"m" ["(\\d{1,2})" m]
"s" ["(\\d{1,2})" s]
"S" ["(\\d{1,2})" S]
"h" ["(\\d{1,2})" h]
"H" ["(\\d{1,2})" H]
"hh" ["(\\d{2})" h]
"HH" ["(\\d{2})" H]
"mm" ["(\\d{2})" m]
"ss" ["(\\d{2})" s]
"SSS" ["(\\d{3})" S]
"Z" ["((?:(?:\\+|-)\\d{2}:?\\d{2})|Z+)" tz]
"ZZ" ["((?:(?:\\+|-)\\d{2}:\\d{2})|Z+)" tz]}))
(def date-setters
{:years #(.setYear %1 %2)
:months #(.setMonth %1 %2)
:days #(.setDate %1 %2)
:hours #(.setHours %1 %2)
:minutes #(.setMinutes %1 %2)
:seconds #(.setSeconds %1 %2)
:millis #(.setMilliseconds %1 %2)
:time-zone timezone-adjustment})
(defn parser-sort-order-pred [parser]
(index-of
["YYYY" "YY" "Y" "yyyy" "yy" "y" "d" "dd" "D" "DD" "DDD" "dth"
"M" "MM" "MMM" "MMMM" "dow" "h" "H" "m" "s" "S" "hh" "HH" "mm" "ss" "a" "A"
"SSS" "Z" "ZZ"]
parser))
(def date-format-pattern
(re-pattern
(str "(" (string/join ")|(" (reverse (sort-by count (keys date-formatters)))) ")")))
(defn old-string-replace [s match replacement]
(.replace s (js/RegExp. (.-source match) "g") replacement))
(defn date-parse-pattern [formatter]
(-> formatter
(old-string-replace #"'([^']+)'" "$1")
(old-string-replace date-format-pattern #(first (date-parsers %)))
re-pattern))
(defn- parser-fn [fmts]
(fn [s]
(->> (interleave (nfirst (re-seq (date-parse-pattern fmts) s))
(map first (re-seq date-format-pattern fmts)))
(partition 2)
(sort-by (comp parser-sort-order-pred second)))))
(defn- formatter-fn [fmts formatters]
(fn [date & [formatter-overrides]]
(let [a (atom {:c 0})]
[(old-string-replace
fmts
#"'([^']+)'"
(fn [x s]
(if (and (seq s) (= \' (first x)) (= \' (last x)))
(let [{:keys [c]} @a
k (str "&&&&" c)]
(swap! a assoc-in [:replace k] (constantly s))
(swap! a update-in [:c] inc)
k)
x)))
(-> (.-source date-format-pattern)
(cond->>
(:replace @a)
(str "(" (string/join ")|(" (keys (:replace @a))) ")|"))
(re-pattern))
#(((merge formatters formatter-overrides (:replace @a)) %) date)])))
(defn formatter
([fmts]
(formatter fmts time/utc))
([fmts dtz]
(with-meta
{:format-str fmts
:formatters date-formatters}
{:type ::formatter})))
(defn formatter-local [fmts]
(with-meta
{:format-str fmts
:formatters (assoc date-formatters
"Z" (constantly "")
"ZZ" (constantly ""))}
{:type ::formatter}))
(defn not-implemented [sym]
#(throw (clj->js {:name :not-implemented
:message (format "%s not implemented yet" (name sym))})))
(defn with-default-year
"Return a copy of a formatter that uses the given default year."
[f default-year]
(assoc f :default-year default-year))
(def ^{:doc "Map of ISO 8601 and a single RFC 822 formatters that can be used
for parsing and, in most cases, printing.
Note: due to current implementation limitations, timezone information
cannot be kept. Although the correct offset will be applied to UTC
time if supplied."}
formatters
{:basic-date (formatter "yyyyMMdd")
:basic-date-time (formatter "yyyyMMdd'T'HHmmss.SSSZ")
:basic-date-time-no-ms (formatter "yyyyMMdd'T'HHmmssZ")
:basic-ordinal-date (formatter "yyyyDDD")
:basic-ordinal-date-time (formatter "yyyyDDD'T'HHmmss.SSSZ")
:basic-ordinal-date-time-no-ms (formatter "yyyyDDD'T'HHmmssZ")
:basic-time (formatter "HHmmss.SSSZ")
:basic-time-no-ms (formatter "HHmmssZ")
:basic-t-time (formatter "'T'HHmmss.SSSZ")
:basic-t-time-no-ms (formatter "'T'HHmmssZ")
:basic-week-date (formatter "xxxx'W'wwe")
:basic-week-date-time (formatter "xxxx'W'wwe'T'HHmmss.SSSZ")
:basic-week-date-time-no-ms (formatter "xxxx'W'wwe'T'HHmmssZ")
:date (formatter "yyyy-MM-dd")
:date-element-parser (not-implemented 'dateElementParser)
:date-hour (formatter "yyyy-MM-dd'T'HH")
:date-hour-minute (formatter "yyyy-MM-dd'T'HH:mm")
:date-hour-minute-second (formatter "yyyy-MM-dd'T'HH:mm:ss")
:date-hour-minute-second-fraction (formatter "yyyy-MM-dd'T'HH:mm:ss.SSS")
:date-hour-minute-second-ms (formatter "yyyy-MM-dd'T'HH:mm:ss.SSS")
:date-opt-time (not-implemented 'dateOptionalTimeParser)
:date-parser (not-implemented 'dateParser)
:date-time (formatter "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
:date-time-no-ms (formatter "yyyy-MM-dd'T'HH:mm:ssZZ")
:date-time-parser (not-implemented 'dateTimeParser)
:hour (formatter "HH")
:hour-minute (formatter "HH:mm")
:hour-minute-second (formatter "HH:mm:ss")
:hour-minute-second-fraction (formatter "HH:mm:ss.SSS")
:hour-minute-second-ms (formatter "HH:mm:ss.SSS")
:local-date-opt-time (not-implemented 'localDateOptionalTimeParser)
:local-date (not-implemented 'localDateParser)
:local-time (not-implemented 'localTimeParser)
:ordinal-date (formatter "yyyy-DDD")
:ordinal-date-time (formatter "yyyy-DDD'T'HH:mm:ss.SSSZZ")
:ordinal-date-time-no-ms (formatter "yyyy-DDD'T'HH:mm:ssZZ")
:time (formatter "HH:mm:ss.SSSZZ")
:time-element-parser (not-implemented 'timeElementParser)
:time-no-ms (formatter "HH:mm:ssZZ")
:time-parser (formatter 'timeParser)
:t-time (formatter "'T'HH:mm:ss.SSSZZ")
:t-time-no-ms (formatter "'T'HH:mm:ssZZ")
:week-date (formatter "xxxx-'W'ww-e")
:week-date-time (formatter "xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ")
:week-date-time-no-ms (formatter "xxxx-'W'ww-e'T'HH:mm:ssZZ")
:weekyear (formatter "xxxx")
:weekyear-week (formatter "xxxx-'W'ww")
:weekyear-week-day (formatter "xxxx-'W'ww-e")
:year (formatter "yyyy")
:year-month (formatter "yyyy-MM")
:year-month-day (formatter "yyyy-MM-dd")
:rfc822 (formatter "EEE, dd MMM yyyy HH:mm:ss Z")
:mysql (formatter "yyyy-MM-dd HH:mm:ss")})
(def ^{:private true} parsers
#{:date-element-parser :date-opt-time :date-parser :date-time-parser
:local-date-opt-time :local-date :local-time :time-element-parser
:time-parser})
(def ^{:private true} printers
(difference (set (keys formatters)) parsers))
(def part-splitter-regex
#"(?:(?!(?:\+|-)\d{2}):(?!\d{2}$))|[^\w:]+|.[TW]|'[^']+'")
(defprotocol IDateMap
(date-map [date]))
(extend-protocol IDateMap
goog.date.Date
(date-map [date]
{:years 0 :months 0 :days 1})
goog.date.DateTime
(date-map [date]
{:years 0 :months 0 :days 1 :hours 0 :minutes 0 :seconds 0 :millis 0})
goog.date.UtcDateTime
(date-map [date]
{:years 0 :months 0 :days 1 :hours 0 :minutes 0 :seconds 0 :millis 0
:time-zone nil}))
(defn parse* [constructor {:keys [format-str default-year] :as fmt} s]
{:pre [(seq s)]}
(let [min-parts (count (string/split s part-splitter-regex))]
(let [parse-fn (parser-fn format-str)
parse-seq (seq (map (fn [[a b]] [a (second (date-parsers b))])
(parse-fn s)))]
(if (>= (count parse-seq) min-parts)
(let [d (new constructor 0 0 0 0 0 0 0)
empty (assoc (date-map d) :years (or default-year 0))
setters (select-keys date-setters (keys empty))]
(->> parse-seq
(reduce (fn [date [part do-parse]] (do-parse date part)) empty)
valid-date?
(merge-with #(%1 d %2) setters))
d)
(throw
(ex-info "The parser could not match the input string."
{:type :parser-no-match}))))))
(defn parse
"Returns a DateTime instance in the UTC time zone obtained by parsing the
given string according to the given formatter."
([fmt s]
(parse* goog.date.UtcDateTime fmt s))
([s]
(first
(for [f (vals formatters)
:let [d (try (parse f s) (catch :default _))]
:when d] d))))
(defn parse-local
"Returns a local DateTime instance obtained by parsing the
given string according to the given formatter."
([fmt s]
(parse* goog.date.DateTime fmt s))
([s]
(first
(for [f (vals formatters)
:let [d (try (parse-local f s) (catch js/Error _ nil))]
:when d] d))))
(defn parse-local-date
"Returns a local Date instance obtained by parsing the
given string according to the given formatter."
([fmt s]
(parse* goog.date.Date fmt s))
([s]
(first
(for [f (vals formatters)
:let [d (try (parse-local-date f s) (catch js/Error _ nil))]
:when d] d))))
(defn unparse
"Returns a string representing the given DateTime instance in UTC and in the
form determined by the given formatter."
[{:keys [format-str formatters]} dt]
{:pre [(not (nil? dt)) (instance? goog.date.DateTime dt)]}
(apply old-string-replace ((formatter-fn format-str formatters) dt)))
(defn unparse-local
"Returns a string representing the given local DateTime instance in the
form determined by the given formatter."
[{:keys [format-str formatters] :as fmt} dt]
{:pre [(not (nil? dt)) (instance? goog.date.DateTime dt)]}
(apply old-string-replace
((formatter-fn format-str formatters) dt (assoc date-formatters
"Z" (constantly "")
"ZZ" (constantly "")))))
(defn unparse-local-date
"Returns a string representing the given local Date instance in the form
determined by the given formatter."
[{:keys [format-str formatters] :as fmt} dt]
{:pre [(not (nil? dt)) (instance? goog.date.Date dt)]}
(apply old-string-replace
((formatter-fn format-str formatters) dt (assoc date-formatters
"Z" (constantly "")
"ZZ" (constantly "")))))
(defn show-formatters
"Shows how a given DateTime, or by default the current time, would be
formatted with each of the available printing formatters."
([] (show-formatters (time/now)))
([dt]
(doseq [p (sort printers)]
(let [fmt (formatters p)]
(print (format "%-40s%s\n" p (unparse fmt dt)))))))
(defprotocol Mappable
(instant->map [instant] "Returns a map representation of the given instant.
It will contain the following keys: :years, :months,
:days, :hours, :minutes and :seconds."))
(defn unparse-duration
"Accepts a Period or Interval and outputs an absolute duration time
in form of \"1 day\", \"2 hours\", \"20 minutes\", \"2 days 1 hour
15 minutes\" etc."
[duration]
(-> duration time/in-millis duration/format))
(defn- to-map [years months days hours minutes seconds millis]
{:years years
:months months
:days days
:hours hours
:minutes minutes
:seconds seconds
:millis millis})
(extend-protocol Mappable
goog.date.UtcDateTime
(instant->map [dt]
(to-map
(.getYear dt)
(inc (.getMonth dt))
(.getDate dt)
(.getHours dt)
(.getMinutes dt)
(.getSeconds dt)
(.getMilliseconds dt)))
cljs-time.core.Period
(instant->map [m]
(time/->period m))
cljs-time.core.Interval
(instant->map [m]
(time/->period m))
cljs.core/PersistentArrayMap
(instant->map [m]
(case (:type (meta m))
:cljs-time.core/period m
:cljs-time.core/interval (time/->period m))))