Skip to content

Commit

Permalink
Add PARSE.DATE and PARSE.DATETIME functions (#112)
Browse files Browse the repository at this point in the history
Add two new function: `PARSE.DATE()` and `PARSE.DATETIME()`

The first argument is a date/time string, second - formatter string as described in section "Patterns for Formatting and Parsing" of [that article](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)

All alpha-characters in formatter string should be quoted using `'` character, eg. `"YYY'T'MM:ss"`
  • Loading branch information
DeLaGuardo committed Apr 20, 2021
1 parent ec8fca8 commit 6834668
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 39 deletions.
102 changes: 63 additions & 39 deletions src/axel_f/excel/date.cljc
Original file line number Diff line number Diff line change
@@ -1,16 +1,57 @@
(ns axel-f.excel.date
(:refer-clojure :exclude [format])
(:require #?(:clj [java-time :as jt]
:cljs [goog.i18n.DateTimeFormat])
(:require #?@(:clj [[java-time :as jt]
[java-time.format :as jtf]]
:cljs [[goog.i18n.DateTimeFormat]
[goog.i18n.DateTimeParse]])
[axel-f.excel.coerce :as coerce])
#?(:clj (:import java.time.ZoneOffset
java.time.ZoneId)))

(defn epoch-milli [date]
#?(:clj (.toEpochMilli (.toInstant (.atZone date (ZoneId/ofOffset "UTC" (ZoneOffset/ofHours 0)))))
:cljs (.getTime date)))

(defn local-date-time []
#?(:clj (jt/local-date-time)
:cljs (let [n (js/Date.)]
(js/Date. (js/Date.UTC (.getUTCFullYear n)
(.getUTCMonth n)
(.getUTCDate n)
(.getUTCHours n)
(.getUTCMinutes n)
(.getUTCSeconds n)
(.getUTCMilliseconds n))))))

(defn local-date []
#?(:clj (jt/local-date)
:cljs (let [n (js/Date.)]
(js/Date. (js/Date.UTC (.getUTCFullYear n)
(.getUTCMonth n)
(.getUTCDate n))))))

(defn format [date fmt]
#?(:clj (jt/format fmt (coerce/inst date))
:cljs (let [formatter (goog.i18n.DateTimeFormat. fmt)]
(.format formatter (coerce/inst date)))))

(defn parse [type date-string pattern]
[(case type
:local-date "LocalDate"
:local-date-time "LocalDateTime")
(epoch-milli #?(:clj
(let [pattern (jtf/formatter pattern)]
((case type
:local-date jt/local-date
:local-date-time jt/local-date-time)
pattern date-string))
:cljs
(let [date (js/Date. (js/Date.UTC 0 0 0 0 0 0 0))]
(.strictParse (goog.i18n.DateTimeParse. pattern)
date-string
date)
date)))])

(defmethod coerce/inst "LocalDate" [[_ millis]]
#?(:clj (.. (java.time.Instant/ofEpochMilli millis) (atZone (java.time.ZoneId/systemDefault)) toLocalDate)
:cljs (js/Date. millis)))
Expand All @@ -36,18 +77,7 @@
(defn NOW*
"Returns the current date and time as a date value."
[]
["LocalDateTime"
#?(:clj (.toEpochMilli (.toInstant (.atZone (jt/local-date-time) (ZoneId/ofOffset "UTC" (ZoneOffset/ofHours 0)))))
:cljs (let [n (js/Date.)
ldt (js/Date. (js/Date.UTC (.getUTCFullYear n)
(.getUTCMonth n)
(.getUTCDate n)
(.getUTCHours n)
(.getUTCMinutes n)
(.getUTCSeconds n)
(.getUTCMilliseconds n)))]
;; (Math/round (/ (.getTime ldt) 1000))
(.getTime ldt)))])
["LocalDateTime" (epoch-milli (local-date-time))])

(def NOW #'NOW*)

Expand All @@ -56,11 +86,7 @@
[]
["LocalDate"
#?(:clj (.toEpochMilli (.toInstant (.atStartOfDay (jt/local-date) (ZoneId/ofOffset "UTC" (ZoneOffset/ofHours 0)))))
:cljs (let [n (js/Date.)
ld (js/Date. (js/Date.UTC (.getUTCFullYear n)
(.getUTCMonth n)
(.getUTCDate n)))]
(.getTime ld)))])
:cljs (epoch-milli (local-date)))])

(def TODAY #'TODAY*)

Expand Down Expand Up @@ -106,30 +132,28 @@

(def YEAR #'YEAR*)

(defn PARSE-DATE*
"Returns the date of the date and time if parsed from first argument using pattern as a second argument."
[^{:doc "A string"} date-string
^{:doc "A pattern sutable to parse date/time strings. Should follow java.time.format.DateTimeFormatter specification"} pattern]
(parse :local-date date-string pattern))

(def PARSE-DATE #'PARSE-DATE*)

(defn PARSE-DATE-TIME*
"Returns the date of the date and time if parsed from first argument using pattern as a second argument."
[^{:doc "A string"} date-string
^{:doc "A pattern sutable to parse date/time strings. Should follow java.time.format.DateTimeFormatter specification"} pattern]
(parse :local-date-time date-string pattern))

(def PARSE-DATE-TIME #'PARSE-DATE-TIME*)

(def env
{"YEAR" YEAR
"MONTH" MONTH
"NOW" NOW
;; "NETWORKDAYS" NETWORKDAYS
;; "DAYS" DAYS
"DATE" DATE
;; "DAYS360" DAYS360
;; "YEARFRAC" YEARFRAC
;; "WEEKDAY" WEEKDAY
;; "TIME" TIME
;; "WORKDAY" WORKDAY
;; "EDATE" EDATE
;; "WEEKNUM" WEEKNUM
;; "EOMONTH" EOMONTH
;; "ISOWEEKNUM" ISOWEEKNUM
"DAY" DAY
;; "MINUTE" MINUTE
;; "WORKDAY.INTL" WORKDAY.INTL
;; "NETWORKDAYS.INTL" NETWORKDAYS.INTL
;; "DATEDIF" DATEDIF
;; "HOUR" HOUR
;; "TIMEVALUE" TIMEVALUE
;; "DATEVALUE" DATEVALUE
;; "SECOND" SECOND
"TODAY" TODAY
})
"PARSE" {"DATE" PARSE-DATE
"DATETIME" PARSE-DATE-TIME}})
4 changes: 4 additions & 0 deletions test/axel_f/date_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@
(t/deftest format-test
(t/is (= "1988-08-21" ((af/compile "coerce.to-string(DATE(1988, 8, 21))"))))
(t/is (= "Aug-88-21" ((af/compile "coerce.to-string(DATE(1988, 8, 21), 'MMM-yy-dd')")))))

(t/deftest parse-test
(t/is (= ["LocalDateTime" 1596193200000]
((af/compile "PARSE.DATETIME('2020-07-31T11:00', \"yyyy-MM-dd'T'HH:mm\")")))))

0 comments on commit 6834668

Please sign in to comment.