feat: more readable time delta #193
5 changed files with 74 additions and 20 deletions
|
@ -1,4 +1,10 @@
|
|||
import { nextTimeUnit, showDate, strToSeconds, TimeSecond } from "../../utils/time";
|
||||
import {
|
||||
nextTimeUnit,
|
||||
showDate,
|
||||
strToSeconds,
|
||||
timeDeltaToString,
|
||||
TimeSecond,
|
||||
} from "../../utils/time";
|
||||
|
||||
describe("Date with correct timezone", () => {
|
||||
const map = new Map([["u_time_at", "@"]]);
|
||||
|
@ -84,3 +90,25 @@ describe("Next time unit", () => {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("Relative time", () => {
|
||||
// Thoses tests are based on time, we have 10s of acceptance.
|
||||
{
|
||||
const name = Date.now() + (10 * TimeSecond.Minute + 30) * 1000;
|
||||
test(name.toString(), () => {
|
||||
expect(timeDeltaToString(name)).toMatch(/10m 30s|10m 2\ds/);
|
||||
});
|
||||
}
|
||||
{
|
||||
const name = Date.now() + (12 * TimeSecond.Hour + 30 * TimeSecond.Minute) * 1000;
|
||||
test(name.toString(), () => {
|
||||
expect(timeDeltaToString(name)).toMatch(/12h 30m|12h 29m 5\ds/);
|
||||
});
|
||||
}
|
||||
{
|
||||
const name = Date.now() + (TimeSecond.Week + TimeSecond.Day + 6 * TimeSecond.Hour) * 1000;
|
||||
test(name.toString(), () => {
|
||||
expect(timeDeltaToString(name)).toMatch(/1w 1d 6h|1w 1d 5h 59m 5\ds/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -131,3 +131,8 @@ export const emojiPng = (emoji: string) =>
|
|||
`https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/72x72/${emoji
|
||||
.codePointAt(0)
|
||||
?.toString(16)}.png`;
|
||||
|
||||
/**
|
||||
* Blank character
|
||||
*/
|
||||
export const blank = "\u200b";
|
||||
|
|
|
@ -2,6 +2,7 @@ import { EmbedBuilder } from "@discordjs/builders";
|
|||
import { GuildQueue, QueueRepeatMode } from "discord-player";
|
||||
import { Client } from "discord.js";
|
||||
import { getLocale } from "./locales";
|
||||
import { blank } from "./misc";
|
||||
|
||||
export const embedListQueue = (
|
||||
client: Client,
|
||||
|
@ -30,7 +31,7 @@ export const embedListQueue = (
|
|||
? loc.get("c_queue10")
|
||||
: (idx === 1 && page === 1) || (idx === 0 && page > 1)
|
||||
? loc.get("c_queue11")
|
||||
: "\u200b";
|
||||
: blank;
|
||||
const idx_track = now_playing ? "" : `${idx + limit_fields * (page - 1)}. `;
|
||||
embed.addFields({
|
||||
name,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Client, Colors, EmbedBuilder, User } from "discord.js";
|
||||
import { getLocale } from "./locales";
|
||||
import { cleanCodeBlock } from "./misc";
|
||||
import { blank, cleanCodeBlock } from "./misc";
|
||||
import { showDate, strToSeconds, timeDeltaToString } from "./time";
|
||||
import { RegexC, RegExpFlags } from "./regex";
|
||||
|
||||
|
@ -88,6 +88,8 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
|
|||
|
||||
const timeoutId = setTimeoutReminder(client, info, data.option, timeout);
|
||||
|
||||
const expiration_date = info.createdAt + timeout * 1000;
|
||||
|
||||
// Add the remind to the db
|
||||
client.db.run(
|
||||
"INSERT INTO reminder ( \
|
||||
|
@ -95,7 +97,7 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
|
|||
) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );",
|
||||
[
|
||||
info.message,
|
||||
`${info.createdAt + timeout * 1000}`,
|
||||
`${expiration_date}`,
|
||||
data.option.valueOf(),
|
||||
info.channelId,
|
||||
`${info.createdAt}`,
|
||||
|
@ -110,7 +112,7 @@ export const newReminder = async (client: Client, time: string, info: infoRemind
|
|||
}
|
||||
|
||||
// Send confirmation to user
|
||||
ok(`${loc.get("c_reminder1")} ${data.time}.`);
|
||||
ok(`${loc.get("c_reminder1")} ${timeDeltaToString(expiration_date)}.`);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -440,7 +442,7 @@ export const embedListReminders = async (
|
|||
});
|
||||
} else {
|
||||
embed.addFields({
|
||||
name: "\u200b",
|
||||
name: blank,
|
||||
value: `${loc.get("c_reminder10")}${page} ${loc.get("c_reminder11")}.`,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@ import { RegexC, RegExpFlags } from "./regex";
|
|||
|
||||
/**
|
||||
* Parsed string adapted with TZ (locales) and format for the specified lang
|
||||
* @param tz Lang
|
||||
* @param locale Locales
|
||||
* @param lang Locale
|
||||
* @param translation Translation for "at"
|
||||
* @param date Date
|
||||
* @returns String
|
||||
*/
|
||||
export const showDate = (tz: string, locale: Map<string, unknown>, date: Date) => {
|
||||
const localeInfo = new Intl.Locale(tz);
|
||||
export const showDate = (lang: string, translation: Map<string, unknown>, date: Date) => {
|
||||
const localeInfo = new Intl.Locale(lang);
|
||||
const intlTimezone = moment.tz.zonesForCountry(localeInfo.region ?? localeInfo.baseName);
|
||||
const formattedDate = new Intl.DateTimeFormat(tz, {
|
||||
const formattedDate = new Intl.DateTimeFormat(lang, {
|
||||
timeZone: intlTimezone ? intlTimezone[0] : "Factory",
|
||||
dateStyle: "short",
|
||||
timeStyle: "medium",
|
||||
|
@ -19,14 +19,14 @@ export const showDate = (tz: string, locale: Map<string, unknown>, date: Date) =
|
|||
.format(date)
|
||||
.split(" ");
|
||||
|
||||
return `${formattedDate[0]} ${locale.get("u_time_at")} ${formattedDate[1]}`;
|
||||
return `${formattedDate[0]} ${translation.get("u_time_at")} ${formattedDate[1]}`;
|
||||
};
|
||||
|
||||
export enum TimeSecond {
|
||||
Year = 31536000,
|
||||
Week = 604800,
|
||||
Day = 86400,
|
||||
Hour = 3600,
|
||||
Year = 60 * 60 * 24 * 365,
|
||||
Week = 60 * 60 * 24 * 7,
|
||||
Day = 60 * 60 * 24,
|
||||
Hour = 60 * 60,
|
||||
Minute = 60,
|
||||
Second = 1,
|
||||
}
|
||||
|
@ -95,13 +95,31 @@ export const strToSeconds = (time: string) => {
|
|||
|
||||
/**
|
||||
* Calculating the difference between a date and now
|
||||
* @param lang Locale
|
||||
* @param time Time
|
||||
* @returns Delta between the time and now
|
||||
*/
|
||||
export const timeDeltaToString = (time: number) => {
|
||||
const now = Date.now();
|
||||
// TODO: adapt the output and not always parse the time as seconds
|
||||
// https://git.mylloon.fr/ConfrerieDuKassoulait/Botanique/issues/189
|
||||
// Use Intl.RelativeTimeFormat ?
|
||||
return `${strToSeconds(`${(now - time) / 1000}`)} secs`;
|
||||
let secondsDifference = Math.abs(Math.ceil((time - now) / 1000) - 2);
|
||||
|
||||
if (secondsDifference === 0) {
|
||||
return "0s";
|
||||
}
|
||||
|
||||
return Object.entries(TimeSecond)
|
||||
.map(([key, value]) => ({
|
||||
label: key.charAt(0).toLowerCase(),
|
||||
value: value as TimeSecond,
|
||||
}))
|
||||
.map(({ label, value }) => {
|
||||
if (secondsDifference >= value) {
|
||||
const amount = Math.floor(secondsDifference / value);
|
||||
secondsDifference -= amount * value;
|
||||
return `${amount}${label}`;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue