Merge pull request #1080 from MaxTomahawk/feat/device-date-time

feat(mods): add shared local date and time formatting
This commit is contained in:
bryanthaboi
2026-08-11 21:21:51 -04:00
committed by GitHub
8 changed files with 265 additions and 1 deletions
+41
View File
@@ -0,0 +1,41 @@
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local DateTime = require("src.core.DateTime")
local SaveData = require("src.core.SaveData")
local stamp = os.time({ year = 2026, month = 8, day = 11, hour = 17, min = 5, sec = 0 })
local defaults = SaveData.defaultOptions()
T.eq(defaults.dateFormat, "device", "date format defaults to device locale")
T.eq(defaults.timeFormat, "device", "time format defaults to device locale")
local game = { save = { options = { dateFormat = "dmy", timeFormat = "24h" } } }
T.eq(DateTime.date(game, stamp), os.date("%d-%m-%Y", stamp),
"explicit DMY uses day-month-year")
T.eq(DateTime.time(game, stamp), os.date("%H:%M", stamp),
"explicit 24-hour time omits seconds")
T.eq(DateTime.dateTime(game, stamp),
os.date("%d-%m-%Y %H:%M", stamp),
"combined formatter composes exact date and time preferences")
game.save.options.dateFormat = "mdy"
T.eq(DateTime.date(game, stamp), os.date("%m-%d-%Y", stamp),
"explicit MDY is available")
game.save.options.dateFormat = "ymd"
T.eq(DateTime.date(game, stamp), os.date("%Y-%m-%d", stamp),
"explicit YMD is available")
game.save.options.timeFormat = "12h"
T.eq(DateTime.time(game, stamp), os.date("%I:%M %p", stamp),
"explicit 12-hour time is available")
local fallback = DateTime.formatWithLocale(stamp, "device", "device", "C")
T.eq(fallback.date, os.date("%d-%m-%Y", stamp),
"missing device locale falls back to requested DMY")
T.eq(fallback.time, os.date("%H:%M", stamp),
"missing device locale falls back to requested 24-hour time")
local invalid = DateTime.date({}, -1)
T.eq(invalid, "----", "invalid timestamps fail closed")
T.finish("date_time")
+18 -1
View File
@@ -288,7 +288,7 @@ local WANT_IDS = { "textSpeed", "animations", "battleStyle", "battleLayout",
"tilt", "gbcfx", "zoom", "voidFill", "videoMode",
"faithfulRes", "fpsCap",
"speedOverworld", "speedBattle", "speedMenu",
"mods", "controls" }
"mods", "controls", "dateFormat", "timeFormat" }
check(#om.rows == #WANT_IDS, "vanilla options row count (plus MODS/CONTROLS)")
for i, id in ipairs(WANT_IDS) do
check(om.rows[i].id == id, "options row order: " .. id)
@@ -408,6 +408,23 @@ check(bm.items[1].label == "UP" and bm.items[1].right == "UP/D-UP"
"with no rebind the rows mirror the fixed map, key and pad both (#589)")
check(cbGame.save.options.bindings == nil,
"opening the screen alone writes nothing")
-- shared date/time presentation stays in options.lua and is available to
-- engine UI and mods without becoming checkpoint progress
om.game.save.options.dateFormat = "device"
om.game.save.options.timeFormat = "device"
check(om.rows[26].value(om.game) == "DEVICE",
"DATE FORMAT defaults to device locale")
om.rows[26].step(om.game, 1)
check(om.game.save.options.dateFormat == "dmy"
and om.rows[26].value(om.game) == "DD-MM-YYYY",
"DATE FORMAT exposes deterministic DMY override")
check(om.rows[27].value(om.game) == "DEVICE",
"TIME FORMAT defaults to device locale")
om.rows[27].step(om.game, 1)
check(om.game.save.options.timeFormat == "24h"
and om.rows[27].value(om.game) == "24 HOUR",
"TIME FORMAT exposes deterministic 24-hour override")
check(bm.onKeyPressed == nil and bm.onGamepadPressed == nil,
"no raw-input claim until a capture is armed")
press(bm, "a")
+37
View File
@@ -0,0 +1,37 @@
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.modkit")
local FIXTURE = {
["mods/date_probe/manifest.json"] = [[{
"id": "date_probe",
"name": "Date Probe",
"version": "1.0.0",
"entry": "main.lua",
"api": 2
}]],
["mods/date_probe/main.lua"] = [[
local mod = ...
mod.exports.datetime = mod.datetime
]],
}
local run = T.sdk.loadMods({ "mods/date_probe" }, { fs = T.sdk.memfs(FIXTURE) })
T.eq(#run.errors, 0, "fixture mod loads cleanly")
local datetime = run.loader.exports.date_probe.datetime
T.eq(type(datetime), "table", "mod object exposes datetime formatting")
T.eq(type(datetime.date), "function", "public formatter exposes date")
T.eq(type(datetime.time), "function", "public formatter exposes time")
T.eq(type(datetime.dateTime), "function", "public formatter exposes date-time")
local stamp = os.time({ year = 2026, month = 8, day = 11, hour = 17, min = 5, sec = 0 })
local game = { save = { options = { dateFormat = "dmy", timeFormat = "24h" } } }
T.eq(datetime:date(game, stamp), os.date("%d-%m-%Y", stamp),
"mod date follows current global engine preference")
T.eq(datetime:time(game, stamp), os.date("%H:%M", stamp),
"mod time follows current global engine preference")
T.eq(datetime:dateTime(game, stamp), os.date("%d-%m-%Y %H:%M", stamp),
"mod date-time composes the same preference")
run.release()
T.finish("date_time")