Skip to content

Commit 12d79cb

Browse files
committed
feat(builtins): add two new builtins to manipulate dates
1 parent 54225fe commit 12d79cb

17 files changed

Lines changed: 160 additions & 14 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- new builtin to extract the bytes of a string: builtin__string:bytes
1010
- math:fromBase to convert a number from a given base to base 10
1111
- string:surrogate? and string:privateUse? to check if an unicode character is in one of those planes
12+
- new builtins `builtin__time:timeToDate` and `builtin__time:parseDate` to convert a timestamp to a date, and parse a string date as a timestamp
1213

1314
### Changed
1415
- math:toBase handles 0 correctly

include/Ark/Builtins/Builtins.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ namespace Ark::internal::Builtins
6262
namespace Time
6363
{
6464
ARK_BUILTIN(timeSinceEpoch);
65+
ARK_BUILTIN(timestampToDate);
66+
ARK_BUILTIN(parseDate);
6567
}
6668

6769
namespace System

src/arkreactor/Builtins/Builtins.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ namespace Ark::internal::Builtins
5151

5252
// Time
5353
{ "time", Value(Time::timeSinceEpoch) },
54+
{ "builtin__time:timeToDate", Value(Time::timestampToDate) },
55+
{ "builtin__time:parseDate", Value(Time::parseDate) },
5456

5557
// System
5658
{ "builtin__sys:platform", platform },

src/arkreactor/Builtins/Time.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#include <Ark/Builtins/Builtins.hpp>
22

3+
#include <Ark/VM/DefaultValues.hpp>
4+
#include <Ark/VM/Value/Dict.hpp>
5+
#include <Ark/TypeChecker.hpp>
6+
37
#undef abs
48
#include <chrono>
9+
#include <ctime>
510

611
#include <Ark/VM/VM.hpp>
712

@@ -15,11 +20,92 @@ namespace Ark::internal::Builtins::Time
1520
* =end
1621
* @author https://github.com/SuperFola
1722
*/
23+
// cppcheck-suppress constParameterReference
1824
Value timeSinceEpoch(std::vector<Value>& n [[maybe_unused]], VM* vm [[maybe_unused]])
1925
{
2026
const auto now = std::chrono::system_clock::now();
2127
const auto epoch = now.time_since_epoch();
2228
const auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(epoch);
2329
return Value(static_cast<double>(microseconds.count()) / 1000000);
2430
}
31+
32+
Value timestampToDate(std::vector<Value>& n, VM* vm [[maybe_unused]])
33+
{
34+
if (!types::check(n, ValueType::Number, ValueType::Any))
35+
throw types::TypeCheckingError(
36+
"timeToDate",
37+
{ { types::Contract {
38+
{ types::Typedef("timestamp", ValueType::Number),
39+
types::Typedef("utc?", ValueType::Any) } } } },
40+
n);
41+
42+
const bool is_utc = n[1] == True;
43+
const auto timestamp = std::chrono::milliseconds(static_cast<long long>(1000.0 * n[0].number()));
44+
const auto timepoint = std::chrono::time_point<std::chrono::system_clock>(timestamp);
45+
const std::time_t time = std::chrono::system_clock::to_time_t(timepoint);
46+
const std::tm* calendar_time = is_utc ? std::gmtime(&time) : std::localtime(&time);
47+
48+
if (calendar_time != nullptr)
49+
{
50+
internal::Dict dict;
51+
dict.set(Value("millisecond"), Value(timestamp.count() % 1000));
52+
dict.set(Value("second"), Value(calendar_time->tm_sec));
53+
dict.set(Value("minute"), Value(calendar_time->tm_min));
54+
dict.set(Value("hour"), Value(calendar_time->tm_hour));
55+
dict.set(Value("day"), Value(calendar_time->tm_mday));
56+
dict.set(Value("month"), Value(calendar_time->tm_mon + 1));
57+
dict.set(Value("year"), Value(calendar_time->tm_year + 1900));
58+
dict.set(Value("week_day"), Value(calendar_time->tm_wday));
59+
dict.set(Value("year_day"), Value(calendar_time->tm_yday));
60+
dict.set(Value("is_dst"), calendar_time->tm_isdst ? True : False);
61+
62+
return Value(std::move(dict));
63+
}
64+
return Nil;
65+
}
66+
67+
int64_t makeTimestamp(const int tm_sec, const int tm_min, const int tm_hour, const int tm_mday, const int tm_mon, const int tm_year)
68+
{
69+
constexpr int MonthsPerYear = 12;
70+
static const std::array<int, MonthsPerYear> cumulative_days = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
71+
72+
const long year = 1900 + tm_year + tm_mon / MonthsPerYear;
73+
int64_t result = (year - 1970) * 365 + cumulative_days[static_cast<std::size_t>(tm_mon % MonthsPerYear)];
74+
result += (year - 1968) / 4;
75+
result -= (year - 1900) / 100;
76+
result += (year - 1600) / 400;
77+
if ((year % 4) == 0 &&
78+
((year % 100) != 0 || (year % 400) == 0) &&
79+
(tm_mon % MonthsPerYear) < 2)
80+
result--;
81+
result += tm_mday - 1;
82+
result *= 24;
83+
result += tm_hour;
84+
result *= 60;
85+
result += tm_min;
86+
result *= 60;
87+
result += tm_sec;
88+
return result;
89+
}
90+
91+
Value parseDate(std::vector<Value>& n, VM* vm [[maybe_unused]])
92+
{
93+
if (!types::check(n, ValueType::String) && !types::check(n, ValueType::String, ValueType::String))
94+
throw types::TypeCheckingError(
95+
"parseDate",
96+
{ { types::Contract {
97+
{ types::Typedef("date", ValueType::String) } },
98+
types::Contract {
99+
{ types::Typedef("date", ValueType::String),
100+
types::Typedef("format", ValueType::String) } } } },
101+
n);
102+
103+
std::tm t = {};
104+
std::istringstream ss(n[0].string());
105+
ss >> std::get_time(&t, n.size() == 1 ? "%Y-%m-%dT%H:%M:%S" : n[1].string().c_str());
106+
107+
if (ss.fail())
108+
return Nil;
109+
return Value(makeTimestamp(t.tm_sec, t.tm_min, t.tm_hour, t.tm_mday, t.tm_mon, t.tm_year));
110+
}
25111
}

tests/unittests/resources/CompilerSuite/ir/99bottles.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ page_0
3737
LOAD_CONST 3
3838
LOAD_FAST 4
3939
LOAD_FAST 4
40-
CALL_BUILTIN 27, 3
40+
CALL_BUILTIN 29, 3
4141
.L7:
4242
CALL_BUILTIN 9, 1
4343
.L6:
@@ -50,7 +50,7 @@ page_0
5050
PUSH_RETURN_ADDRESS L9
5151
LOAD_CONST 4
5252
LOAD_FAST 4
53-
CALL_BUILTIN 27, 2
53+
CALL_BUILTIN 29, 2
5454
.L9:
5555
CALL_BUILTIN 9, 1
5656
.L8:

tests/unittests/resources/CompilerSuite/ir/breakpoints.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ page_1
6262
LOAD_FAST_BY_INDEX 0
6363
LOAD_FAST_BY_INDEX 1
6464
LOAD_FAST_BY_INDEX 2
65-
CALL_BUILTIN 27, 4
65+
CALL_BUILTIN 29, 4
6666
.L1:
6767
CALL_BUILTIN 9, 1
6868
.L0:

tests/unittests/resources/CompilerSuite/ir/operators_as_builtins.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
page_0
22
PUSH_RETURN_ADDRESS L0
3-
BUILTIN 87
4-
BUILTIN 88
53
BUILTIN 89
64
BUILTIN 90
75
BUILTIN 91
@@ -25,6 +23,8 @@ page_0
2523
BUILTIN 109
2624
BUILTIN 110
2725
BUILTIN 111
26+
BUILTIN 112
27+
BUILTIN 113
2828
CALL_BUILTIN 9, 25
2929
.L0:
3030
POP 0

tests/unittests/resources/CompilerSuite/ir/plugin.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ page_0
88
.L1:
99
EQ 0
1010
LOAD_CONST 3
11-
CALL_BUILTIN 26, 2
11+
CALL_BUILTIN 28, 2
1212
.L0:
1313
POP 0
1414
HALT 0

tests/unittests/resources/CompilerSuite/optimized_ir/99bottles.expected

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ page_0
22
LOAD_CONST_STORE 0, 0
33
LOAD_CONST_STORE 1, 2
44
LOAD_CONST_STORE 2, 4
5-
BUILTIN 22
5+
BUILTIN 24
66
STORE 6
77
STORE_FROM 8, 7
88
STORE_FROM 10, 9
@@ -37,7 +37,7 @@ page_0
3737
LOAD_CONST 6
3838
LOAD_FAST 13
3939
LOAD_FAST 13
40-
CALL_BUILTIN 27, 3
40+
CALL_BUILTIN 29, 3
4141
.L10:
4242
CALL_BUILTIN 9, 1
4343
.L9:
@@ -47,7 +47,7 @@ page_0
4747
PUSH_RETURN_ADDRESS L12
4848
LOAD_CONST 7
4949
LOAD_FAST 13
50-
CALL_BUILTIN 27, 2
50+
CALL_BUILTIN 29, 2
5151
.L12:
5252
CALL_BUILTIN 9, 1
5353
.L11:
@@ -58,19 +58,19 @@ page_0
5858
HALT 0
5959

6060
page_1
61-
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 23, 1
61+
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 25, 1
6262
.L0:
6363
RET 0
6464
HALT 0
6565

6666
page_2
67-
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 24, 1
67+
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 26, 1
6868
.L1:
6969
RET 0
7070
HALT 0
7171

7272
page_3
73-
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 25, 1
73+
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS 27, 1
7474
.L2:
7575
RET 0
7676
HALT 0

0 commit comments

Comments
 (0)