Skip to content

Commit

Permalink
Merge pull request atomvm#672 from fadushin/system_time_to_universal_…
Browse files Browse the repository at this point in the history
…time

Added support for `calendar:system_time_to_universal_time/2`

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Jul 9, 2023
2 parents 799d9ae + 9bfff73 commit 78f4eb0
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for code:load_abs/1, code:load_binary/3
- Added support for loading / closing AVMPacks at runtime
- Added support for ESP-IDF v5.x
- Added support for `calendar:system_time_to_universal_time/2`

### Fixed
- Fixed issue with formatting integers with io:format() on STM32 platform
Expand Down
8 changes: 8 additions & 0 deletions doc/src/programmers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,14 @@ Use `erlang:universaltime/0` to get the current time at second resolution, to ob

> Note. Setting the system time is done in a platform-specific manner. For information about how to set system time on the ESP32, see the [Network Programming Guide](./network-programming-guide.md).
To convert a time (in seconds, milliseconds, or microseconds from the UNIX epoch) to a date-time, use the `calendar:system_time_to_universal_time/2` function. For example,

%% erlang
Milliseconds = ... %% get milliseconds from the UNIX epoch
{{Year, Month, Day}, {Hour, Minute, Second}} = calendar:system_time_to_universal_time(Milliseconds, millisecond).

As above, valid time units are `second`, `millisecond`, and `microsecond`.

### Miscellaneous

Use the `erlang:md5/1` function to compute the MD5 hash of an input binary. The output is a fixed-length binary ()
Expand Down
26 changes: 25 additions & 1 deletion libs/estdlib/src/calendar.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,17 @@
%%-----------------------------------------------------------------------------
-module(calendar).

-export([date_to_gregorian_days/1, date_to_gregorian_days/3, day_of_the_week/1, day_of_the_week/3]).
-export([
date_to_gregorian_days/1,
date_to_gregorian_days/3,
day_of_the_week/1,
day_of_the_week/3,
system_time_to_universal_time/2
]).

-type date() :: {year(), month(), day()}.
-type time() :: {hour(), minute(), second()}.
-type datetime() :: {date(), time()}.

%%-----------------------------------------------------------------------------
%% @doc Year cannot be abbreviated.
Expand All @@ -52,8 +60,12 @@

-type month() :: 1..12.
-type day() :: 1..31.
-type hour() :: 0..23.
-type minute() :: 0..59.
-type second() :: 0..59.
-type gregorian_days() :: integer().
-type day_of_week() :: 1..7.
-type time_unit() :: second | millisecond | microsecond.

%%-----------------------------------------------------------------------------
%% @equiv date_to_gregorian_days(Year, M, D)
Expand Down Expand Up @@ -122,3 +134,15 @@ day_of_the_week({Y, M, D}) ->
-spec day_of_the_week(Y :: year(), M :: month(), D :: day()) -> day_of_week().
day_of_the_week(Y, M, D) ->
(date_to_gregorian_days(Y, M, D) + 5) rem 7 + 1.

%%-----------------------------------------------------------------------------
%% @param Time the time, as an integer, in the specified unit
%% @param TimeUnit the time unit
%% @param The date and time (in UTC) converted from the specified time and time unit
%% @doc Convert an integer time value to a date and time in UTC.
%% @end
%%-----------------------------------------------------------------------------
-spec system_time_to_universal_time(Time :: integer(), TimeUnit :: time_unit()) ->
datetime().
system_time_to_universal_time(_Time, _TimeUnit) ->
throw(nif_error).
36 changes: 36 additions & 0 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static term nif_binary_first_1(Context *ctx, int argc, term argv[]);
static term nif_binary_last_1(Context *ctx, int argc, term argv[]);
static term nif_binary_part_3(Context *ctx, int argc, term argv[]);
static term nif_binary_split_2(Context *ctx, int argc, term argv[]);
static term nif_calendar_system_time_to_universal_time_2(Context *ctx, int argc, term argv[]);
static term nif_erlang_delete_element_2(Context *ctx, int argc, term argv[]);
static term nif_erlang_atom_to_binary_2(Context *ctx, int argc, term argv[]);
static term nif_erlang_atom_to_list_1(Context *ctx, int argc, term argv[]);
Expand Down Expand Up @@ -482,6 +483,12 @@ static const struct Nif timestamp_nif =
.nif_ptr = nif_erlang_timestamp_0
};

static const struct Nif system_time_to_universal_time_nif =
{
.base.type = NIFFunctionType,
.nif_ptr = nif_calendar_system_time_to_universal_time_2
};

static const struct Nif tuple_to_list_nif =
{
.base.type = NIFFunctionType,
Expand Down Expand Up @@ -1555,6 +1562,35 @@ term nif_erlang_timestamp_0(Context *ctx, int argc, term argv[])
return timestamp_tuple;
}

term nif_calendar_system_time_to_universal_time_2(Context *ctx, int argc, term argv[])
{
UNUSED(ctx);
UNUSED(argc);

struct timespec ts;

avm_int64_t value = term_maybe_unbox_int64(argv[0]);

if (argv[1] == SECOND_ATOM) {
ts.tv_sec = (time_t) value;
ts.tv_nsec = 0;

} else if (argv[1] == MILLISECOND_ATOM) {
ts.tv_sec = (time_t) (value / 1000);
ts.tv_nsec = (value % 1000) * 1000000;

} else if (argv[1] == MICROSECOND_ATOM) {
ts.tv_sec = (time_t) (value / 1000000);
ts.tv_nsec = (value % 1000000) * 1000;

} else {
RAISE_ERROR(BADARG_ATOM);
}

struct tm broken_down_time;
return build_datetime_from_tm(ctx, gmtime_r(&ts.tv_sec, &broken_down_time));
}

static term nif_erlang_make_tuple_2(Context *ctx, int argc, term argv[])
{
UNUSED(argc);
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ binary:first/1, &binary_first_nif
binary:last/1, &binary_last_nif
binary:part/3, &binary_part_nif
binary:split/2, &binary_split_nif
calendar:system_time_to_universal_time/2, &system_time_to_universal_time_nif
erlang:atom_to_binary/2, &atom_to_binary_nif
erlang:atom_to_list/1, &atom_to_list_nif
erlang:binary_to_atom/2, &binary_to_atom_nif
Expand Down
4 changes: 2 additions & 2 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ compile_erlang(compact27bitsinteger)
compile_erlang(compact23bitsneginteger)
compile_erlang(negatives2)
compile_erlang(datetime)
compile_erlang(timestamp)
compile_erlang(test_system_time)
compile_erlang(is_type)
compile_erlang(test_bitshift)
compile_erlang(test_bitwise)
Expand Down Expand Up @@ -542,7 +542,7 @@ add_custom_target(erlang_test_modules DEPENDS
compact23bitsneginteger.beam
negatives2.beam
datetime.beam
timestamp.beam
test_system_time.beam
is_type.beam
test_bitshift.beam
test_bitwise.beam
Expand Down
79 changes: 79 additions & 0 deletions tests/erlang_tests/test_system_time.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
%
% This file is part of AtomVM.
%
% Copyright 2018-2023 Davide Bettio <davide@uninstall.it>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_system_time).

-export([start/0]).

start() ->
ok = test_system_time(second, 1001),
ok = test_system_time(millisecond, 10),
ok = test_system_time(microsecond, 1),

ok = expect(fun() -> erlang:system_time(not_a_time_unit) end, badarg),

ok = test_system_time_to_universal_time(),

0.

test_system_time(Unit, SleepMs) ->
Before = verify_system_time_value(erlang:system_time(Unit)),
sleep(SleepMs),
After = verify_system_time_value(erlang:system_time(Unit)),
true = (After > Before),
ok.

verify_system_time_value(M) when is_integer(M) andalso M > 0 ->
M.

sleep(Ms) ->
receive
after Ms ->
ok
end.

expect(F, Expect) ->
try
F(),
fail
catch
_:E when E == Expect ->
ok
end.

test_system_time_to_universal_time() ->
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, second),
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1, second),

{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, millisecond),
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, millisecond),
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1000, millisecond),
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1001, millisecond),

{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, microsecond),
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(1, microsecond),
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(1000, microsecond),
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1000000, microsecond),

{{2023, 7, 8}, {20, 19, 39}} = calendar:system_time_to_universal_time(1688847579, second),

{{1969, 12, 31}, {23, 59, 59}} = calendar:system_time_to_universal_time(-1, second),

ok.
29 changes: 0 additions & 29 deletions tests/erlang_tests/timestamp.erl

This file was deleted.

2 changes: 1 addition & 1 deletion tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ struct Test tests[] = {
TEST_CASE_EXPECTED(compact23bitsneginteger, -47376112),
TEST_CASE_EXPECTED(negatives2, -500),
TEST_CASE_EXPECTED(datetime, 3),
TEST_CASE_EXPECTED(timestamp, 1),
TEST_CASE(test_system_time),
TEST_CASE_EXPECTED(is_type, 255),
TEST_CASE(test_bitshift),
TEST_CASE_EXPECTED(test_bitwise, -4),
Expand Down

0 comments on commit 78f4eb0

Please sign in to comment.