|
|
@@ -1,270 +0,0 @@
|
|
|
-%%--------------------------------------------------------------------
|
|
|
-%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
|
-%%
|
|
|
-%% 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(emqx_rule_date).
|
|
|
-
|
|
|
--export([date/3, date/4, parse_date/4]).
|
|
|
-
|
|
|
--export([
|
|
|
- is_int_char/1,
|
|
|
- is_symbol_char/1,
|
|
|
- is_m_char/1
|
|
|
-]).
|
|
|
-
|
|
|
--record(result, {
|
|
|
- %%year()
|
|
|
- year = "1970" :: string(),
|
|
|
- %%month()
|
|
|
- month = "1" :: string(),
|
|
|
- %%day()
|
|
|
- day = "1" :: string(),
|
|
|
- %%hour()
|
|
|
- hour = "0" :: string(),
|
|
|
- %%minute() %% epoch in millisecond precision
|
|
|
- minute = "0" :: string(),
|
|
|
- %%second() %% epoch in millisecond precision
|
|
|
- second = "0" :: string(),
|
|
|
- %%integer() %% zone maybe some value
|
|
|
- zone = "+00:00" :: string()
|
|
|
-}).
|
|
|
-
|
|
|
-%% -type time_unit() :: 'microsecond'
|
|
|
-%% | 'millisecond'
|
|
|
-%% | 'nanosecond'
|
|
|
-%% | 'second'.
|
|
|
-%% -type offset() :: [byte()] | (Time :: integer()).
|
|
|
-date(TimeUnit, Offset, FormatString) ->
|
|
|
- date(TimeUnit, Offset, FormatString, erlang:system_time(TimeUnit)).
|
|
|
-
|
|
|
-date(TimeUnit, Offset, FormatString, TimeEpoch) ->
|
|
|
- [Head | Other] = string:split(FormatString, "%", all),
|
|
|
- R = create_tag([{st, Head}], Other),
|
|
|
- Res = lists:map(
|
|
|
- fun(Expr) ->
|
|
|
- eval_tag(rmap(make_time(TimeUnit, Offset, TimeEpoch)), Expr)
|
|
|
- end,
|
|
|
- R
|
|
|
- ),
|
|
|
- lists:concat(Res).
|
|
|
-
|
|
|
-parse_date(TimeUnit, Offset, FormatString, InputString) ->
|
|
|
- [Head | Other] = string:split(FormatString, "%", all),
|
|
|
- R = create_tag([{st, Head}], Other),
|
|
|
- IsZ = fun(V) ->
|
|
|
- case V of
|
|
|
- {tag, $Z} -> true;
|
|
|
- _ -> false
|
|
|
- end
|
|
|
- end,
|
|
|
- R1 = lists:filter(IsZ, R),
|
|
|
- IfFun = fun(Con, A, B) ->
|
|
|
- case Con of
|
|
|
- [] -> A;
|
|
|
- _ -> B
|
|
|
- end
|
|
|
- end,
|
|
|
- Res = parse_input(FormatString, InputString),
|
|
|
- Str =
|
|
|
- Res#result.year ++ "-" ++
|
|
|
- Res#result.month ++ "-" ++
|
|
|
- Res#result.day ++ "T" ++
|
|
|
- Res#result.hour ++ ":" ++
|
|
|
- Res#result.minute ++ ":" ++
|
|
|
- Res#result.second ++
|
|
|
- IfFun(R1, Offset, Res#result.zone),
|
|
|
- calendar:rfc3339_to_system_time(Str, [{unit, TimeUnit}]).
|
|
|
-
|
|
|
-mlist(R) ->
|
|
|
- %% %H Shows hour in 24-hour format [15]
|
|
|
- [
|
|
|
- {$H, R#result.hour},
|
|
|
- %% %M Displays minutes [00-59]
|
|
|
- {$M, R#result.minute},
|
|
|
- %% %S Displays seconds [00-59]
|
|
|
- {$S, R#result.second},
|
|
|
- %% %y Displays year YYYY [2021]
|
|
|
- {$y, R#result.year},
|
|
|
- %% %m Displays the number of the month [01-12]
|
|
|
- {$m, R#result.month},
|
|
|
- %% %d Displays the number of the month [01-12]
|
|
|
- {$d, R#result.day},
|
|
|
- %% %Z Displays Time zone
|
|
|
- {$Z, R#result.zone}
|
|
|
- ].
|
|
|
-
|
|
|
-rmap(Result) ->
|
|
|
- maps:from_list(mlist(Result)).
|
|
|
-
|
|
|
-support_char() -> "HMSymdZ".
|
|
|
-
|
|
|
-create_tag(Head, []) ->
|
|
|
- Head;
|
|
|
-create_tag(Head, [Val1 | RVal]) ->
|
|
|
- case Val1 of
|
|
|
- [] ->
|
|
|
- create_tag(Head ++ [{st, [$%]}], RVal);
|
|
|
- [H | Other] ->
|
|
|
- case lists:member(H, support_char()) of
|
|
|
- true -> create_tag(Head ++ [{tag, H}, {st, Other}], RVal);
|
|
|
- false -> create_tag(Head ++ [{st, [$% | Val1]}], RVal)
|
|
|
- end
|
|
|
- end.
|
|
|
-
|
|
|
-eval_tag(_, {st, Str}) ->
|
|
|
- Str;
|
|
|
-eval_tag(Map, {tag, Char}) ->
|
|
|
- maps:get(Char, Map, "undefined").
|
|
|
-
|
|
|
-%% make_time(TimeUnit, Offset) ->
|
|
|
-%% make_time(TimeUnit, Offset, erlang:system_time(TimeUnit)).
|
|
|
-make_time(TimeUnit, Offset, TimeEpoch) ->
|
|
|
- Res = calendar:system_time_to_rfc3339(
|
|
|
- TimeEpoch,
|
|
|
- [{unit, TimeUnit}, {offset, Offset}]
|
|
|
- ),
|
|
|
- [
|
|
|
- Y1,
|
|
|
- Y2,
|
|
|
- Y3,
|
|
|
- Y4,
|
|
|
- $-,
|
|
|
- Mon1,
|
|
|
- Mon2,
|
|
|
- $-,
|
|
|
- D1,
|
|
|
- D2,
|
|
|
- _T,
|
|
|
- H1,
|
|
|
- H2,
|
|
|
- $:,
|
|
|
- Min1,
|
|
|
- Min2,
|
|
|
- $:,
|
|
|
- S1,
|
|
|
- S2
|
|
|
- | TimeStr
|
|
|
- ] = Res,
|
|
|
- IsFractionChar = fun(C) -> C >= $0 andalso C =< $9 orelse C =:= $. end,
|
|
|
- {FractionStr, UtcOffset} = lists:splitwith(IsFractionChar, TimeStr),
|
|
|
- #result{
|
|
|
- year = [Y1, Y2, Y3, Y4],
|
|
|
- month = [Mon1, Mon2],
|
|
|
- day = [D1, D2],
|
|
|
- hour = [H1, H2],
|
|
|
- minute = [Min1, Min2],
|
|
|
- second = [S1, S2] ++ FractionStr,
|
|
|
- zone = UtcOffset
|
|
|
- }.
|
|
|
-
|
|
|
-is_int_char(C) ->
|
|
|
- C >= $0 andalso C =< $9.
|
|
|
-is_symbol_char(C) ->
|
|
|
- C =:= $- orelse C =:= $+.
|
|
|
-is_m_char(C) ->
|
|
|
- C =:= $:.
|
|
|
-
|
|
|
-parse_char_with_fun(_, []) ->
|
|
|
- error(null_input);
|
|
|
-parse_char_with_fun(ValidFun, [C | Other]) ->
|
|
|
- Res =
|
|
|
- case erlang:is_function(ValidFun) of
|
|
|
- true -> ValidFun(C);
|
|
|
- false -> erlang:apply(emqx_rule_date, ValidFun, [C])
|
|
|
- end,
|
|
|
- case Res of
|
|
|
- true -> {C, Other};
|
|
|
- false -> error({unexpected, [C | Other]})
|
|
|
- end.
|
|
|
-parse_string([], Input) ->
|
|
|
- {[], Input};
|
|
|
-parse_string([C | Other], Input) ->
|
|
|
- {C1, Input1} = parse_char_with_fun(fun(V) -> V =:= C end, Input),
|
|
|
- {Res, Input2} = parse_string(Other, Input1),
|
|
|
- {[C1 | Res], Input2}.
|
|
|
-
|
|
|
-parse_times(0, _, Input) ->
|
|
|
- {[], Input};
|
|
|
-parse_times(Times, Fun, Input) ->
|
|
|
- {C1, Input1} = parse_char_with_fun(Fun, Input),
|
|
|
- {Res, Input2} = parse_times((Times - 1), Fun, Input1),
|
|
|
- {[C1 | Res], Input2}.
|
|
|
-
|
|
|
-parse_int_times(Times, Input) ->
|
|
|
- parse_times(Times, is_int_char, Input).
|
|
|
-
|
|
|
-parse_fraction(Input) ->
|
|
|
- IsFractionChar = fun(C) -> C >= $0 andalso C =< $9 orelse C =:= $. end,
|
|
|
- lists:splitwith(IsFractionChar, Input).
|
|
|
-
|
|
|
-parse_second(Input) ->
|
|
|
- {M, Input1} = parse_int_times(2, Input),
|
|
|
- {M1, Input2} = parse_fraction(Input1),
|
|
|
- {M ++ M1, Input2}.
|
|
|
-
|
|
|
-parse_zone(Input) ->
|
|
|
- {S, Input1} = parse_char_with_fun(is_symbol_char, Input),
|
|
|
- {M, Input2} = parse_int_times(2, Input1),
|
|
|
- {C, Input3} = parse_char_with_fun(is_m_char, Input2),
|
|
|
- {V, Input4} = parse_int_times(2, Input3),
|
|
|
- {[S | M ++ [C | V]], Input4}.
|
|
|
-
|
|
|
-mlist1() ->
|
|
|
- maps:from_list(
|
|
|
- %% %H Shows hour in 24-hour format [15]
|
|
|
- [
|
|
|
- {$H, fun(Input) -> parse_int_times(2, Input) end},
|
|
|
- %% %M Displays minutes [00-59]
|
|
|
- {$M, fun(Input) -> parse_int_times(2, Input) end},
|
|
|
- %% %S Displays seconds [00-59]
|
|
|
- {$S, fun(Input) -> parse_second(Input) end},
|
|
|
- %% %y Displays year YYYY [2021]
|
|
|
- {$y, fun(Input) -> parse_int_times(4, Input) end},
|
|
|
- %% %m Displays the number of the month [01-12]
|
|
|
- {$m, fun(Input) -> parse_int_times(2, Input) end},
|
|
|
- %% %d Displays the number of the month [01-12]
|
|
|
- {$d, fun(Input) -> parse_int_times(2, Input) end},
|
|
|
- %% %Z Displays Time zone
|
|
|
- {$Z, fun(Input) -> parse_zone(Input) end}
|
|
|
- ]
|
|
|
- ).
|
|
|
-
|
|
|
-update_result($H, Res, Str) -> Res#result{hour = Str};
|
|
|
-update_result($M, Res, Str) -> Res#result{minute = Str};
|
|
|
-update_result($S, Res, Str) -> Res#result{second = Str};
|
|
|
-update_result($y, Res, Str) -> Res#result{year = Str};
|
|
|
-update_result($m, Res, Str) -> Res#result{month = Str};
|
|
|
-update_result($d, Res, Str) -> Res#result{day = Str};
|
|
|
-update_result($Z, Res, Str) -> Res#result{zone = Str}.
|
|
|
-
|
|
|
-parse_tag(Res, {st, St}, InputString) ->
|
|
|
- {_A, B} = parse_string(St, InputString),
|
|
|
- {Res, B};
|
|
|
-parse_tag(Res, {tag, St}, InputString) ->
|
|
|
- Fun = maps:get(St, mlist1()),
|
|
|
- {A, B} = Fun(InputString),
|
|
|
- NRes = update_result(St, Res, A),
|
|
|
- {NRes, B}.
|
|
|
-
|
|
|
-parse_tags(Res, [], _) ->
|
|
|
- Res;
|
|
|
-parse_tags(Res, [Tag | Others], InputString) ->
|
|
|
- {NRes, B} = parse_tag(Res, Tag, InputString),
|
|
|
- parse_tags(NRes, Others, B).
|
|
|
-
|
|
|
-parse_input(FormatString, InputString) ->
|
|
|
- [Head | Other] = string:split(FormatString, "%", all),
|
|
|
- R = create_tag([{st, Head}], Other),
|
|
|
- parse_tags(#result{}, R, InputString).
|