Skip to content

Commit

Permalink
Make date handling in os library calls more compatible with Lua
Browse files Browse the repository at this point in the history
  • Loading branch information
rvirding committed Jan 28, 2024
1 parent afb0dd5 commit 76b3c9b
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 91 deletions.
4 changes: 2 additions & 2 deletions src/luerl_lib_os.erl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%% Copyright (c) 2013 Robert Virding
%% Copyright (c) 2013-2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -226,7 +226,7 @@ clock(_, As, St) ->
{[Tot*1.0e-3],St}.

date(ConfArg, [], St) ->
date(ConfArg, [<<"%Y-%m-%d %H:%M:%S">>], St);
date(ConfArg, [<<"%c">>], St);
date(ConfArg, [Fmt], St) when is_binary(Fmt) ->
date(ConfArg, [Fmt, current_timestamp()], St);
date(_, [Fmt, TimeStamp], St) when is_binary(Fmt) and is_number(TimeStamp) ->
Expand Down
242 changes: 153 additions & 89 deletions src/luerl_lib_os_date.erl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%% Copyright (c) 2013 Robert Virding
%% Copyright (c) 2023-2024 Robert Virding
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand All @@ -21,136 +21,193 @@
-export([format/2]).

%% IMPLEMENTED FORMATS:
%% %Y - Year with century, as decimal number (eg. 2007)
%% %y - Year without century, as decimal number (00 - 99) (eg. 07)
%% %m - Month as decimal number (01 - 12)
%% %A - Full weekday name (eg. Wednesday)
%% %a - Abbreviated weekday name (eg. Wed)
%% %B - Full month name (eg. September)
%% %b - Abbreviated month name (eg. Sep)
%% %c - date and time (e.g. Wed Jan 24 12:27:59 2024)
%% %D - date (e.g. 09/16/98)
%% %d - Day of month as decimal number (01 - 31)
%% %e - Day of month as decimal number ( 1 - 31)
%% %F - date (e.g. 2024-01-31)
%% %H - Hour in 24-hour format (00 - 23)
%% %I - Hour in 12-hour format (01 - 12)
%% %M - Minute as decimal number (00 - 59)
%% %m - Month as decimal number (01 - 12)
%% %n - Newline character
%% %P - As %p but lowercase
%% %p - Current locale’s A.M./P.M. indicator for 12-hour clock (eg. AM/PM)
%% %S - Second as decimal number (00 - 59)
%% %w - Weekday as decimal number (0 - 6; Sunday is 0)
%% %I - Hour in 12-hour format (01 - 12)
%% %T - time (e.g. 23:48:10)
%% %t - Tab character
%% %u - Weekday as decimal number (1 - 7; Monday is 1)
%% %V - IOS week number (01 - 53)
%% %W - Week of year as decimal number, with Monday as first day of week 1 (00 - 53)
%% %w - Weekday as decimal number (0 - 6; Sunday is 0)
%% %X - time (e.g., 23:48:10)
%% %x - date (e.g., 09/16/98)
%% %X - time (e.g., 23:48:10)
%% %c - date and time (e.g., 09/16/98 23:48:10)
%% %b - Abbreviated month name (eg. Sep)
%% %B - Full month name (eg. September)
%% %a - Abbreviated weekday name (eg. Wed)
%% %A - Full weekday name (eg. Wednesday)
%% %p - Current locale’s A.M./P.M. indicator for 12-hour clock (eg. AM/PM)
%% %Y - Year with century, as decimal number (eg. 2007)
%% %y - Year without century, as decimal number (00 - 99) (eg. 07)
%% %Z - Time-zone name or abbreviation; no characters if time zone is unknown
%% %% - Percent sign

%% NOT IMPLEMENTED:
%% %j - Day of year as decimal number (001 - 366)
%% %U - Week of year as decimal number, with Sunday as first day of week 1 (00 - 53)
%% %Z - Time-zone name or abbreviation; no characters if time zone is unknown


%% *t format returns a table with the datetime values
%% format(DateTime, Format) -> Formatted.
%% *t format returns a table with the datetime values.
format(DateTime, <<"*t">>) ->
{{Ye,Mo,Da},{Ho,Mi,Sec}} = DateTime,
{{Ye,Mo,Da}=Date,{Ho,Mi,Sec}} = DateTime,
[
{<<"year">>, Ye},
{<<"month">>, Mo},
{<<"day">>, Da},
{<<"hour">>, Ho},
{<<"min">>, Mi},
{<<"sec">>, Sec}
{<<"year">>, Ye},
{<<"month">>, Mo},
{<<"day">>, Da},
{<<"hour">>, Ho},
{<<"min">>, Mi},
{<<"sec">>, Sec},
{<<"wday">>, get_day_number(Date)+1}
];

format(DateTime, Format) ->
Patterns = [
<<"%Y">>,
<<"%y">>,
<<"%m">>,
<<"%A">>,
<<"%a">>,
<<"%B">>,
<<"%b">>,
<<"%c">>,
<<"%D">>,
<<"%d">>,
<<"%e">>,
<<"%F">>,
<<"%H">>,
<<"%I">>,
<<"%M">>,
<<"%m">>,
<<"%n">>,
<<"%P">>,
<<"%p">>,
<<"%S">>,
<<"%w">>,
<<"%I">>,
<<"%T">>,
<<"%t">>,
<<"%u">>,
<<"%V">>,
<<"%W">>,
<<"%x">>,
<<"%w">>,
<<"%X">>,
<<"%c">>,
<<"%b">>,
<<"%B">>,
<<"%a">>,
<<"%A">>,
<<"%p">>,
<<"%x">>,
<<"%Y">>,
<<"%y">>,
<<"%Z">>,
<<"%%">>
],
lists:foldl(fun(Pat, Str) -> replace_pattern(Str, DateTime, Pat) end, Format, Patterns).
lists:foldl(fun(Pat, Str) -> replace_pattern(Str, DateTime, Pat) end,
Format, Patterns).

replace_pattern(Str, DateTime, Pat) ->
case re:run(Str, Pat) of
nomatch -> Str;
{match, _} ->
{Fmt, Val} = get_pattern_values(Pat, DateTime),
Formatted = io_lib:fwrite(Fmt,Val),
re:replace(Str, Pat, Formatted, [{return, binary}, global])
{Format, Val} = get_pattern_values(Pat, DateTime),
Formatted = io_lib:fwrite(Format, Val),
re:replace(Str, Pat, Formatted, [{return, binary}, global])
end.

get_pattern_values(<<"%Y">>, {{Ye, _, _}, _}) -> {"~.4.0w", [Ye]};
get_pattern_values(<<"%y">>, {{Ye, _, _}, _}) -> {"~.2.0w", [Ye rem 100]};
get_pattern_values(<<"%m">>, {{_, Mo, _}, _}) -> {"~.2.0w", [Mo]};
%% get_pattern_values(Patters, DateTime) -> {FormatString, Values}.

get_pattern_values(<<"%A">>, {Date, _}) ->
{"~s", [get_day_name(get_day_number(Date))]};
get_pattern_values(<<"%a">>, {Date, _}) ->
{"~s", [get_abbreviated_day_name(get_day_number(Date))]};
get_pattern_values(<<"%B">>, {{_, Mo, _}, _}) ->
{"~s", [get_month_name(Mo)]};
get_pattern_values(<<"%b">>, {{_, Mo, _}, _}) ->
{"~s", [get_abbreviated_month_name(Mo)]};
get_pattern_values(<<"%c">>, {{Ye, Mo, Da}=Date, {Ho, Mi, Sec}}) ->
{"~s ~s ~.2w ~.2.0w:~.2.0w:~.2.0w ~.4.0w",
[get_abbreviated_day_name(get_day_number(Date)),
get_abbreviated_month_name(Mo),Da,
Ho,Mi,Sec,
Ye]};
get_pattern_values(<<"%D">>, {{Ye, Mo, Da}, _}) ->
{"~.2.0w/~.2.0w/~.2.0w", [Mo, Da, Ye rem 100]};
get_pattern_values(<<"%d">>, {{_, _, Da}, _}) -> {"~.2.0w", [Da]};
get_pattern_values(<<"%e">>, {{_, _, Da}, _}) -> {"~.2w", [Da]};
get_pattern_values(<<"%F">>, {{Ye, Mo, Da}, _}) ->
{"~.4.0w-~.2.0w-~.2.0w", [Ye, Mo, Da]};
get_pattern_values(<<"%H">>, {_, {Ho, _, _}}) -> {"~.2.0w", [Ho]};
get_pattern_values(<<"%I">>, {_, {Ho, _, _}}) ->
{"~.2.0w", [get_am_pm_hour(Ho)]};
get_pattern_values(<<"%M">>, {_, {_, Mi, _}}) -> {"~.2.0w", [Mi]};
get_pattern_values(<<"%m">>, {{_, Mo, _}, _}) -> {"~.2.0w", [Mo]};
get_pattern_values(<<"%n">>, _) -> {"\n", []};
get_pattern_values(<<"%P">>, {_, {Ho, _, _}}) ->
Val = if Ho < 12 -> <<"am">>;
true -> <<"pm">>
end,
{"~s", [Val]};
get_pattern_values(<<"%p">>, {_, {Ho, _, _}}) ->
Val = if Ho < 12 -> <<"AM">>;
true -> <<"PM">>
end,
{"~s", [Val]};
get_pattern_values(<<"%S">>, {_, {_, _, Sec}}) -> {"~.2.0w", [Sec]};
get_pattern_values(<<"%w">>, {Date, _}) -> {"~.1.0w", [get_day_number(Date)]}; % sun = 0 Sat = 6
get_pattern_values(<<"%I">>, {_, {Ho, _, _}}) -> {"~.2.0w", [get_am_pm_hour(Ho)]};
get_pattern_values(<<"%p">>, {_, {Ho, _, _}}) when Ho < 12 -> {"~s", [<<"AM">>]};
get_pattern_values(<<"%p">>, _) -> {"~s", [<<"PM">>]};
get_pattern_values(<<"%W">>, {Date, _}) -> {"~.1.0w", [element(2, calendar:iso_week_number(Date))]};
get_pattern_values(<<"%x">>, {{Ye, Mo, Da}, _}) -> {"~.2.0w/~.2.0w/~.2.0w", [Mo, Da, Ye rem 100]};
get_pattern_values(<<"%X">>, {_, {Ho, Mi, Sec}}) -> {"~.2.0w:~.2.0w:~.2.0w", [Ho, Mi, Sec]};
get_pattern_values(<<"%c">>, {{Ye, Mo, Da}, {Ho, Mi, Sec}}) -> {"~.2.0w/~.2.0w/~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Mo, Da, Ye rem 100, Ho, Mi, Sec]};
get_pattern_values(<<"%%">>, _) -> {"~1s", ["%"]};

get_pattern_values(<<"%b">>, {{_, 1, _}, _}) -> {"~s", ["Jan"]};
get_pattern_values(<<"%b">>, {{_, 2, _}, _}) -> {"~s", ["Feb"]};
get_pattern_values(<<"%b">>, {{_, 3, _}, _}) -> {"~s", ["Mar"]};
get_pattern_values(<<"%b">>, {{_, 4, _}, _}) -> {"~s", ["Apr"]};
get_pattern_values(<<"%b">>, {{_, 5, _}, _}) -> {"~s", ["May"]};
get_pattern_values(<<"%b">>, {{_, 6, _}, _}) -> {"~s", ["Jun"]};
get_pattern_values(<<"%b">>, {{_, 7, _}, _}) -> {"~s", ["Jul"]};
get_pattern_values(<<"%b">>, {{_, 8, _}, _}) -> {"~s", ["Aug"]};
get_pattern_values(<<"%b">>, {{_, 9, _}, _}) -> {"~s", ["Sep"]};
get_pattern_values(<<"%b">>, {{_, 10, _}, _}) -> {"~s", ["Oct"]};
get_pattern_values(<<"%b">>, {{_, 11, _}, _}) -> {"~s", ["Nov"]};
get_pattern_values(<<"%b">>, {{_, 12, _}, _}) -> {"~s", ["Dec"]};

get_pattern_values(<<"%B">>, {{_, 1, _}, _}) -> {"~s", ["January"]};
get_pattern_values(<<"%B">>, {{_, 2, _}, _}) -> {"~s", ["February"]};
get_pattern_values(<<"%B">>, {{_, 3, _}, _}) -> {"~s", ["March"]};
get_pattern_values(<<"%B">>, {{_, 4, _}, _}) -> {"~s", ["April"]};
get_pattern_values(<<"%B">>, {{_, 5, _}, _}) -> {"~s", ["May"]};
get_pattern_values(<<"%B">>, {{_, 6, _}, _}) -> {"~s", ["June"]};
get_pattern_values(<<"%B">>, {{_, 7, _}, _}) -> {"~s", ["July"]};
get_pattern_values(<<"%B">>, {{_, 8, _}, _}) -> {"~s", ["August"]};
get_pattern_values(<<"%B">>, {{_, 9, _}, _}) -> {"~s", ["September"]};
get_pattern_values(<<"%B">>, {{_, 10, _}, _}) -> {"~s", ["October"]};
get_pattern_values(<<"%B">>, {{_, 11, _}, _}) -> {"~s", ["November"]};
get_pattern_values(<<"%B">>, {{_, 12, _}, _}) -> {"~s", ["December"]};

get_pattern_values(<<"%a">>, {Date, _}) -> {"~s", [get_abbreviated_day_name(get_day_number(Date))]};
get_pattern_values(<<"%A">>, {Date, _}) -> {"~s", [get_day_name(get_day_number(Date))]}.

get_day_number(Date) -> calendar:day_of_the_week(Date) rem 7.
get_pattern_values(<<"%T">>, {_, {Ho, Mi, Sec}}) ->
{"~.2.0w:~.2.0w:~.2.0w", [Ho, Mi, Sec]};
get_pattern_values(<<"%t">>, _) -> {"\t", []};
get_pattern_values(<<"%u">>, {Date, _}) ->
{"~.1.0w", [calendar:day_of_the_week(Date)]}; % Mon = 1 Sun = 7
get_pattern_values(<<"%V">>, {Date, _}) ->
{_Year,Week} = calendar:iso_week_number(Date),
{"~.2.0w", [Week]};
get_pattern_values(<<"%W">>, {Date, _}) ->
{_Year,Week} = calendar:iso_week_number(Date),
{"~.2.0w", [Week]};
get_pattern_values(<<"%w">>, {Date, _}) ->
{"~.1.0w", [get_day_number(Date)]}; % Sun = 0 Sat = 6
get_pattern_values(<<"%X">>, {_, {Ho, Mi, Sec}}) ->
{"~.2.0w:~.2.0w:~.2.0w", [Ho, Mi, Sec]};
get_pattern_values(<<"%x">>, {{Ye, Mo, Da}, _}) ->
{"~.2.0w/~.2.0w/~.2.0w", [Mo, Da, Ye rem 100]};
get_pattern_values(<<"%Y">>, {{Ye, _, _}, _}) -> {"~.4.0w", [Ye]};
get_pattern_values(<<"%y">>, {{Ye, _, _}, _}) -> {"~.2.0w", [Ye rem 100]};
get_pattern_values(<<"%Z">>, _) -> {"", []};
get_pattern_values(<<"%%">>, _) -> {"~c", [$%]}.

%% get_day_number(Date) -> DayNumber.
%% This is US so Sunday is day 1.

get_day_number(Date) -> calendar:day_of_the_week(Date) rem 7.

get_am_pm_hour(0) -> 12;
get_am_pm_hour(H) when H > 12 -> H - 12;
get_am_pm_hour(H) -> H.

get_abbreviated_day_name(0) -> "Sun";
get_abbreviated_day_name(1) -> "Mon";
get_abbreviated_day_name(2) -> "Tue";
get_abbreviated_day_name(3) -> "Wed";
get_abbreviated_day_name(4) -> "Thu";
get_abbreviated_day_name(5) -> "Fri";
get_abbreviated_day_name(6) -> "Sat".
get_month_name(1) -> "January";
get_month_name(2) -> "February";
get_month_name(3) -> "March";
get_month_name(4) -> "April";
get_month_name(5) -> "May";
get_month_name(6) -> "June";
get_month_name(7) -> "July";
get_month_name(8) -> "August";
get_month_name(9) -> "September";
get_month_name(10) -> "October";
get_month_name(11) -> "November";
get_month_name(12) -> "December".

get_abbreviated_month_name(1) -> "Jan";
get_abbreviated_month_name(2) -> "Feb";
get_abbreviated_month_name(3) -> "Mar";
get_abbreviated_month_name(4) -> "Apr";
get_abbreviated_month_name(5) -> "May";
get_abbreviated_month_name(6) -> "Jun";
get_abbreviated_month_name(7) -> "Jul";
get_abbreviated_month_name(8) -> "Aug";
get_abbreviated_month_name(9) -> "Sep";
get_abbreviated_month_name(10) -> "Oct";
get_abbreviated_month_name(11) -> "Nov";
get_abbreviated_month_name(12) -> "Dec".

get_day_name(0) -> "Sunday";
get_day_name(1) -> "Monday";
Expand All @@ -160,3 +217,10 @@ get_day_name(4) -> "Thursday";
get_day_name(5) -> "Friday";
get_day_name(6) -> "Saturday".

get_abbreviated_day_name(0) -> "Sun";
get_abbreviated_day_name(1) -> "Mon";
get_abbreviated_day_name(2) -> "Tue";
get_abbreviated_day_name(3) -> "Wed";
get_abbreviated_day_name(4) -> "Thu";
get_abbreviated_day_name(5) -> "Fri";
get_abbreviated_day_name(6) -> "Sat".

0 comments on commit 76b3c9b

Please sign in to comment.