feat(plugins): Add c2.DateTime (#7096)

This adds the `DateTime` API described in #7021.

Reviewed-by: Mm2PL <mm2pl+gh@kotmisia.pl>
Reviewed-by: pajlada <rasmus.karlsson@pajlada.com>
This commit is contained in:
Nerixyz
2026-07-19 13:26:21 +00:00
committed by GitHub
parent 14b5c665a4
commit d853137820
10 changed files with 371 additions and 0 deletions
+14
View File
@@ -664,6 +664,20 @@ declare namespace c2 {
}
var windows: WindowManager;
class DateTime {
static from_iso_string(str: string): DateTime;
to_iso_string(): string;
to_iso_string_without_ms(): string;
static current_local(): DateTime;
static current_utc(): DateTime;
static from_unix_milliseconds(ts: number): DateTime;
static from_unix_seconds(ts: number): DateTime;
to_unix_milliseconds(): number;
to_unix_seconds(): number;
}
}
declare module "chatterino.json" {
+53
View File
@@ -332,6 +332,58 @@ function c2.ConnectionHandle:is_connected() end
-- End src/controllers/plugins/api/ConnectionHandle.hpp
-- Begin src/controllers/plugins/api/DateTime.hpp
---A zoned date and time.
---@class c2.DateTime
c2.DateTime = {}
---Parse a date from an ISO 8601 string with milliseconds (yyyy-MM-ddTHH:mm:ss.zzz)
---@param str string
---@return c2.DateTime
function c2.DateTime.from_iso_string(str) end
---Format the datetime as an ISO string with milliseconds (yyyy-MM-ddTHH:mm:ss.zzz)
---@return string
function c2.DateTime:to_iso_string() end
---Format the datetime as an ISO string without milliseconds (yyyy-MM-ddTHH:mm:ss)
---@return string
function c2.DateTime:to_iso_string_without_ms() end
---Get the current datetime in the system's local time zone.
---@return c2.DateTime
function c2.DateTime.current_local() end
---Get the current datetime in the UTC time zone (00:00).
---@return c2.DateTime
function c2.DateTime.current_utc() end
---Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
---
---The returned date time will be in the local time zone.
---@param ts number
---@return c2.DateTime
function c2.DateTime.from_unix_milliseconds(ts) end
---Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
---
---The returned date time will be in the local time zone.
---@param ts number
---@return c2.DateTime
function c2.DateTime.from_unix_seconds(ts) end
---Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
---@return number
function c2.DateTime:to_unix_milliseconds() end
---Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
---@return number
function c2.DateTime:to_unix_seconds() end
-- End src/controllers/plugins/api/DateTime.hpp
-- Begin src/controllers/plugins/api/HTTPResponse.hpp
---@class c2.HTTPResponse
@@ -801,6 +853,7 @@ c2.MessageFlag = {
InvalidReplyTarget = 0,
WatchStreak = 0,
Announcement = 0,
UncategorizedNotification = 0,
}
-- End src/messages/MessageFlag.hpp
+44
View File
@@ -981,6 +981,50 @@ Get all open windows.
The global [`WindowManager`](#windowmanager).
#### `DateTime`
A zoned date and time.
##### `DateTime.from_iso_string(str)`
Parse a date from an ISO 8601 string with milliseconds (`yyyy-MM-ddTHH:mm:ss.zzz±hh:mm`)
##### `DateTime:to_iso_string()`
Format the datetime as an ISO string with milliseconds (`yyyy-MM-ddTHH:mm:ss.zzz±hh:mm`)
##### `DateTime:to_iso_string_without_ms()`
Format the datetime as an ISO string without milliseconds (`yyyy-MM-ddTHH:mm:ss±hh:mm`)
##### `DateTime.current_local()`
Get the current datetime in the system's local time zone.
##### `DateTime.current_utc()`
Get the current datetime in the UTC time zone (00:00).
##### `DateTime.from_unix_milliseconds(ts)`
Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
The returned date time will be in the local time zone.
##### `DateTime.from_unix_seconds(ts)`
Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
The returned date time will be in the local time zone.
##### `DateTime:to_unix_milliseconds()`
Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
##### `DateTime:to_unix_seconds()`
Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
### Input/Output API
These functions are wrappers for Lua's I/O library. Functions on file pointer
+2
View File
@@ -262,6 +262,8 @@ set(SOURCE_FILES
controllers/plugins/api/ChannelRef.hpp
controllers/plugins/api/ConnectionHandle.cpp
controllers/plugins/api/ConnectionHandle.hpp
controllers/plugins/api/DateTime.cpp
controllers/plugins/api/DateTime.hpp
controllers/plugins/api/DebugLibrary.cpp
controllers/plugins/api/DebugLibrary.hpp
controllers/plugins/api/EventType.hpp
+1
View File
@@ -95,6 +95,7 @@ sol::table toTable(lua_State *L, const CompletionEvent &ev);
* @includefile controllers/plugins/api/Accounts.hpp
* @includefile controllers/plugins/api/ChannelRef.hpp
* @includefile controllers/plugins/api/ConnectionHandle.hpp
* @includefile controllers/plugins/api/DateTime.hpp
* @includefile controllers/plugins/api/HTTPResponse.hpp
* @includefile controllers/plugins/api/HTTPRequest.hpp
* @includefile controllers/plugins/api/Images.hpp
@@ -14,6 +14,7 @@
# include "controllers/plugins/api/Accounts.hpp"
# include "controllers/plugins/api/ChannelRef.hpp"
# include "controllers/plugins/api/ConnectionHandle.hpp"
# include "controllers/plugins/api/DateTime.hpp"
# include "controllers/plugins/api/DebugLibrary.hpp"
# include "controllers/plugins/api/HTTPRequest.hpp"
# include "controllers/plugins/api/HTTPResponse.hpp"
@@ -255,6 +256,7 @@ void PluginController::initSol(sol::state_view &lua, Plugin *plugin)
lua::api::images::createUserTypes(c2);
lua::api::createAccounts(c2);
lua::api::windowmanager::createUserTypes(c2);
lua::api::datetime::createUserTypes(c2);
c2["ChannelType"] = lua::createEnumTable<Channel::Type>(lua);
c2["HTTPMethod"] = lua::createEnumTable<NetworkRequestType>(lua);
c2["EventType"] = lua::createEnumTable<lua::api::EventType>(lua);
+68
View File
@@ -0,0 +1,68 @@
#include "controllers/plugins/api/DateTime.hpp"
#ifdef CHATTERINO_HAVE_PLUGINS
# include "controllers/plugins/SolTypes.hpp" // IWYU pragma: keep
# include <QDateTime>
namespace chatterino::lua::api::datetime {
void createUserTypes(sol::table &c2)
{
c2.new_usertype<QDateTime>(
"DateTime", sol::no_constructor, //
// __tostring uses the ISO date.
sol::meta_method::to_string,
[](const QDateTime &self) {
return self.toString(Qt::ISODateWithMs);
},
// ISO 8601 conversion.
"from_iso_string",
[](const QString &s) {
return QDateTime::fromString(s, Qt::ISODateWithMs);
},
"to_iso_string",
[](const QDateTime &self) {
return self.toString(Qt::ISODateWithMs);
},
"to_iso_string_without_ms",
[](const QDateTime &self) {
return self.toString(Qt::ISODate);
},
// System time constructors.
"current_local",
[] {
return QDateTime::currentDateTime();
},
"current_utc",
[] {
return QDateTime::currentDateTimeUtc();
},
// Unix (milli)seconds conversion.
"from_unix_milliseconds",
[](int64_t ts) {
return QDateTime::fromMSecsSinceEpoch(ts);
},
"from_unix_seconds",
[](int64_t ts) {
return QDateTime::fromSecsSinceEpoch(ts);
},
"to_unix_milliseconds",
[](const QDateTime &self) {
return self.toMSecsSinceEpoch();
},
"to_unix_seconds",
[](const QDateTime &self) {
return self.toSecsSinceEpoch();
} //
);
}
} // namespace chatterino::lua::api::datetime
#endif
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#ifdef CHATTERINO_HAVE_PLUGINS
# include <sol/forward.hpp>
namespace chatterino::lua::api::datetime {
/* @lua-fragment
---A zoned date and time.
---@class c2.DateTime
c2.DateTime = {}
---Parse a date from an ISO 8601 string with milliseconds (yyyy-MM-ddTHH:mm:ss.zzz)
---@param str string
---@return c2.DateTime
function c2.DateTime.from_iso_string(str) end
---Format the datetime as an ISO string with milliseconds (yyyy-MM-ddTHH:mm:ss.zzz)
---@return string
function c2.DateTime:to_iso_string() end
---Format the datetime as an ISO string without milliseconds (yyyy-MM-ddTHH:mm:ss)
---@return string
function c2.DateTime:to_iso_string_without_ms() end
---Get the current datetime in the system's local time zone.
---@return c2.DateTime
function c2.DateTime.current_local() end
---Get the current datetime in the UTC time zone (00:00).
---@return c2.DateTime
function c2.DateTime.current_utc() end
---Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
---
---The returned date time will be in the local time zone.
---@param ts number
---@return c2.DateTime
function c2.DateTime.from_unix_milliseconds(ts) end
---Get a datetime from a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
---
---The returned date time will be in the local time zone.
---@param ts number
---@return c2.DateTime
function c2.DateTime.from_unix_seconds(ts) end
---Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in milliseconds.
---@return number
function c2.DateTime:to_unix_milliseconds() end
---Convert a datetime to a Unix timestamp (offset from 1970-01-01 00:00 UTC) in seconds.
---@return number
function c2.DateTime:to_unix_seconds() end
*/
/// Creates
/// - `c2.DateTime` (QDateTime)
void createUserTypes(sol::table &c2);
} // namespace chatterino::lua::api::datetime
#endif
+109
View File
@@ -0,0 +1,109 @@
---@generic T
---@param a T
---@param b T
local function assert_eq(a, b)
if a ~= b then
error(tostring(a) .. " ~= " .. tostring(b))
end
end
local specs_ms = {
{
iso_ms = "2026-07-10T20:14:41.477Z",
iso_sec = "2026-07-10T20:14:41Z",
unix_ms = 1783714481477,
unix_sec = 1783714481,
},
{
iso_ms = "1930-11-18T00:28:29.123Z",
iso_sec = "1930-11-18T00:28:29Z",
unix_ms = -1234567890877,
unix_sec = -1234567890,
},
}
local specs_sec = {
{
iso_ms = "2026-07-10T20:14:41.000Z",
iso_sec = "2026-07-10T20:14:41Z",
unix_ms = 1783714481000,
unix_sec = 1783714481,
},
{
iso_ms = "1930-11-18T00:28:29.000Z",
iso_sec = "1930-11-18T00:28:29Z",
unix_ms = -1234567891000,
unix_sec = -1234567891,
},
}
local tests = {
iso_string_invalid = function()
local dt = c2.DateTime.from_iso_string("invalid")
assert_eq(dt:to_unix_milliseconds(), 0)
assert_eq(dt:to_iso_string(), "")
end,
iso_string_ms = function()
for _, spec in pairs(specs_ms) do
local dt = c2.DateTime.from_iso_string(spec.iso_ms)
assert_eq(dt, c2.DateTime.from_iso_string(spec.iso_ms))
assert_eq(dt:to_iso_string(), tostring(dt))
assert_eq(dt:to_iso_string(), spec.iso_ms)
assert_eq(dt:to_iso_string_without_ms(), spec.iso_sec)
assert_eq(dt:to_unix_milliseconds(), spec.unix_ms)
assert_eq(dt:to_unix_seconds(), spec.unix_sec)
end
end,
iso_string_sec = function()
for _, spec in pairs(specs_sec) do
local dt = c2.DateTime.from_iso_string(spec.iso_sec)
assert_eq(dt, c2.DateTime.from_iso_string(spec.iso_ms))
assert_eq(dt, c2.DateTime.from_iso_string(spec.iso_sec))
assert_eq(dt:to_iso_string(), tostring(dt))
assert_eq(dt:to_iso_string(), spec.iso_ms)
assert_eq(dt:to_iso_string_without_ms(), spec.iso_sec)
assert_eq(dt:to_unix_milliseconds(), spec.unix_ms)
assert_eq(dt:to_unix_seconds(), spec.unix_sec)
end
end,
current_time = function()
local dt = c2.DateTime.current_local()
-- We don't know what it should be, but we know that it must be valid.
assert(dt:to_iso_string() ~= "")
dt = c2.DateTime.current_utc()
assert(dt:to_iso_string() ~= "")
end,
unix_timestamp_ms = function()
for _, spec in pairs(specs_ms) do
local dt = c2.DateTime.from_unix_milliseconds(spec.unix_ms)
assert_eq(dt, c2.DateTime.from_unix_milliseconds(spec.unix_ms))
assert_eq(dt:to_unix_milliseconds(), spec.unix_ms)
assert_eq(dt:to_unix_seconds(), spec.unix_sec)
-- FIXME: Check the ISO string here, but it's in the local time zone right now.
-- assert_eq(dt, c2.DateTime.from_iso_string(spec.iso_ms))
-- assert_eq(dt:to_iso_string(), spec.iso_ms)
-- assert_eq(dt:to_iso_string_without_ms(), spec.iso_sec)
end
end,
unix_timestamp_sec = function()
for _, spec in pairs(specs_sec) do
-- without milliseconds
local dt = c2.DateTime.from_unix_seconds(spec.unix_sec)
assert_eq(dt, c2.DateTime.from_unix_seconds(spec.unix_sec))
assert_eq(dt, c2.DateTime.from_unix_milliseconds(spec.unix_ms))
assert_eq(dt:to_unix_milliseconds(), spec.unix_ms)
assert_eq(dt:to_unix_seconds(), spec.unix_sec)
end
end,
}
for name, fn in pairs(tests) do
local ok, res = xpcall(fn, debug.traceback)
if not ok then
error(name .. " failed: " .. res)
end
end
+13
View File
@@ -1698,6 +1698,19 @@ TEST_F(PluginImageTest, NoPerms)
INSTANTIATE_TEST_SUITE_P(PluginImage, PluginImageTest,
testing::ValuesIn(discoverLuaTests("images")));
class PluginDateTimeTest : public PluginTest,
public ::testing::WithParamInterface<QString>
{
};
TEST_P(PluginDateTimeTest, Run)
{
this->configure();
runLuaTest("datetime", GetParam(), *this->lua);
}
INSTANTIATE_TEST_SUITE_P(PluginChannel, PluginDateTimeTest,
testing::ValuesIn(discoverLuaTests("datetime")));
// verify that all snapshots are included
TEST(PluginMessageConstructionTest, Integrity)
{