/src/support/z_convert.erl

https://code.google.com/p/zotonic/ · Erlang · 304 lines · 225 code · 38 blank · 41 comment · 3 complexity · fa1c47de29c572e45a97641895750a1e MD5 · raw file

  1. %% @author Rusty Klophaus
  2. %% @copyright Copyright (c) 2008-2009 Rusty Klophaus, Copyright (c) 2009 Marc Worrell
  3. %%
  4. %% @doc Conversion functions for all kinds of data types. Changes to
  5. %% Rusty's version: added date conversion, undefined handling and more
  6. %% to_bool cases.
  7. %% Copyright 2009 Marc Worrell
  8. %%
  9. %% Licensed under the Apache License, Version 2.0 (the "License");
  10. %% you may not use this file except in compliance with the License.
  11. %% You may obtain a copy of the License at
  12. %%
  13. %% http://www.apache.org/licenses/LICENSE-2.0
  14. %%
  15. %% Unless required by applicable law or agreed to in writing, software
  16. %% distributed under the License is distributed on an "AS IS" BASIS,
  17. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. %% See the License for the specific language governing permissions and
  19. %% limitations under the License.
  20. -module(z_convert).
  21. -author("Rusty Klophaus").
  22. -author("Marc Worrell <marc@worrell.nl>").
  23. -author("Arjan Scherpenisse <arjan@scherpenisse.net>").
  24. -export ([
  25. clean_lower/1,
  26. to_list/1,
  27. to_flatlist/1,
  28. to_atom/1,
  29. to_binary/1,
  30. to_integer/1,
  31. to_float/1,
  32. to_bool_strict/1,
  33. to_bool/1,
  34. to_utc/1,
  35. to_localtime/1,
  36. to_datetime/1,
  37. to_date/1,
  38. to_time/1,
  39. to_isotime/1,
  40. to_json/1,
  41. ip_to_list/1
  42. ]).
  43. -include("zotonic.hrl").
  44. %%% CONVERSION %%%
  45. clean_lower(L) -> string:strip(z_string:to_lower(to_list(L))).
  46. to_list(undefined) -> [];
  47. to_list(<<>>) -> [];
  48. to_list({rsc_list, L}) -> L;
  49. to_list(L) when is_list(L) -> lists:flatten(L);
  50. to_list(A) when is_atom(A) -> atom_to_list(A);
  51. to_list(B) when is_binary(B) -> binary_to_list(B);
  52. to_list(I) when is_integer(I) -> integer_to_list(I);
  53. to_list(F) when is_float(F) -> float_to_list(F).
  54. to_flatlist(L) when is_list(L) ->
  55. case z_string:is_string(L) of
  56. true -> L;
  57. false -> to_list(iolist_to_binary(L))
  58. end;
  59. to_flatlist(L) ->
  60. to_list(L).
  61. to_atom(<<>>) -> undefined;
  62. to_atom([]) -> undefined;
  63. to_atom(A) when is_atom(A) -> A;
  64. to_atom(B) when is_binary(B) -> to_atom(binary_to_list(B));
  65. to_atom(I) when is_integer(I) -> to_atom(integer_to_list(I));
  66. to_atom(L) when is_list(L) -> list_to_atom(binary_to_list(list_to_binary(L))).
  67. to_binary(undefined) -> <<>>;
  68. to_binary(A) when is_atom(A) -> to_binary(atom_to_list(A));
  69. to_binary(B) when is_binary(B) -> B;
  70. to_binary(I) when is_integer(I) -> to_binary(integer_to_list(I));
  71. to_binary(F) when is_float(F) -> to_binary(float_to_list(F));
  72. to_binary(L) when is_list(L) -> list_to_binary(L).
  73. to_integer(undefined) -> undefined;
  74. to_integer([]) -> undefined;
  75. to_integer(A) when is_atom(A) -> to_integer(atom_to_list(A));
  76. to_integer(B) when is_binary(B) -> to_integer(binary_to_list(B));
  77. to_integer(I) when is_integer(I) -> I;
  78. to_integer(F) when is_float(F) -> erlang:round(F);
  79. to_integer([C]) when is_integer(C) andalso (C > $9 orelse C < $0) -> C;
  80. to_integer(L) when is_list(L) -> list_to_integer(L).
  81. to_float(undefined) -> undefined;
  82. to_float([]) -> undefined;
  83. to_float(A) when is_atom(A) -> to_float(atom_to_list(A));
  84. to_float(B) when is_binary(B) -> to_float(binary_to_list(B));
  85. to_float(I) when is_integer(I) -> I + 0.0;
  86. to_float(F) when is_float(F) -> F;
  87. to_float(L) when is_list(L) ->
  88. case lists:member($., L) of
  89. true -> list_to_float(L);
  90. false -> list_to_float(L++".0") %% list_to_float("1") gives a badarg
  91. end.
  92. %% @doc Quite loose conversion of values to boolean
  93. to_bool("false") -> false;
  94. to_bool("FALSE") -> false;
  95. to_bool("n") -> false;
  96. to_bool("N") -> false;
  97. to_bool("NO") -> false;
  98. to_bool(<<"false">>) -> false;
  99. to_bool(<<"FALSE">>) -> false;
  100. to_bool(<<"n">>) -> false;
  101. to_bool(<<"N">>) -> false;
  102. to_bool(<<"NO">>) -> false;
  103. to_bool("disabled") -> false;
  104. to_bool(<<"disabled">>) -> false;
  105. to_bool("DISABLED") -> false;
  106. to_bool(<<"DISABLED">>) -> false;
  107. to_bool([0]) -> false;
  108. to_bool(V) -> to_bool_strict(V).
  109. % @doc Convert values to boolean values according to the Django rules
  110. to_bool_strict(undefined) -> false;
  111. to_bool_strict(false) -> false;
  112. to_bool_strict(0) -> false;
  113. to_bool_strict(0.0) -> false;
  114. to_bool_strict(<<>>) -> false;
  115. to_bool_strict(<<0>>) -> false;
  116. to_bool_strict([]) -> false;
  117. to_bool_strict({rsc_list, []}) -> false;
  118. to_bool_strict(#m{value=V}) -> to_bool(V);
  119. to_bool_strict(#m_search_result{result=V}) -> to_bool(V);
  120. to_bool_strict(#search_result{result=[]}) -> false;
  121. to_bool_strict("0") -> false;
  122. to_bool_strict(<<"0">>) -> false;
  123. to_bool_strict(_) -> true.
  124. %% @doc Convert a local date time to utc
  125. to_utc(undefined) ->
  126. undefined;
  127. to_utc({{9999,_,_}, _}) ->
  128. ?ST_JUTTEMIS;
  129. to_utc(D) ->
  130. case catch calendar:local_time_to_universal_time_dst(D) of
  131. [] -> D; % This time never existed in the local time, just take it as-is
  132. [UTC] -> UTC;
  133. [DstUTC, _UTC] -> DstUTC;
  134. {'EXIT', _} -> D
  135. end.
  136. %% @doc Convert a utc date time to local
  137. to_localtime(undefined) ->
  138. undefined;
  139. to_localtime({{9999,_,_},_}) ->
  140. ?ST_JUTTEMIS;
  141. to_localtime(D) ->
  142. case catch calendar:universal_time_to_local_time(D) of
  143. {'EXIT', _} -> D;
  144. LocalD -> LocalD
  145. end.
  146. %% @doc Convert an input to a datetime, using to_date/1 and to_time/1.
  147. to_datetime({{_,_,_},{_,_,_}} = DT) -> DT;
  148. to_datetime({_,_,_} = D) -> {D, {0,0,0}};
  149. to_datetime(L) when is_list(L) ->
  150. try
  151. case string:tokens(L, " T") of
  152. [Date,Time] ->
  153. WithTZ = fun(Tm, Tz, Mul) ->
  154. TZTime = to_time(Tz),
  155. Add = calendar:datetime_to_gregorian_seconds({{0,1,1},TZTime}),
  156. Secs = calendar:datetime_to_gregorian_seconds({to_date(Date), to_time(Tm)}),
  157. calendar:universal_time_to_local_time(calendar:gregorian_seconds_to_datetime(Secs+(Mul*Add)))
  158. end,
  159. case string:tokens(Time, "+") of
  160. [Time1, TZ] ->
  161. %% Timestamp with positive time zone
  162. WithTZ(Time1, TZ, -1);
  163. _ ->
  164. case string:tokens(Time, "-") of
  165. [Time1, TZ] ->
  166. %% Timestamp with negative time zone
  167. WithTZ(Time1, TZ, 1);
  168. _ ->
  169. case lists:reverse(Time) of
  170. [$Z|Rest] ->
  171. %% Timestamp ending on Z (= UTC)
  172. calendar:universal_time_to_local_time({to_date(Date), to_time(lists:reverse(Rest))});
  173. _ ->
  174. %% Timestamp without time zone
  175. {to_date(Date), to_time(Time)}
  176. end
  177. end
  178. end;
  179. [Date] ->
  180. {to_date(Date), {0,0,0}}
  181. end
  182. catch
  183. _:_ -> undefined
  184. end;
  185. to_datetime(undefined) ->
  186. undefined.
  187. %% @doc Convert an input to a date.
  188. to_date({_,_,_} = D) -> D;
  189. to_date(L) when is_list(L) ->
  190. case string:tokens(L, "-/") of
  191. [D,M,Y] when length(Y) =:= 4 ->
  192. {to_integer(Y),to_integer(M),to_integer(D)};
  193. [Y,M,D] ->
  194. {to_integer(Y),to_integer(M),to_integer(D)}
  195. end.
  196. %% @doc Convert an input to a time.
  197. to_time({_,_,_} = D) -> D;
  198. to_time(L) when is_list(L) ->
  199. [H,I,S|_] = lists:flatten([[to_integer(X) ||X <- string:tokens(L, ":.")], 0, 0]),
  200. {H,I,S}.
  201. %% @doc Convert a datetime (in local time) to an ISO time string (in universal time).
  202. %% @spec to_isotime(DateTime) -> string()
  203. to_isotime(DateTime) ->
  204. z_convert:to_list(erlydtl_dateformat:format(hd(calendar:local_time_to_universal_time_dst(DateTime)), "Y-m-d\\TH:i:s\\Z", #context{})).
  205. %%
  206. %% @doc Convert an Erlang structure to a format that can be serialized by mochijson.
  207. %%
  208. %% Simple values
  209. to_json(undefined) ->
  210. null;
  211. to_json(X) when is_atom(X) ->
  212. X;
  213. to_json(X) when is_integer(X) ->
  214. X;
  215. to_json(X) when is_binary(X) ->
  216. X;
  217. %% Tuple values
  218. to_json({{Y,M,D},{H,I,S}} = DateTime)
  219. when is_integer(Y), is_integer(M), is_integer(D),
  220. is_integer(H), is_integer(I), is_integer(S) ->
  221. erlydtl_dateformat:format(DateTime, "Y-m-d H:i:s", en);
  222. to_json({array, X}) ->
  223. %% Explicit request for array (to prevent string conversion for some lists)
  224. {array, [to_json(V) || V <- X]};
  225. to_json({X, Y}) ->
  226. {struct, to_json_struct([{X, Y}])};
  227. to_json(X) when is_tuple(X) ->
  228. {array, [to_json(V) || V <- tuple_to_list(X)]};
  229. %% List values
  230. to_json([{X, Y}]) when is_atom(X) ->
  231. {struct, to_json_struct([{X, Y}])};
  232. to_json([{X, Y} | Z]) when is_atom(X) ->
  233. {struct, to_json_struct([{X, Y} | Z])};
  234. to_json(X) when is_list(X) ->
  235. case z_string:is_string(X) of
  236. true ->
  237. X;
  238. false ->
  239. {array, [to_json(V) || V <- X]}
  240. end.
  241. %% Handle structs specially
  242. to_json_struct([]) ->
  243. [];
  244. to_json_struct([{X,Y}|T]) ->
  245. [{to_json_struct_key(X), to_json(Y)} | to_json_struct(T)].
  246. to_json_struct_key(X) when is_atom(X) orelse is_integer(X) orelse is_binary(X) ->
  247. X;
  248. to_json_struct_key(X) when is_list(X) ->
  249. case z_string:is_string(X) of
  250. true ->
  251. X;
  252. false ->
  253. invalid_key
  254. end;
  255. to_json_struct_key(_) ->
  256. invalid_key.
  257. ip_to_list({IP,Port}) when is_tuple(IP), is_integer(Port) ->
  258. ip_to_list(IP);
  259. ip_to_list({N1,N2,N3,N4} ) ->
  260. lists:flatten([integer_to_list(N1), $., integer_to_list(N2), $., integer_to_list(N3), $., integer_to_list(N4)]);
  261. ip_to_list({_K1,_K2,_K3,_K4,_K5,_K6,_K7,_K8} = IPv6) ->
  262. L = lists:map(fun(0) -> "";
  263. (N) -> io_lib:format("~.16b", [N])
  264. end,
  265. tuple_to_list(IPv6)),
  266. lists:flatten(string:join(L, ":")).