最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

clojure - How do you validate a string representing a local date in malli? - Stack Overflow

programmeradmin6浏览0评论

I'm trying to use #malliexperimentaltime

Here is what I have

(ns foobar.core
  (:require [malli.core :as m]
            [malli.registry :as mr]
            [malli.experimental.time :as met]))

;; This should add the malli.experimental.time schemas
(mr/set-default-registry!
  (mr/composite-registry
    (m/default-schemas)
    (met/schemas)))

So, I would expect one of those things to be truthy, but none is:

(m/validate :time/local-date "2024-01-01")
(m/validate [:time/local-date] "2024-01-01")
(m/validate [:time/local-date {:pattern "yyyy-mm-dd"}] "2024-01-01")
(m/validate [:time/local-date {:pattern "yyyy-MM-dd"}] "2024-01-01")
(m/validate [:time/local-date {:pattern "YYYY-MM-DD"}] "2024-01-01")

What am I missing here ?

The doc also mentions a "transformation" feature, is that required here ? #transformation---malliexperimentaltimetransform

I'm trying to use https://github/metosin/malli?tab=readme-ov-file#malliexperimentaltime

Here is what I have

(ns foobar.core
  (:require [malli.core :as m]
            [malli.registry :as mr]
            [malli.experimental.time :as met]))

;; This should add the malli.experimental.time schemas
(mr/set-default-registry!
  (mr/composite-registry
    (m/default-schemas)
    (met/schemas)))

So, I would expect one of those things to be truthy, but none is:

(m/validate :time/local-date "2024-01-01")
(m/validate [:time/local-date] "2024-01-01")
(m/validate [:time/local-date {:pattern "yyyy-mm-dd"}] "2024-01-01")
(m/validate [:time/local-date {:pattern "yyyy-MM-dd"}] "2024-01-01")
(m/validate [:time/local-date {:pattern "YYYY-MM-DD"}] "2024-01-01")

What am I missing here ?

The doc also mentions a "transformation" feature, is that required here ? https://github/metosin/malli?tab=readme-ov-file#transformation---malliexperimentaltimetransform

Share Improve this question asked Mar 10 at 22:02 phtrivierphtrivier 13.4k9 gold badges52 silver badges82 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

I installed it and found out, you fot to parse your string to a `java.time.LocalDate` object:

(ns malli-test.main
  (:require [malli.core :as m]
            [malli.registry :as mr]
            [malli.experimental.time :as met]))

;; Register the experimental time schemas
(mr/set-default-registry!
 (mr/composite-registry
  (m/default-schemas)
  (met/schemas)))

;; Testing time validation
(defn test-time-validation []
  (println "Test results:")
  (println (m/validate :time/local-date (java.time.LocalDate/parse "2024-01-01"))) ; Expected: true
  (println (m/validate [:time/local-date] (java.time.LocalDate/parse "2024-01-01"))) ; Expected: true
  (println (m/validate [:time/local-date {:pattern "yyyy-mm-dd"}] (java.time.LocalDate/parse "2024-01-01"))) ; Expected: true
  (println (m/validate [:time/local-date {:pattern "yyyy-MM-dd"}] (java.time.LocalDate/parse "2024-01-01"))) ; Expected: true
  (println (m/validate [:time/local-date {:pattern "YYYY-MM-DD"}] (java.time.LocalDate/parse "2024-01-01")))) ; Expected: true

(test-time-validation) ;; this will print all `true`!

Since java.time is already in the standard packages, you don't need to require anything extra.

Use custom predicate functions to test for validity

If your aim is to test a date string for validity, then I would use custom predicate functions which then could be used with malli:

(ns malli-test.main
  (:require [malli.core :as m]
            [malli.registry :as mr]
            [malli.experimental.time :as met])
  (:import [java.time LocalDate]
           [java.time.format DateTimeParseException]))

;; Register the experimental time schemas
(mr/set-default-registry!
 (mr/composite-registry
  (m/default-schemas)
  (met/schemas)))

;; Build your custom predicate function to test for validity of a time string
(defn valid-date-string? [date-str]
  (try
    (LocalDate/parse date-str)
    true ;; successfully parsed, otherwise:
    (catch DateTimeParseException _ false)))

(valid-date-string? "2025-01-01") ; => true
(valid-date-string? "2023-02-29") ; => false



;; (LocalDate/parse "2025-01-01")

;; Define a Malli schema using this custom predicate
(def DateSchema [:and string? [:fn valid-date-string?]])

;; Test like this:
(m/validate DateSchema "2025-01-01") ; => true
(m/validate DateSchema "2025-13-99") ; => false (invalid date)
(m/validate DateSchema "not-a-date") ; => false
(m/validate DateSchema "2024-02-29") ; => true  (leap year)
(m/validate DateSchema "2023-02-29") ; => false (not a leap year)

Thanks to @gwang-jim-kim for the help, but I found at what I was missing:

  • I was confusing the role of m/validate, m/decode and m/coerce

    • m/validate check that a value conforms to a schema
    • m/decode will use a transformer to turn a value into something conforming to the schema, but silently fail
    • m/coerce will do the same, but raise
  • In my case, I want to turn the input string into a date, so I was needing m/coerce

  • There are already built-in transformers to pass to m/coerce in the malli.experimental.time.transformer namespace

  • However, for some reason, my project was depending on .clojure/clojure "1.10.1", and this caused an error when loading the workspace (because math was not available.) (This is fixed by depending on more recent version (eg "1.11.1"))

  • Also, I was using the wrong parsing pattern sometimes :facepalm:...

So, in the end, this does what I want:

(ns flight.core
  (:require [malli.core :as m]
            [malli.registry :as mr]
            [malli.experimental.time :as met]
            [malli.experimental.time.transform :as mett]))

(mr/set-default-registry!
  (mr/composite-registry
    (m/default-schemas)
    (met/schemas)))

(def schema [:time/local-date {:pattern "yyyy-MM-dd"}] )

(m/validate schema "2024-01-01") ;;=> false, the string is not a LocalDate 

(m/coerce schema "2024-01-01" mett/time-transformer) ;;=> this returns a java.time.LocalDate

(m/decode schema "2024-01-01x" mett/time-transformer) ;;=> this returns the string "2024-01-01x" because decode silently ignores transformation that raise error

(m/coerce schema "2024-01-01x" mett/time-transformer) ;;=> this raises an error

(m/validate schema (m/decode schema "2024-01-01x" mett/time-transformer)) ;;=> this returns false

(m/validate schema (m/decode schema "2024-01-01" mett/time-transformer)) ;;=> this returns true
发布评论

评论列表(0)

  1. 暂无评论