Current section
Files
Jump to
Current section
Files
src/calendar@time.erl
-module(calendar@time).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/calendar/time.gleam").
-export([new/5, new_simple/3, to_seconds_after_midnight/1, to_seconds_after_midnight_tuple/1, to_microseconds_after_midnight/1, to_timestamp/1, utc_now_with_precision/2, utc_now/0, new_unchecked_bang/5, to_erl/1, from_erl/3, from_erl_unchecked/3, compare/2, before/2, 'after'/2, convert/2, convert_unchecked/2, diff/3, truncate/2, is_valid/4, max_precision/1, convert_unit/3, midnight/0, noon/0, is_midnight/1, second_of_day/1, normalize_precision/2, from_iso8601/1, from_iso8601_unchecked/1, to_string/1, to_iso8601_with_format/2, to_iso8601/1, inspect/1, from_seconds_after_midnight_with_microsecond/3, from_seconds_after_midnight/1, from_timestamp/1, add/3, shift/2]).
-export_type([time/0, time_error/0, time_format/0, time_unit/0, time_comparison/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type time() :: {time,
integer(),
integer(),
integer(),
{integer(), integer()},
binary()}.
-type time_error() :: invalid_time |
invalid_hour |
invalid_minute |
invalid_second |
invalid_microsecond.
-type time_format() :: extended | basic.
-type time_unit() :: second | millisecond | microsecond | native.
-type time_comparison() :: lt | eq | gt.
-file("src/calendar/time.gleam", 75).
?DOC(" Validates if the given time components are valid.\n").
-spec is_valid_time(integer(), integer(), integer(), {integer(), integer()}) -> boolean().
is_valid_time(Hour, Minute, Second, Microsecond) ->
{Ms, Precision} = Microsecond,
(((((((((Hour >= 0) andalso (Hour =< 23)) andalso (Minute >= 0)) andalso (Minute
=< 59))
andalso (Second >= 0))
andalso (Second =< 59))
andalso (Ms >= 0))
andalso (Ms < 1000000))
andalso (Precision >= 0))
andalso (Precision =< 6).
-file("src/calendar/time.gleam", 44).
?DOC(
" Creates a new Time struct.\n"
"\n"
" Returns an error if the time is invalid.\n"
).
-spec new(integer(), integer(), integer(), {integer(), integer()}, binary()) -> {ok,
time()} |
{error, time_error()}.
new(Hour, Minute, Second, Microsecond, Calendar) ->
case is_valid_time(Hour, Minute, Second, Microsecond) of
true ->
{ok, {time, Hour, Minute, Second, Microsecond, Calendar}};
false ->
{error, invalid_time}
end.
-file("src/calendar/time.gleam", 66).
?DOC(
" Creates a new Time struct with default values.\n"
" Uses microsecond #(0, 0) and Calendar.ISO as defaults.\n"
).
-spec new_simple(integer(), integer(), integer()) -> {ok, time()} |
{error, time_error()}.
new_simple(Hour, Minute, Second) ->
new(Hour, Minute, Second, {0, 0}, <<"Calendar.ISO"/utf8>>).
-file("src/calendar/time.gleam", 143).
?DOC(" Converts time to total seconds since midnight.\n").
-spec to_seconds_after_midnight(time()) -> integer().
to_seconds_after_midnight(Time) ->
((erlang:element(2, Time) * 3600) + (erlang:element(3, Time) * 60)) + erlang:element(
4,
Time
).
-file("src/calendar/time.gleam", 148).
?DOC(" Converts time to total seconds since midnight as a tuple with microseconds.\n").
-spec to_seconds_after_midnight_tuple(time()) -> {integer(), integer()}.
to_seconds_after_midnight_tuple(Time) ->
{Ms, _} = erlang:element(5, Time),
Total_seconds = ((erlang:element(2, Time) * 3600) + (erlang:element(3, Time)
* 60))
+ erlang:element(4, Time),
{Total_seconds, Ms}.
-file("src/calendar/time.gleam", 155).
?DOC(" Converts time to total microseconds since midnight.\n").
-spec to_microseconds_after_midnight(time()) -> integer().
to_microseconds_after_midnight(Time) ->
{Ms, _} = erlang:element(5, Time),
Total_seconds = ((erlang:element(2, Time) * 3600) + (erlang:element(3, Time)
* 60))
+ erlang:element(4, Time),
(Total_seconds * 1000000) + Ms.
-file("src/calendar/time.gleam", 187).
?DOC(
" Converts a Time to a Unix timestamp (seconds since epoch).\n"
" Note: This requires a date to be meaningful. This function assumes \n"
" the Unix epoch date (1970-01-01) for demonstration purposes.\n"
).
-spec to_timestamp(time()) -> integer().
to_timestamp(Time) ->
to_seconds_after_midnight(Time).
-file("src/calendar/time.gleam", 204).
?DOC(" Returns the current time in UTC with specified precision and calendar.\n").
-spec utc_now_with_precision(time_unit(), binary()) -> {ok, time()} |
{error, time_error()}.
utc_now_with_precision(Precision, Calendar) ->
{_, Nanoseconds} = gleam_time_ffi:system_time(),
Total_seconds = Nanoseconds div 1000000000,
Remaining_nanos = Nanoseconds rem 1000000000,
Microseconds = Remaining_nanos div 1000,
Hours = (Total_seconds div 3600) rem 24,
Remaining = Total_seconds rem 3600,
Minutes = Remaining div 60,
Seconds = Remaining rem 60,
Adjusted_precision = case Precision of
second ->
{0, 0};
millisecond ->
{(Microseconds div 1000) * 1000, 3};
microsecond ->
{Microseconds, 6};
native ->
{Microseconds, 6}
end,
new(Hours, Minutes, Seconds, Adjusted_precision, Calendar).
-file("src/calendar/time.gleam", 199).
?DOC(" Returns the current time in UTC.\n").
-spec utc_now() -> {ok, time()} | {error, time_error()}.
utc_now() ->
utc_now_with_precision(microsecond, <<"Calendar.ISO"/utf8>>).
-file("src/calendar/time.gleam", 235).
?DOC(" Creates a new Time, panicking if invalid.\n").
-spec new_unchecked_bang(
integer(),
integer(),
integer(),
{integer(), integer()},
binary()
) -> time().
new_unchecked_bang(Hour, Minute, Second, Microsecond, Calendar) ->
case new(Hour, Minute, Second, Microsecond, Calendar) of
{ok, Time} ->
Time;
{error, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Invalid time"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"calendar/time"/utf8>>,
function => <<"new_unchecked_bang"/utf8>>,
line => 244})
end.
-file("src/calendar/time.gleam", 266).
?DOC(" Converts time to Erlang time tuple.\n").
-spec to_erl(time()) -> {integer(), integer(), integer()}.
to_erl(Time) ->
{erlang:element(2, Time), erlang:element(3, Time), erlang:element(4, Time)}.
-file("src/calendar/time.gleam", 271).
?DOC(" Creates time from Erlang time tuple.\n").
-spec from_erl(
{integer(), integer(), integer()},
{integer(), integer()},
binary()
) -> {ok, time()} | {error, time_error()}.
from_erl(Erl_time, Microsecond, Calendar) ->
{Hour, Minute, Second} = Erl_time,
new(Hour, Minute, Second, Microsecond, Calendar).
-file("src/calendar/time.gleam", 281).
?DOC(" Creates time from Erlang time tuple, panicking on error.\n").
-spec from_erl_unchecked(
{integer(), integer(), integer()},
{integer(), integer()},
binary()
) -> time().
from_erl_unchecked(Erl_time, Microsecond, Calendar) ->
case from_erl(Erl_time, Microsecond, Calendar) of
{ok, Time} ->
Time;
{error, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Invalid Erlang time tuple"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"calendar/time"/utf8>>,
function => <<"from_erl_unchecked"/utf8>>,
line => 288})
end.
-file("src/calendar/time.gleam", 335).
-spec compare(time(), time()) -> time_comparison().
compare(Time1, Time2) ->
Ms1 = to_microseconds_after_midnight(Time1),
Ms2 = to_microseconds_after_midnight(Time2),
case Ms1 < Ms2 of
true ->
lt;
false ->
case Ms1 > Ms2 of
true ->
gt;
false ->
eq
end
end.
-file("src/calendar/time.gleam", 350).
?DOC(" Check if first time is before second time.\n").
-spec before(time(), time()) -> boolean().
before(Time1, Time2) ->
case compare(Time1, Time2) of
lt ->
true;
_ ->
false
end.
-file("src/calendar/time.gleam", 358).
?DOC(" Check if first time is after second time.\n").
-spec 'after'(time(), time()) -> boolean().
'after'(Time1, Time2) ->
case compare(Time1, Time2) of
gt ->
true;
_ ->
false
end.
-file("src/calendar/time.gleam", 366).
?DOC(" Convert time between calendars.\n").
-spec convert(time(), binary()) -> {ok, time()} | {error, time_error()}.
convert(Time, Target_calendar) ->
case erlang:element(6, Time) =:= Target_calendar of
true ->
{ok, Time};
false ->
case new(
erlang:element(2, Time),
erlang:element(3, Time),
erlang:element(4, Time),
erlang:element(5, Time),
Target_calendar
) of
{ok, T} ->
{ok, T};
{error, E} ->
{error, E}
end
end.
-file("src/calendar/time.gleam", 388).
?DOC(" Convert time between calendars, panicking on error.\n").
-spec convert_unchecked(time(), binary()) -> time().
convert_unchecked(Time, Target_calendar) ->
case convert(Time, Target_calendar) of
{ok, Converted} ->
Converted;
{error, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Calendar conversion failed"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"calendar/time"/utf8>>,
function => <<"convert_unchecked"/utf8>>,
line => 391})
end.
-file("src/calendar/time.gleam", 396).
?DOC(" Calculate difference between two times.\n").
-spec diff(time(), time(), time_unit()) -> integer().
diff(Time1, Time2, Unit) ->
Ms1 = to_microseconds_after_midnight(Time1),
Ms2 = to_microseconds_after_midnight(Time2),
Diff_microseconds = Ms1 - Ms2,
case Unit of
microsecond ->
Diff_microseconds;
native ->
Diff_microseconds;
millisecond ->
Diff_microseconds div 1000;
second ->
Diff_microseconds div 1000000
end.
-file("src/calendar/time.gleam", 423).
?DOC(" Truncate time precision.\n").
-spec truncate(time(), integer()) -> time().
truncate(Time, Precision) ->
{Ms, _} = erlang:element(5, Time),
Truncated_ms = case Precision of
0 ->
{0, 0};
P when P >= 6 ->
{Ms, 6};
P@1 ->
Divisor = case P@1 of
1 ->
100000;
2 ->
10000;
3 ->
1000;
4 ->
100;
5 ->
10;
_ ->
1
end,
{(case Divisor of
0 -> 0;
Gleam@denominator -> Ms div Gleam@denominator
end) * Divisor, P@1}
end,
case new(
erlang:element(2, Time),
erlang:element(3, Time),
erlang:element(4, Time),
Truncated_ms,
erlang:element(6, Time)
) of
{ok, T} ->
T;
{error, _} ->
Time
end.
-file("src/calendar/time.gleam", 454).
?DOC(" Check if a time is valid.\n").
-spec is_valid(integer(), integer(), integer(), {integer(), integer()}) -> boolean().
is_valid(Hour, Minute, Second, Microsecond) ->
is_valid_time(Hour, Minute, Second, Microsecond).
-file("src/calendar/time.gleam", 464).
?DOC(" Get the maximum precision for a time unit.\n").
-spec max_precision(time_unit()) -> integer().
max_precision(Unit) ->
case Unit of
second ->
0;
millisecond ->
3;
microsecond ->
6;
native ->
6
end.
-file("src/calendar/time.gleam", 491).
?DOC(" Convert between time units.\n").
-spec convert_unit(integer(), time_unit(), time_unit()) -> integer().
convert_unit(Amount, From_unit, To_unit) ->
Microseconds = case From_unit of
second ->
Amount * 1000000;
millisecond ->
Amount * 1000;
microsecond ->
Amount;
native ->
Amount
end,
case To_unit of
second ->
Microseconds div 1000000;
millisecond ->
Microseconds div 1000;
microsecond ->
Microseconds;
native ->
Microseconds
end.
-file("src/calendar/time.gleam", 506).
?DOC(" Get the time at midnight (00:00:00).\n").
-spec midnight() -> {ok, time()} | {error, time_error()}.
midnight() ->
new(0, 0, 0, {0, 0}, <<"Calendar.ISO"/utf8>>).
-file("src/calendar/time.gleam", 511).
?DOC(" Get the time at noon (12:00:00).\n").
-spec noon() -> {ok, time()} | {error, time_error()}.
noon() ->
new(12, 0, 0, {0, 0}, <<"Calendar.ISO"/utf8>>).
-file("src/calendar/time.gleam", 516).
?DOC(" Check if a time is at midnight.\n").
-spec is_midnight(time()) -> boolean().
is_midnight(Time) ->
(((erlang:element(2, Time) =:= 0) andalso (erlang:element(3, Time) =:= 0))
andalso (erlang:element(4, Time) =:= 0))
andalso (erlang:element(5, Time) =:= {0, 0}).
-file("src/calendar/time.gleam", 524).
?DOC(" Get the second of the day (0-86399).\n").
-spec second_of_day(time()) -> integer().
second_of_day(Time) ->
((erlang:element(2, Time) * 3600) + (erlang:element(3, Time) * 60)) + erlang:element(
4,
Time
).
-file("src/calendar/time.gleam", 572).
-spec int_pow(integer(), integer()) -> integer().
int_pow(Base, Exp) ->
case Exp of
0 ->
1;
N when N > 0 ->
Base * int_pow(Base, N - 1);
_ ->
1
end.
-file("src/calendar/time.gleam", 473).
?DOC(" Normalize microseconds to a specific precision.\n").
-spec normalize_precision({integer(), integer()}, integer()) -> {integer(),
integer()}.
normalize_precision(Microsecond, Target_precision) ->
{Ms, Current_precision} = Microsecond,
case Target_precision >= Current_precision of
true ->
{Ms * int_pow(10, Target_precision - Current_precision),
Target_precision};
false ->
{case int_pow(10, Current_precision - Target_precision) of
0 -> 0;
Gleam@denominator -> Ms div Gleam@denominator
end, Target_precision}
end.
-file("src/calendar/time.gleam", 565).
-spec normalize_microseconds(integer(), integer()) -> integer().
normalize_microseconds(Ms, Precision) ->
case Precision of
P when P >= 6 ->
Ms;
P@1 ->
Ms * int_pow(10, 6 - P@1)
end.
-file("src/calendar/time.gleam", 530).
-spec parse_iso8601_time(binary()) -> {ok,
{integer(), integer(), integer(), {integer(), integer()}}} |
{error, nil}.
parse_iso8601_time(Time_string) ->
case gleam@string:split(Time_string, <<":"/utf8>>) of
[Hour_str, Minute_str, Second_part] ->
case {gleam_stdlib:parse_int(Hour_str),
gleam_stdlib:parse_int(Minute_str)} of
{{ok, Hour}, {ok, Minute}} ->
case gleam@string:split(Second_part, <<"."/utf8>>) of
[Second_str] ->
case gleam_stdlib:parse_int(Second_str) of
{ok, Second} ->
{ok, {Hour, Minute, Second, {0, 0}}};
{error, _} ->
{error, nil}
end;
[Second_str@1, Ms_str] ->
case {gleam_stdlib:parse_int(Second_str@1),
gleam_stdlib:parse_int(Ms_str)} of
{{ok, Second@1}, {ok, Ms}} ->
Precision = string:length(Ms_str),
Normalized_ms = normalize_microseconds(
Ms,
Precision
),
{ok,
{Hour,
Minute,
Second@1,
{Normalized_ms, Precision}}};
{_, _} ->
{error, nil}
end;
_ ->
{error, nil}
end;
{_, _} ->
{error, nil}
end;
_ ->
{error, nil}
end.
-file("src/calendar/time.gleam", 249).
?DOC(" Parses an ISO8601 time string.\n").
-spec from_iso8601(binary()) -> {ok, time()} | {error, time_error()}.
from_iso8601(Time_string) ->
case parse_iso8601_time(Time_string) of
{ok, {Hour, Minute, Second, Microsecond}} ->
new(Hour, Minute, Second, Microsecond, <<"Calendar.ISO"/utf8>>);
{error, _} ->
{error, invalid_time}
end.
-file("src/calendar/time.gleam", 258).
?DOC(" Parses an ISO8601 time string, panicking on error.\n").
-spec from_iso8601_unchecked(binary()) -> time().
from_iso8601_unchecked(Time_string) ->
case from_iso8601(Time_string) of
{ok, Time} ->
Time;
{error, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Invalid ISO8601 time format"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"calendar/time"/utf8>>,
function => <<"from_iso8601_unchecked"/utf8>>,
line => 261})
end.
-file("src/calendar/time.gleam", 581).
?DOC(" Helper function to pad single digits with leading zero.\n").
-spec pad_zero(integer()) -> binary().
pad_zero(N) ->
case N < 10 of
true ->
<<"0"/utf8, (erlang:integer_to_binary(N))/binary>>;
false ->
erlang:integer_to_binary(N)
end.
-file("src/calendar/time.gleam", 589).
?DOC(" Helper function to format microseconds with proper padding.\n").
-spec pad_microseconds(integer(), integer()) -> binary().
pad_microseconds(Microseconds, Precision) ->
Ms_str = erlang:integer_to_binary(Microseconds),
Padded = case string:length(Ms_str) < Precision of
true ->
<<(gleam@string:repeat(
<<"0"/utf8>>,
Precision - string:length(Ms_str)
))/binary,
Ms_str/binary>>;
false ->
Ms_str
end,
gleam@string:slice(Padded, 0, Precision).
-file("src/calendar/time.gleam", 96).
?DOC(" Converts a Time to a string in ISO8601 format.\n").
-spec to_string(time()) -> binary().
to_string(Time) ->
Hour_str = pad_zero(erlang:element(2, Time)),
Minute_str = pad_zero(erlang:element(3, Time)),
Second_str = pad_zero(erlang:element(4, Time)),
{Ms, Precision} = erlang:element(5, Time),
case (Ms =:= 0) orelse (Precision =:= 0) of
true ->
<<<<<<<<Hour_str/binary, ":"/utf8>>/binary, Minute_str/binary>>/binary,
":"/utf8>>/binary,
Second_str/binary>>;
false ->
Ms_str = pad_microseconds(Ms, Precision),
<<<<<<<<<<<<Hour_str/binary, ":"/utf8>>/binary, Minute_str/binary>>/binary,
":"/utf8>>/binary,
Second_str/binary>>/binary,
"."/utf8>>/binary,
Ms_str/binary>>
end.
-file("src/calendar/time.gleam", 122).
?DOC(" Converts a Time to ISO8601 format with specified format.\n").
-spec to_iso8601_with_format(time(), time_format()) -> binary().
to_iso8601_with_format(Time, Format) ->
case Format of
extended ->
to_string(Time);
basic ->
Hour_str = pad_zero(erlang:element(2, Time)),
Minute_str = pad_zero(erlang:element(3, Time)),
Second_str = pad_zero(erlang:element(4, Time)),
{Ms, Precision} = erlang:element(5, Time),
case (Ms =:= 0) orelse (Precision =:= 0) of
true ->
<<<<Hour_str/binary, Minute_str/binary>>/binary,
Second_str/binary>>;
false ->
Ms_str = pad_microseconds(Ms, Precision),
<<<<<<<<Hour_str/binary, Minute_str/binary>>/binary,
Second_str/binary>>/binary,
"."/utf8>>/binary,
Ms_str/binary>>
end
end.
-file("src/calendar/time.gleam", 112).
?DOC(" Converts a Time to ISO8601 format.\n").
-spec to_iso8601(time()) -> binary().
to_iso8601(Time) ->
to_iso8601_with_format(Time, extended).
-file("src/calendar/time.gleam", 449).
?DOC(" Get string representation for inspection.\n").
-spec inspect(time()) -> binary().
inspect(Time) ->
<<<<"~T["/utf8, (to_string(Time))/binary>>/binary, "]"/utf8>>.
-file("src/calendar/time.gleam", 167).
?DOC(" Creates a Time from seconds since midnight with microsecond precision.\n").
-spec from_seconds_after_midnight_with_microsecond(
integer(),
{integer(), integer()},
binary()
) -> {ok, time()} | {error, time_error()}.
from_seconds_after_midnight_with_microsecond(Seconds, Microsecond, Calendar) ->
case (Seconds >= 0) andalso (Seconds < 86400) of
true ->
Hours = Seconds div 3600,
Remaining_seconds = Seconds rem 3600,
Minutes = Remaining_seconds div 60,
Secs = Remaining_seconds rem 60,
new(Hours, Minutes, Secs, Microsecond, Calendar);
false ->
{error, invalid_time}
end.
-file("src/calendar/time.gleam", 162).
?DOC(" Creates a Time from seconds since midnight.\n").
-spec from_seconds_after_midnight(integer()) -> {ok, time()} |
{error, time_error()}.
from_seconds_after_midnight(Seconds) ->
from_seconds_after_midnight_with_microsecond(
Seconds,
{0, 0},
<<"Calendar.ISO"/utf8>>
).
-file("src/calendar/time.gleam", 193).
?DOC(
" Creates a Time from a Unix timestamp.\n"
" Note: This extracts only the time portion, ignoring the date.\n"
).
-spec from_timestamp(integer()) -> {ok, time()} | {error, time_error()}.
from_timestamp(Timestamp) ->
Seconds_in_day = case 86400 of
0 -> 0;
Gleam@denominator -> Timestamp rem Gleam@denominator
end,
from_seconds_after_midnight(Seconds_in_day).
-file("src/calendar/time.gleam", 293).
?DOC(" Adds a duration to a time.\n").
-spec add(time(), integer(), time_unit()) -> {ok, time()} |
{error, time_error()}.
add(Time, Amount, Unit) ->
Total_microseconds = to_microseconds_after_midnight(Time),
Microseconds_to_add = case Unit of
second ->
Amount * 1000000;
millisecond ->
Amount * 1000;
microsecond ->
Amount;
native ->
Amount
end,
New_total = Total_microseconds + Microseconds_to_add,
New_seconds = New_total div 1000000,
New_microseconds = New_total rem 1000000,
case from_seconds_after_midnight(case 86400 of
0 -> 0;
Gleam@denominator -> New_seconds rem Gleam@denominator
end) of
{ok, Base_time} ->
{_, Precision} = erlang:element(5, Time),
case new(
erlang:element(2, Base_time),
erlang:element(3, Base_time),
erlang:element(4, Base_time),
{New_microseconds, Precision},
erlang:element(6, Time)
) of
{ok, T} ->
{ok, T};
{error, E} ->
{error, E}
end;
{error, Err} ->
{error, Err}
end.
-file("src/calendar/time.gleam", 409).
?DOC(" Shift time by a duration.\n").
-spec shift(time(), calendar@duration:duration()) -> {ok, time()} |
{error, time_error()}.
shift(Time, Duration) ->
Total_seconds = ((erlang:element(6, Duration) * 3600) + (erlang:element(
7,
Duration
)
* 60))
+ erlang:element(8, Duration),
{Duration_ms, _} = erlang:element(9, Duration),
Total_microseconds = (Total_seconds * 1000000) + Duration_ms,
add(Time, Total_microseconds, microsecond).