Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions contract/system_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ static int os_date(lua_State *L) {
#endif
lua_gasuse(L, 100);
if (*s == '!') { /* UTC? */
s++; /* Skip '!' */
s++; /* Skip '!' as it always use UTC */
}
#if LJ_TARGET_POSIX
stm = gmtime_r(&t, &rtm);
Expand All @@ -322,7 +322,6 @@ static int os_date(lua_State *L) {
setfield(L, "year", stm->tm_year+1900);
setfield(L, "wday", stm->tm_wday+1);
setfield(L, "yday", stm->tm_yday+1);
setboolfield(L, "isdst", stm->tm_isdst);
} else {
char cc[3];
luaL_Buffer b;
Expand All @@ -332,15 +331,22 @@ static int os_date(lua_State *L) {
if (*s != '%' || *(s + 1) == '\0') { /* No conversion specifier? */
luaL_addchar(&b, *s);
} else {
size_t reslen;
char buff[200]; /* Should be big enough for any conversion result. */
/* strftime specifiers with deterministic output */
const char *allowed = "cCdDeFgGHjmMRSTuUVwWyY%";
cc[1] = *(++s);
if (cc[1] == 'c') {
reslen = strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", stm);
if (strchr(allowed, cc[1])) { /* Check if the specifier is allowed */
size_t reslen;
char buff[200]; /* Should be big enough for any conversion result. */
if (cc[1] == 'c') {
reslen = strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", stm);
} else {
reslen = strftime(buff, sizeof(buff), cc, stm);
}
luaL_addlstring(&b, buff, reslen);
} else {
reslen = strftime(buff, sizeof(buff), cc, stm);
luaL_addchar(&b, '%'); /* Add the previously skipped '%' */
luaL_addchar(&b, cc[1]); /* Add the not allowed character */
}
luaL_addlstring(&b, buff, reslen);
}
}
luaL_pushresult(&b);
Expand All @@ -351,19 +357,18 @@ static int os_date(lua_State *L) {
static int os_time(lua_State *L) {
time_t t;
lua_gasuse(L, 100);
if (lua_isnoneornil(L, 1)) {
t = blocktime(L);
if (lua_isnoneornil(L, 1)) { /* called without args? */
t = blocktime(L); /* get current time */
} else {
struct tm ts;
struct tm ts = {0};
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0);
ts.tm_min = getfield(L, "min", 0);
ts.tm_hour = getfield(L, "hour", 12);
ts.tm_mday = getfield(L, "day", -1);
ts.tm_mon = getfield(L, "month", -1) - 1;
ts.tm_year = getfield(L, "year", -1) - 1900;
ts.tm_isdst = getboolfield(L, "isdst");
#if LJ_TARGET_POSIX
t = timegm(&ts);
#else
Expand Down
26 changes: 16 additions & 10 deletions contract/vm_dummy/test_files/type_datetime.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,32 @@ state.var {
}

function constructor()
cdate:set(906000490)
cdate:set(905465118)
end

function CreateDate()
return system.date("%c", cdate:get())
function SetTimestamp(value)
cdate:set(value)
end

function Extract(fmt)
return system.date(fmt, cdate:get())
function CreateDate(format, timestamp)
return system.date(format, timestamp)
end

function Extract(format)
return system.date(format, cdate:get())
end

function Difftime()
system.print(system.date("%c", cdate:get()))
-- test convertion to table
s = system.date("*t", cdate:get())
system.print(s)
-- modification of table
s.hour = 2
s.min = 0
s.sec = 0
system.print(system.date("*t", system.time(s)))
return system.difftime(cdate:get(), system.time(s))
-- system.difftime() and system.time()
diff = system.difftime(cdate:get(), system.time(s))
-- conversion of diff to hours
return diff, system.date("%T",diff)
end

abi.register(CreateDate, Extract, Difftime)
abi.register(CreateDate, SetTimestamp, Extract, Difftime)
96 changes: 90 additions & 6 deletions contract/vm_dummy/vm_dummy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1995,22 +1995,106 @@ func TestTypeDatetime(t *testing.T) {
err = bc.ConnectBlock(NewLuaTxAccount("user1", 1, types.Aergo), NewLuaTxDeploy("user1", "datetime", 0, code))
require.NoErrorf(t, err, "failed to deploy")

err = bc.Query("datetime", `{"Name": "CreateDate"}`, "", `"1998-09-17 02:48:10"`)
// not allowed specifiers

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%a%A%b%B%h%I%n%p%r%t%x%X%z%Z"]}`, "", `"%a%A%b%B%h%I%n%p%r%t%x%X%z%Z"`)
require.NoErrorf(t, err, "failed to query")

// allowed specifiers

specifiers := map[string]string{
"%c": "1998-09-10 22:05:18",
"%C": "19",
"%d": "10",
"%D": "09/10/98",
"%e": "10",
"%F": "1998-09-10",
"%g": "98",
"%G": "1998",
"%H": "22",
"%j": "253", // Day of the year [001,366]
"%m": "09",
"%M": "05",
"%R": "22:05",
"%S": "18",
"%T": "22:05:18",
"%u": "4", // Monday as 1 through Sunday as 7
"%U": "36", // Week number of the year (Sunday as the first day of the week)
"%V": "37", // ISO 8601 week number
"%w": "4", // Sunday as 0, Saturday as 6
"%W": "36", // Week number of the year (Monday as the first day of the week)
"%y": "98",
"%Y": "1998",
"%%": "%",
}

for specifier, expected := range specifiers {
err := bc.Query("datetime", `{"Name": "Extract", "Args":["`+specifier+`"]}`, "", `"`+expected+`"`)
require.NoErrorf(t, err, "failed to query with specifier %s", specifier)
}

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%FT%T"]}`, "", `"1998-09-10T22:05:18"`)
require.NoErrorf(t, err, "failed to query")

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%x"]}`, "", `"09/17/98"`)
err = bc.Query("datetime", `{"Name": "Extract", "Args":["%Y-%m-%d %H:%M:%S"]}`, "", `"1998-09-10 22:05:18"`)
require.NoErrorf(t, err, "failed to query")

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%X"]}`, "", `"02:48:10"`)
err = bc.Query("datetime", `{"Name": "Difftime"}`, "", `[72318,"20:05:18"]`)
require.NoErrorf(t, err, "failed to query")

// set a fixed timestamp for the next block
bc.SetTimestamp(false, 1696286666)
// need to create the block for the next queries to use the value
err = bc.ConnectBlock(
NewLuaTxCall("user1", "datetime", 0, `{"Name": "SetTimestamp", "Args": [2527491900]}`),
)
require.NoErrorf(t, err, "failed to call tx")

// use the block timestamp

err = bc.Query("datetime", `{"Name": "CreateDate", "Args":["%Y-%m-%d %H:%M:%S"]}`, "", `"2023-10-02 22:44:26"`)
require.NoErrorf(t, err, "failed to query")

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%A"]}`, "", `"Thursday"`)
// used the new stored timestamp

specifiers = map[string]string{
"%c": "2050-02-03 09:05:00",
"%C": "20",
"%d": "03",
"%D": "02/03/50",
"%e": " 3", // Space-padded day of the month
"%F": "2050-02-03",
"%g": "50",
"%G": "2050",
"%H": "09",
"%j": "034", // Day of the year [001,366]
"%m": "02",
"%M": "05",
"%R": "09:05",
"%S": "00",
"%T": "09:05:00",
"%u": "4", // Thursday (Monday as 1, Sunday as 7)
"%U": "05", // Week number of the year (Sunday as the first day of the week)
"%V": "05", // ISO 8601 week number
"%w": "4", // Sunday as 0, Saturday as 6
"%W": "05", // Week number of the year (Monday as the first day of the week)
"%y": "50",
"%Y": "2050",
"%%": "%",
}

for specifier, expected := range specifiers {
err := bc.Query("datetime", `{"Name": "Extract", "Args":["`+specifier+`"]}`, "", `"`+expected+`"`)
require.NoErrorf(t, err, "failed to query with specifier %s", specifier)
}

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%FT%T"]}`, "", `"2050-02-03T09:05:00"`)
require.NoErrorf(t, err, "failed to query")

err = bc.Query("datetime", `{"Name": "Extract", "Args":["%I:%M:%S %p"]}`, "", `"02:48:10 AM"`)
err = bc.Query("datetime", `{"Name": "Extract", "Args":["%Y-%m-%d %H:%M:%S"]}`, "", `"2050-02-03 09:05:00"`)
require.NoErrorf(t, err, "failed to query")

err = bc.Query("datetime", `{"Name": "Difftime"}`, "", `2890`)
err = bc.Query("datetime", `{"Name": "Difftime"}`, "", `[25500,"07:05:00"]`)
require.NoErrorf(t, err, "failed to query")

}
Expand Down