Current section

Files

Jump to
zotonic_mod_base src validators validator_base_date.erl
Raw

src/validators/validator_base_date.erl

%% @author Michael Connors <michael@bring42.net>
%% @copyright 2012-2026 Michael Connors
%%
%% Copyright 2012-2026 Michael Connors
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(validator_base_date).
-moduledoc("
Validate input date against a given date format.
A quick guide to validating a date in Zotonic:
```django
<input type=\"text\" id=\"my_date\" name=\"my_date\" value=\"\" />
{% validate id=\"my_date\" type={date separator=\"-\" format=\"b\"} %}
```
This code validates when the user enters a date in the following format:
```none
yyyy-mm-dd
```
Arguments
---------
| Argument | Description | Example |
| --------- | -------------------------------------------------------------------------------- | --------------- |
| separator | Character used to separate date parts, such as `/` `-` `\\\\`. Defaults to `\"-\"`. | `separator=\"-\"` |
| format | Date format, big endian (starting with year), little endian (starting with day) or middle endian (starting with month). Defaults to `\"l\"` (little). | `format=\"m\"` |
").
-include_lib("zotonic_core/include/zotonic.hrl").
-export([render_validator/5, validate/5]).
render_validator(date, TriggerId, _TargetId, Args, _Context) ->
Format = proplists:get_value(format, Args, <<"l">>),
Separator = proplists:get_value(separator, Args, <<"-">>),
JsObject = z_utils:js_object(z_validation:rename_args(Args)),
Script = [<<"z_add_validator(\"">>,TriggerId,<<"\", \"date\", ">>, JsObject, <<");\n">>],
{[Format, Separator], Script}.
is_valid(Day, Month, Year) ->
try
calendar:valid_date(z_convert:to_integer(Year), z_convert:to_integer(Month), z_convert:to_integer(Day))
catch
_:_ -> false
end.
is_valid(Day, Month, Year, <<"l">>) ->
is_valid(Day, Month, Year);
is_valid(Month, Day, Year, <<"m">>) ->
is_valid(Day, Month, Year);
is_valid(Year, Month, Day, <<"b">>) ->
is_valid(Day, Month, Year);
is_valid(_, _, _, _) ->
false.
-spec validate(term(), term(), term(), list(), z:context()) ->
{{ok, term()}, z:context()} | {{error, term(), term()}, z:context()}.
%% Error = invalid | novalue | {script, Script}
validate(date, Id, Value, [Format, Separator], Context) ->
case lists:member(Format, [<<"l">>, <<"m">>, <<"b">>]) of
true ->
case z_string:trim(Value) of
<<>> -> {{ok, <<>>}, Context};
Trimmed ->
SepB = z_convert:to_binary(Separator),
case binary:split(Trimmed, SepB, [ global ]) of
[ Part1, Part2, Part3 ] ->
case is_valid(Part1, Part2, Part3, Format) of
true ->
{{ok, Trimmed}, Context};
false ->
{{error, Id, invalid}, Context}
end;
_Others -> {{error, Id, invalid}, Context}
end
end;
false ->
{{error, Id, invalid}, Context}
end.