diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a6daaf..3671c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changes * 2.4.next in progress + * Address [#503](https://github.com/seancorfield/honeysql/issues/503) by adding `:at-time-zone` special syntax. * Address [#504](https://github.com/seancorfield/honeysql/issues/504) for BigQuery support, by adding special syntax for ignore/respect nulls, as well as new `:distinct` and `:expr` clauses to allow expressions to be qualified with SQL clauses. The latter will probably be useful for other dialects too. * 2.4.1066 -- 2023-08-27 diff --git a/doc/special-syntax.md b/doc/special-syntax.md index 009e2ba..a29e422 100644 --- a/doc/special-syntax.md +++ b/doc/special-syntax.md @@ -68,6 +68,19 @@ In addition, the argument to `:array` is treated as a literal sequence of Clojur ;;=> ["SELECT ARRAY[inline, (?, ?, ?)] AS arr" 1 2 3] ``` +## at time zone + +Accepts two arguments: an expression (assumed to be a date/time of some sort) +and a time zone name or identifier (can be a string, a symbol, or a keyword): + +```clojure +(sql/format-expr [:at-time-zone [:now] :utc]) +;;=> ["NOW() AT TIME ZONE 'UTC'"] +``` + +The time zone name or identifier will be inlined (as a string) and therefore +cannot be an expression. + ## between Accepts three arguments: an expression, a lower bound, and diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 0ecf3ca..ff3e90d 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -1632,6 +1632,7 @@ :primary-key #'function-0 :references #'function-1 :unique #'function-1-opt + ;; dynamic dotted name creation: :. (fn [_ [expr col subcol]] (let [[sql & params] (format-expr expr)] (into [(str sql "." (format-entity col) @@ -1658,6 +1659,13 @@ (let [[sqls params] (format-expr-list arr) type-str (when type (str "::" (sql-kw type) "[]"))] (into [(str "ARRAY[" (str/join ", " sqls) "]" type-str)] params))) + :at-time-zone + (fn [_ [expr tz]] + (let [[sql & params] (format-expr expr {:nested true}) + [tz-sql & _] + (binding [*inline* true] + (format-expr (if (ident? tz) (name tz) tz)))] + (into [(str sql " AT TIME ZONE " tz-sql)] params))) :between (fn [_ [x a b]] (let [[sql-x & params-x] (format-expr x {:nested true}) diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index 5898828..987b7fb 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -1261,6 +1261,14 @@ ORDER BY id = ? DESC (is (= ["INSERT INTO foo (bar) OUTPUT inserted.* VALUES (?)" 1] (sut/format {:insert-into :foo :columns [:bar] :output [:inserted.*] :values [[1]]})))) +(deftest at-time-zone-503 + (is (= ["SELECT foo AT TIME ZONE 'UTC'"] + (sut/format {:select [[[:at-time-zone :foo "UTC"]]]}))) + (is (= ["SELECT foo AT TIME ZONE 'UTC'"] + (sut/format {:select [[[:at-time-zone :foo :UTC]]]}))) + (is (= ["SELECT FOO(bar) AT TIME ZONE 'UTC'"] + (sut/format {:select [[[:at-time-zone [:foo :bar] :UTC]]]})))) + (comment ;; partial workaround for #407: (sut/format {:select :f.* :from [[:foo [:f :for :system-time]]] :where [:= :f.id 1]})