Localize.Inputs.Date.Validator (Localize.Inputs.Date v0.1.1)

Copy Markdown View Source

Server-side validation for parsed date and date-range values.

Pure Elixir, no Ecto dependency. The Ecto changeset bridge is in Localize.Inputs.Date.Changeset.

Summary

Functions

Validates a parsed Date.t/0 against bounds, weekday restrictions, and required-ness.

Validates a parsed Date.Range.t/0 against bounds, span, weekend restrictions, and required-ness.

Functions

validate_date(value, options \\ [])

@spec validate_date(term(), Keyword.t()) ::
  :ok | {:error, Localize.Inputs.ValidationError.t()}

Validates a parsed Date.t/0 against bounds, weekday restrictions, and required-ness.

Arguments

  • value is a Date.t/0 or nil.

  • options is a keyword list of options.

Options

  • :required — when true, nil is rejected.

  • :min — minimum allowed date (Date or ISO-8601 string).

  • :max — maximum allowed date.

  • :not_weekend — when true, rejects Saturday and Sunday. To customise which weekdays count as "weekend" per locale, pass :weekend_days as a list of 1..7 (1 = Monday).

  • :on_or_after — alias for :min. When both are given, the stricter (later) bound wins.

  • :on_or_before — alias for :max. When both are given, the stricter (earlier) bound wins.

  • :business_days_only — when true, rejects any date falling on the locale's weekend (per :weekend_days or the default [6, 7] = Sat/Sun). Equivalent to :not_weekend but with the more business-domain naming. Future extensions may also reject locale-aware public holidays via a :holidays option (not yet wired).

Returns

  • :ok when every check passes.

  • {:error, %Localize.Inputs.ValidationError{errors: [{atom(), String.t()}]}} with one entry per failing check, in the order :required, :min, :max, :weekend.

Examples

iex> Localize.Inputs.Date.Validator.validate_date(~D[2026-05-16], min: ~D[2026-01-01])
:ok

iex> {:error, %Localize.Inputs.ValidationError{errors: errors}} =
...>   Localize.Inputs.Date.Validator.validate_date(~D[2025-01-01], min: ~D[2026-01-01])
iex> Keyword.get(errors, :min) =~ "2026-01-01"
true

iex> {:error, %Localize.Inputs.ValidationError{errors: errors}} =
...>   Localize.Inputs.Date.Validator.validate_date(nil, required: true)
iex> errors
[{:required, "is required"}]

validate_date_range(value, options \\ [])

@spec validate_date_range(term(), Keyword.t()) ::
  :ok | {:error, Localize.Inputs.ValidationError.t()}

Validates a parsed Date.Range.t/0 against bounds, span, weekend restrictions, and required-ness.

Arguments

  • value is a Date.Range.t/0 or nil.

  • options is a keyword list of options.

Options

  • :required — when true, nil is rejected.

  • :min, :max — bounds that both endpoints must satisfy.

  • :min_span — minimum span in days (inclusive of both endpoints, so ~D[2026-01-01]..~D[2026-01-03] has span 3).

  • :max_span — maximum span in days.

  • :disallow_inverted — when true, rejects descending ranges. The range parser already rejects inverted ranges by default; this is here for direct validator use.

Returns

  • :ok when every check passes.

  • {:error, ValidationError.t()} with errors keyed by :required, :min, :max, :min_span, :max_span, :inverted.

Examples

iex> range = Date.range(~D[2026-05-01], ~D[2026-05-07])
iex> Localize.Inputs.Date.Validator.validate_date_range(range, min_span: 5, max_span: 10)
:ok

iex> range = Date.range(~D[2026-05-01], ~D[2026-05-03])
iex> {:error, %Localize.Inputs.ValidationError{errors: errors}} =
...>   Localize.Inputs.Date.Validator.validate_date_range(range, min_span: 5)
iex> Keyword.get(errors, :min_span) =~ "5"
true