Support Discord @time

This commit is contained in:
Cadence Ember
2026-10-07 22:52:42 +13:00
parent 302427037b
commit c25b5a19c6
12 changed files with 383 additions and 10 deletions
+51
View File
@@ -0,0 +1,51 @@
// @ts-check
const assert = require("assert").strict
/*
https://docs.discord.com/developers/reference#message-formatting
Trying to make these formats fairly culture independent since the Matrix text will show up the same for everyone.
That means no ambiguous short dates like 03/04/26. The shortest allowable date is "medium", which spells the month.
It also means no 24-hour time, since this is ambiguous to 12-hour time users. Times must have am/pm.
Relative time is not supported for text conversion and will fall back to the default, "f".
See the test file for sample outputs.
*/
/** @type {Map<string, Intl.DateTimeFormatOptions>} discord style letter abbreviation -> Intl.DateTimeFormat constructor options */
const styles = new Map([
["t", {timeStyle: "short", timeZone: "UTC"}],
["T", {timeStyle: "medium", timeZone: "UTC"}],
["d", {dateStyle: "medium", timeZone: "UTC"}],
["D", {dateStyle: "long", timeZone: "UTC"}],
["f", {dateStyle: "long", timeStyle: "short", timeZone: "UTC"}],
["F", {dateStyle: "full", timeStyle: "short", timeZone: "UTC"}],
["s", {dateStyle: "medium", timeStyle: "short", timeZone: "UTC"}],
["S", {dateStyle: "medium", timeStyle: "medium", timeZone: "UTC"}]
])
const defaultStyle = "f"
/**
* @param {string | null | undefined} [style]
* @returns {Intl.DateTimeFormatOptions} */
function getFormatOptions(style) {
if (!style || !styles.has(style)) style = defaultStyle
const formatOptions = styles.get(style)
assert(formatOptions)
return formatOptions
}
/**
* @param {number} timestamp unix time in seconds
* @param {string | null | undefined} [style] discord style letter abbreviation, if present
* @returns {string}
*/
function convertDiscordTime(timestamp, style) {
const formatter = new Intl.DateTimeFormat("en-NZ", getFormatOptions(style))
const d = new Date(timestamp * 1000)
return formatter.format(d) + " (UTC)"
}
module.exports.getFormatOptions = getFormatOptions
module.exports.convertDiscordTime = convertDiscordTime
+48
View File
@@ -0,0 +1,48 @@
// @ts-check
const time = require("./time")
const {test} = require("supertape")
const d = new Date("2026-02-14T14:23:06.000Z")
const seconds = d.getTime() / 1000
test("time: default style", t => {
t.equal(time.convertDiscordTime(seconds), "14 February 2026 at 2:23 pm (UTC)")
})
test("time: t style", t => {
t.equal(time.convertDiscordTime(seconds, "t"), "2:23 pm (UTC)")
})
test("time: T style", t => {
t.equal(time.convertDiscordTime(seconds, "T"), "2:23:06 pm (UTC)")
})
test("time: d style", t => {
t.equal(time.convertDiscordTime(seconds, "d"), "14 Feb 2026 (UTC)")
})
test("time: D style", t => {
t.equal(time.convertDiscordTime(seconds, "D"), "14 February 2026 (UTC)")
})
test("time: f style", t => {
t.equal(time.convertDiscordTime(seconds, "f"), "14 February 2026 at 2:23 pm (UTC)")
})
test("time: F style", t => {
t.equal(time.convertDiscordTime(seconds, "F"), "Saturday, 14 February 2026 at 2:23 pm (UTC)")
})
test("time: s style", t => {
t.equal(time.convertDiscordTime(seconds, "s"), "14 Feb 2026, 2:23 pm (UTC)")
})
test("time: S style", t => {
t.equal(time.convertDiscordTime(seconds, "S"), "14 Feb 2026, 2:23:06 pm (UTC)")
})
test("time: R style", t => {
// Discord would display this as relative time, but I don't want to do that in a static message.
t.equal(time.convertDiscordTime(seconds, "R"), "14 February 2026 at 2:23 pm (UTC)")
})