Current section

Files

Jump to
ephp src ephp_lib_date.erl
Raw

src/ephp_lib_date.erl

-module(ephp_lib_date).
-author('manuel@altenwald.com').
-behaviour(ephp_lib).
-export([init_func/0, init_config/0, init_const/0, handle_error/3, time/2, microtime/2,
microtime/3, date/3, date/4, gmdate/3, gmdate/4, date_default_timezone_set/3,
date_default_timezone_get/2, timezone_abbreviations_list/2]).
-include("ephp.hrl").
-spec init_func() -> ephp_lib:php_function_results().
init_func() ->
[time,
date,
gmdate,
microtime,
date_default_timezone_get,
date_default_timezone_set,
timezone_abbreviations_list].
-spec init_config() -> ephp_lib:php_config_results().
init_config() ->
[{<<"date.timezone">>, undefined}].
-spec init_const() -> ephp_lib:php_const_results().
init_const() ->
[].
-spec handle_error(ephp_error:error_type(), ephp_error:error_level(), Args :: term()) ->
string() | ignore.
handle_error(etimezone, _Level, {Function, TZ}) ->
io_lib:format("~s(): Timezone ID '~s' is invalid", [Function, TZ]);
handle_error(enotimezone, _Level, {Function}) ->
io_lib:format("~s(): It is not safe to rely on the system's timezone "
"settings. You are *required* to use the date.timezone "
"setting or the date_default_timezone_set() function. "
"In case you used any of those methods and you are still "
"getting this warning, you most likely misspelled the "
"timezone identifier. We selected the timezone 'UTC' for "
"now, but please set date.timezone to select your "
"timezone.",
[Function]);
handle_error(_Type, _Level, _Args) ->
ignore.
-spec time(ephp:context_id(), line()) -> integer().
time(_Context, _Line) ->
ephp_datetime:posix_time().
-spec microtime(ephp:context_id(),
line(),
GetAsFloat :: {variable() | undefined, boolean()}) ->
float() | binary().
microtime(_Context, _Line, {_, true}) ->
{MS, S, US} = ephp_datetime:timestamp(),
MS * 1000000 + S + US div 1000 / 1000;
microtime(_Context, _Line, {_, false}) ->
{MS, S, US} = ephp_datetime:timestamp(),
Posix = ephp_data:to_bin(MS * 1000000 + S),
MiliSec = ephp_data:to_bin(US div 1000 / 1000),
<<MiliSec/binary, " ", Posix/binary>>;
microtime(Context, Line, {Var, Val}) ->
microtime(Context, Line, {Var, ephp_data:to_boolean(Val)}).
-spec microtime(ephp:context_id(), line()) -> float() | binary().
microtime(Context, Line) ->
microtime(Context, Line, {undefined, false}).
-spec date(ephp:context_id(), line(), Format :: var_value()) -> binary().
date(Context, Line, Format) ->
{MS, S, US} = ephp_datetime:timestamp(),
date(Context, Line, Format, {undefined, MS * 1000000 + S + trunc(US / 1000000)}).
-spec date(ephp:context_id(), line(), Format :: var_value(), Timestamp :: var_value()) ->
binary().
date(Context, Line, {_, Format}, {_, Timestamp}) ->
{M, S, U} = ephp_datetime:get_timestamp(Timestamp),
TZ = ephp_datetime:get_tz(Context, Line),
Date = ephp_datetime:to_zone({M, S, U}, TZ),
date_format(Format, <<>>, {Timestamp, Date, TZ}).
-spec gmdate(ephp:context_id(), line(), Format :: var_value()) -> binary().
gmdate(Context, Line, {_, Format}) ->
{MS, S, US} = ephp_datetime:timestamp(),
gmdate(Context,
Line,
{undefined, Format},
{undefined, MS * 1000000 + S + trunc(US / 1000000)}).
-spec gmdate(ephp:context_id(),
line(),
Format :: var_value(),
Timestamp :: var_value()) ->
binary().
gmdate(_Context, _Line, {_, Format}, {_, Timestamp}) ->
{M, S, U} = ephp_datetime:get_timestamp(Timestamp),
TZ = <<"GMT">>,
Date = calendar:now_to_universal_time({M, S, U}),
date_format(Format, <<>>, {Timestamp, Date, TZ}).
-spec date_default_timezone_get(ephp:context_id(), line()) -> binary().
date_default_timezone_get(Context, Line) ->
ephp_datetime:get_tz(Context, Line).
-spec date_default_timezone_set(ephp:context_id(), line(), TZ :: var_value()) ->
undefined.
date_default_timezone_set(Context, Line, {_, TZ}) ->
case ephp_datetime:set_tz(TZ) of
true ->
undefined;
false ->
Function = <<"date_default_timezone_set">>,
File = ephp_context:get_active_file(Context),
Params = {error, etimezone, Line, File, ?E_NOTICE, {Function, TZ}},
ephp_error:handle_error(Context, Params),
undefined
end.
-spec timezone_abbreviations_list(ephp:context_id(), line()) -> ephp_array:ephp_array().
timezone_abbreviations_list(_Context, _Line) ->
lists:foldl(fun(Abbr, Array) ->
Info =
lists:foldl(fun({Dst, Offset, TZID}, SubArray) ->
A0 = ephp_array:new(),
A1 = ephp_array:store(<<"dst">>, Dst, A0),
A2 = ephp_array:store(<<"offset">>, Offset, A1),
A3 = ephp_array:store(<<"timezone_id">>, TZID, A2),
ephp_array:store(auto, A3, SubArray)
end,
ephp_array:new(),
ephp_timezone:timezone_info(Abbr)),
ephp_array:store(
ephp_string:to_lower(Abbr), Info, Array)
end,
ephp_array:new(),
ephp_timezone:abbreviations()).
%% ----------------------------------------------------------------------------
%% Internal functions
%% ----------------------------------------------------------------------------
-spec date_format(ToAnalyze :: binary(),
Result :: binary(),
{Timestamp :: integer(), Date :: calendar:datetime(), TZ :: binary()}) ->
binary().
date_format(<<>>, Result, _Date) ->
Result;
date_format(<<"d", R/binary>>, Result, {_, {{_, _, D}, _}, _} = Date) ->
Day = ephp_data:pad_to_bin(D, 2),
date_format(R, <<Result/binary, Day/binary>>, Date);
date_format(<<"D", R/binary>>, Result, {_, {D, _}, _} = Date) ->
Day = ephp_datetime:get_abbr_weekday(
calendar:day_of_the_week(D)),
date_format(R, <<Result/binary, Day/binary>>, Date);
date_format(<<"j", R/binary>>, Result, {_, {{_, _, D}, _}, _} = Date) ->
Month = ephp_data:to_bin(D),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"l", R/binary>>, Result, {_, {D, _}, _} = Date) ->
Day = ephp_datetime:get_weekday(
calendar:day_of_the_week(D)),
date_format(R, <<Result/binary, Day/binary>>, Date);
date_format(<<"N", R/binary>>, Result, {_, {D, _}, _} = Date) ->
Day = calendar:day_of_the_week(D),
DayBin = ephp_data:to_bin(Day),
date_format(R, <<Result/binary, DayBin/binary>>, Date);
date_format(<<"S", R/binary>>, Result, {_, {{_, _, D}, _}, _} = Date) ->
Suffix =
if D =:= 1 orelse D =:= 21 orelse D =:= 31 ->
<<"st">>;
D =:= 2 orelse D =:= 22 ->
<<"nd">>;
D =:= 3 orelse D =:= 23 ->
<<"rd">>;
true ->
<<"th">>
end,
date_format(R, <<Result/binary, Suffix/binary>>, Date);
date_format(<<"w", R/binary>>, Result, {_, {D, _}, _} = Date) ->
Day = calendar:day_of_the_week(D),
DayBin =
ephp_data:to_bin(if Day =:= 7 ->
0;
true ->
Day
end),
date_format(R, <<Result/binary, DayBin/binary>>, Date);
date_format(<<"z", R/binary>>, Result, {_, {{Y, M, D}, _}, _} = Date) ->
Days1 = calendar:date_to_gregorian_days(Y, 1, 1),
Days2 = calendar:date_to_gregorian_days(Y, M, D),
Days = ephp_data:to_bin(Days2 - Days1),
date_format(R, <<Result/binary, Days/binary>>, Date);
date_format(<<"W", R/binary>>, Result, {_, {D, _}, _} = Date) ->
{_Y, WY} = calendar:iso_week_number(D),
WeekOfYear = ephp_data:pad_to_bin(WY, 2),
date_format(R, <<Result/binary, WeekOfYear/binary>>, Date);
date_format(<<"F", R/binary>>, Result, {_, {{_, M, _}, _}, _} = Date) ->
Month = ephp_datetime:get_month(M),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"m", R/binary>>, Result, {_, {{_, M, _}, _}, _} = Date) ->
Month = ephp_data:pad_to_bin(M, 2),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"M", R/binary>>, Result, {_, {{_, M, _}, _}, _} = Date) ->
Month = ephp_datetime:get_abbr_month(M),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"n", R/binary>>, Result, {_, {{_, M, _}, _}, _} = Date) ->
Month = ephp_data:to_bin(M),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"t", R/binary>>, Result, {_, {{Y, M, _}, _}, _} = Date) ->
Month =
ephp_data:to_bin(
calendar:last_day_of_the_month(Y, M)),
date_format(R, <<Result/binary, Month/binary>>, Date);
date_format(<<"L", R/binary>>, Result, {_, {{Y, _, _}, _}, _} = Date) ->
Leap =
case calendar:is_leap_year(Y) of
true ->
<<"1">>;
false ->
<<"0">>
end,
date_format(R, <<Result/binary, Leap/binary>>, Date);
date_format(<<"o", R/binary>>, Result, {_, {D, _}, _} = Date) ->
{Y, _WY} = calendar:iso_week_number(D),
YearOfWeek = ephp_data:to_bin(Y),
date_format(R, <<Result/binary, YearOfWeek/binary>>, Date);
date_format(<<"Y", R/binary>>, Result, {_, {{Y, _, _}, _}, _} = Date) ->
Year = ephp_data:to_bin(Y),
date_format(R, <<Result/binary, Year/binary>>, Date);
date_format(<<"y", R/binary>>, Result, {_, {{Y, _, _}, _}, _} = Date) ->
Year = ephp_data:pad_to_bin(Y rem 100, 2),
date_format(R, <<Result/binary, Year/binary>>, Date);
date_format(<<"a", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Type =
if H >= 0 andalso H =< 11 ->
<<"am">>;
true ->
<<"pm">>
end,
date_format(R, <<Result/binary, Type/binary>>, Date);
date_format(<<"A", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Type =
if H >= 0 andalso H =< 11 ->
<<"AM">>;
true ->
<<"PM">>
end,
date_format(R, <<Result/binary, Type/binary>>, Date);
date_format(<<"B", R/binary>>, Result, {Timestamp, _, _} = Date) ->
DateTime = ephp_datetime:get_datetime(Timestamp),
BeatsTime = ephp_datetime:to_bmt(DateTime),
Beats = ephp_data:to_bin(BeatsTime),
date_format(R, <<Result/binary, Beats/binary>>, Date);
date_format(<<"g", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Hour = ephp_data:to_bin((H + 11) rem 12 + 1),
date_format(R, <<Result/binary, Hour/binary>>, Date);
date_format(<<"G", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Hour = ephp_data:to_bin(H),
date_format(R, <<Result/binary, Hour/binary>>, Date);
date_format(<<"h", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Hour = ephp_data:pad_to_bin((H + 11) rem 12 + 1, 2),
date_format(R, <<Result/binary, Hour/binary>>, Date);
date_format(<<"H", R/binary>>, Result, {_, {_, {H, _, _}}, _} = Date) ->
Hour = ephp_data:pad_to_bin(H, 2),
date_format(R, <<Result/binary, Hour/binary>>, Date);
date_format(<<"i", R/binary>>, Result, {_, {_, {_, M, _}}, _} = Date) ->
Minute = ephp_data:pad_to_bin(M, 2),
date_format(R, <<Result/binary, Minute/binary>>, Date);
date_format(<<"s", R/binary>>, Result, {_, {_, {_, _, S}}, _} = Date) ->
Second = ephp_data:pad_to_bin(S, 2),
date_format(R, <<Result/binary, Second/binary>>, Date);
date_format(<<"u", R/binary>>, Result, {Timestamp, _, _} = Date) ->
US = Timestamp * 1000000 rem 1000000,
MSecond = ephp_data:pad_to_bin(US, 6),
date_format(R, <<Result/binary, MSecond/binary>>, Date);
date_format(<<"e", R/binary>>, Result, {_, _, TZ} = Date) ->
TimeZone = ephp_data:to_bin(TZ),
date_format(R, <<Result/binary, TimeZone/binary>>, Date);
date_format(<<"I", R/binary>>, Result, {_, DT, TZ} = Date) ->
Daylight =
case ephp_datetime:is_dst(DT, TZ) of
true ->
<<"1">>;
false ->
<<"0">>
end,
date_format(R, <<Result/binary, Daylight/binary>>, Date);
date_format(<<"O", R/binary>>, Result, {_, DT, TZ} = Date) ->
Timezone = ephp_datetime:get_tz_time(DT, TZ, <<>>),
date_format(R, <<Result/binary, Timezone/binary>>, Date);
date_format(<<"P", R/binary>>, Result, {_, DT, TZ} = Date) ->
Timezone = ephp_datetime:get_tz_time(DT, TZ, <<":">>),
date_format(R, <<Result/binary, Timezone/binary>>, Date);
date_format(<<"T", R/binary>>, Result, {_, DT, TZ} = Date) ->
IsDst = ephp_datetime:is_dst(DT, TZ),
Timezone = ephp_timezone:timezone_to_abbreviation(IsDst, ephp_data:to_bin(TZ)),
date_format(R, <<Result/binary, Timezone/binary>>, Date);
date_format(<<"Z", R/binary>>, Result, {_, DT, TZ} = Date) ->
<<S:1/binary, H:2/binary, M:2/binary>> = ephp_datetime:get_tz_time(DT, TZ, <<>>),
Secs = integer_to_binary((binary_to_integer(H) * 60 + binary_to_integer(M)) * 60),
Timezone =
case S of
<<"+">> ->
Secs;
<<"-">> ->
<<S/binary, Secs/binary>>
end,
date_format(R, <<Result/binary, Timezone/binary>>, Date);
date_format(<<"c", R/binary>>, Result, {_, {{Y, M, D}, {H, I, S}} = DT, TZ} = Date) ->
YB = ephp_data:to_bin(Y),
MB = ephp_data:pad_to_bin(M, 2),
DB = ephp_data:pad_to_bin(D, 2),
HB = ephp_data:pad_to_bin(H, 2),
IB = ephp_data:pad_to_bin(I, 2),
SB = ephp_data:pad_to_bin(S, 2),
TZtime = ephp_datetime:get_tz_time(DT, TZ, <<":">>),
DateTime =
<<YB/binary,
"-",
MB/binary,
"-",
DB/binary,
"T",
HB/binary,
":",
IB/binary,
":",
SB/binary,
TZtime/binary>>,
date_format(R, <<Result/binary, DateTime/binary>>, Date);
date_format(<<"r", R/binary>>,
Result,
{_, {{Y, M, D} = Dt, {H, I, S}} = DT, TZ} = Date) ->
WB = ephp_datetime:get_abbr_weekday(
calendar:day_of_the_week(Dt)),
DB = ephp_data:pad_to_bin(D, 2),
MB = ephp_datetime:get_abbr_month(M),
YB = ephp_data:to_bin(Y),
HB = ephp_data:pad_to_bin(H, 2),
IB = ephp_data:pad_to_bin(I, 2),
SB = ephp_data:pad_to_bin(S, 2),
TZtime = ephp_datetime:get_tz_time(DT, TZ, <<>>),
DateTime =
<<WB/binary,
", ",
DB/binary,
" ",
MB/binary,
" ",
YB/binary,
" ",
HB/binary,
":",
IB/binary,
":",
SB/binary,
" ",
TZtime/binary>>,
date_format(R, <<Result/binary, DateTime/binary>>, Date);
date_format(<<"U", R/binary>>, Result, {Timestamp, _, _} = Date) ->
DateTime = ephp_data:to_bin(trunc(Timestamp)),
date_format(R, <<Result/binary, DateTime/binary>>, Date);
date_format(<<Other:1/binary, R/binary>>, Result, Date) ->
date_format(R, <<Result/binary, Other/binary>>, Date).