First stab at a time abstraction.
[hope.git] / src / hope_time.erl
1 -module(hope_time).
2
3 -export_type(
4 [ t/0
5 ]).
6
7 -export(
8 [ now/0
9 , of_timestamp/1
10 , to_unix_time/1
11 , of_iso8601/1
12
13 % Floatable
14 , of_float/1
15 , to_float/1
16
17 % TODO: Stringable
18 ]).
19
20
21 -define(T, #?MODULE).
22
23
24 -record(?MODULE,
25 { unix_time :: float()
26 }).
27
28 -opaque t() ::
29 ?T{}.
30
31
32 -spec now() ->
33 t().
34 now() ->
35 Timestamp = os:timestamp(),
36 of_timestamp(Timestamp).
37
38 -spec of_timestamp(erlang:timestamp()) ->
39 t().
40 of_timestamp({MegasecondsInt, SecondsInt, MicrosecondsInt}) ->
41 Million = 1000000.0,
42 Megaseconds = float(MegasecondsInt),
43 Seconds = float(SecondsInt),
44 Microseconds = float(MicrosecondsInt),
45 UnixTime = (Megaseconds * Million) + Seconds + (Microseconds / Million),
46 ?T{unix_time = UnixTime}.
47
48 -spec to_unix_time(t()) ->
49 float().
50 to_unix_time(?T{unix_time=UnixTime}) ->
51 UnixTime.
52
53 -spec of_float(float()) ->
54 t().
55 of_float(Float) when is_float(Float) ->
56 ?T{unix_time = Float}.
57
58 -spec to_float(t()) ->
59 float().
60 to_float(?T{unix_time=Float}) ->
61 Float.
62
63 -spec of_iso8601(binary()) ->
64 hope_result:t(t(), {unrecognized_as_iso8601, binary()}).
65 of_iso8601(<<Bin/binary>>) ->
66 % We use regexp rather than just simple binary pattern match, because we
67 % also want to validate character ranges, i.e., that components are
68 % integers.
69 ValidPatterns =
70 [ {zoneless, <<"\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d">>}
71 ],
72 ValidPatternMatchers =
73 [{Tag, make_regexp_bool(RegExp)} || {Tag, RegExp} <- ValidPatterns],
74 case hope_list:first_match(ValidPatternMatchers, Bin)
75 of none -> {error, {unrecognized_as_iso8601, Bin}}
76 ; {some, zoneless} -> {ok, of_iso8601_zoneless(Bin)}
77 end.
78
79 -spec of_iso8601_zoneless(binary()) ->
80 t().
81 of_iso8601_zoneless(<<Bin/binary>>) ->
82 << YearBin:4/binary, "-", MonthBin:2/binary, "-", DayBin:2/binary
83 , "T"
84 , HourBin:2/binary, ":", MinBin:2/binary , ":", SecBin:2/binary
85 >> = Bin,
86 Year = binary_to_integer(YearBin),
87 Month = binary_to_integer(MonthBin),
88 Day = binary_to_integer(DayBin),
89 Hour = binary_to_integer(HourBin),
90 Min = binary_to_integer(MinBin),
91 Sec = binary_to_integer(SecBin),
92 DateTime = {{Year, Month, Day}, {Hour, Min, Sec}},
93 SecondsGregorian = calendar:datetime_to_gregorian_seconds(DateTime),
94 SecondsFromZeroToUnixEpoch = 62167219200,
95 SecondsUnixEpochInt = SecondsGregorian - SecondsFromZeroToUnixEpoch,
96 SecondsUnixEpoch = float(SecondsUnixEpochInt),
97 of_float(SecondsUnixEpoch).
98
99 -spec make_regexp_bool(binary()) ->
100 fun((binary()) -> boolean()).
101 make_regexp_bool(<<RegExp/binary>>) ->
102 fun (<<String/binary>>) ->
103 case re:run(String, RegExp)
104 of nomatch -> false
105 ; {match, _} -> true
106 end
107 end.
This page took 0.06828 seconds and 4 git commands to generate.