diff --git a/frontend/components/Form/DatePicker.vue b/frontend/components/Form/DatePicker.vue
index 725a6074..7422b603 100644
--- a/frontend/components/Form/DatePicker.vue
+++ b/frontend/components/Form/DatePicker.vue
@@ -3,13 +3,27 @@
-
+
-
+
@@ -38,6 +52,8 @@
const isDark = useIsDark();
+ const formatDate = (date: Date | string | number) => fmtDate(date, "human", "date");
+
const selected = computed({
get() {
// String
diff --git a/frontend/components/global/DateTime.vue b/frontend/components/global/DateTime.vue
index b23cac2b..b82cf5c6 100644
--- a/frontend/components/global/DateTime.vue
+++ b/frontend/components/global/DateTime.vue
@@ -22,6 +22,6 @@
return "";
}
- return fmtDate(props.date, props.format);
+ return fmtDate(props.date, props.format, props.datetimeType);
});
diff --git a/frontend/composables/use-formatters.ts b/frontend/composables/use-formatters.ts
index 602a7108..91cf9bf1 100644
--- a/frontend/composables/use-formatters.ts
+++ b/frontend/composables/use-formatters.ts
@@ -1,5 +1,6 @@
-import { useI18n } from "vue-i18n";
-import { type UseTimeAgoMessages, type UseTimeAgoUnitNamesDefault } from "@vueuse/core";
+import { format, formatDistance } from "date-fns";
+/* eslint import/namespace: ['error', { allowComputed: true }] */
+import * as Locales from "date-fns/locale";
const cache = {
currency: "",
@@ -20,105 +21,63 @@ export async function useFormatCurrency() {
}
}
- return (value: number | string) => fmtCurrency(value, cache.currency);
+ return (value: number | string) => fmtCurrency(value, cache.currency, getLocaleCode());
}
export type DateTimeFormat = "relative" | "long" | "short" | "human";
export type DateTimeType = "date" | "time" | "datetime";
-function ordinalIndicator(num: number) {
- if (num > 3 && num < 21) return "th";
- switch (num % 10) {
- case 1:
- return "st";
- case 2:
- return "nd";
- case 3:
- return "rd";
- default:
- return "th";
- }
+export function getLocaleCode() {
+ const { $i18nGlobal } = useNuxtApp();
+ return ($i18nGlobal?.locale?.value as string) ?? "en-US";
}
-export function useLocaleTimeAgo(date: Date) {
- const { t } = useI18n();
-
- const I18N_MESSAGES: UseTimeAgoMessages = {
- justNow: t("components.global.date_time.just-now"),
- past: n => (n.match(/\d/) ? t("components.global.date_time.ago", [n]) : n),
- future: n => (n.match(/\d/) ? t("components.global.date_time.in", [n]) : n),
- month: (n, past) =>
- n === 1
- ? past
- ? t("components.global.date_time.last-month")
- : t("components.global.date_time.next-month")
- : `${n} ${t(`components.global.date_time.months`)}`,
- year: (n, past) =>
- n === 1
- ? past
- ? t("components.global.date_time.last-year")
- : t("components.global.date_time.next-year")
- : `${n} ${t(`components.global.date_time.years`)}`,
- day: (n, past) =>
- n === 1
- ? past
- ? t("components.global.date_time.yesterday")
- : t("components.global.date_time.tomorrow")
- : `${n} ${t(`components.global.date_time.days`)}`,
- week: (n, past) =>
- n === 1
- ? past
- ? t("components.global.date_time.last-week")
- : t("components.global.date_time.next-week")
- : `${n} ${t(`components.global.date_time.weeks`)}`,
- hour: n => `${n} ${n === 1 ? t("components.global.date_time.hour") : t("components.global.date_time.hours")}`,
- minute: n => `${n} ${n === 1 ? t("components.global.date_time.minute") : t("components.global.date_time.minutes")}`,
- second: n => `${n} ${n === 1 ? t("components.global.date_time.second") : t("components.global.date_time.seconds")}`,
- invalid: "",
- };
-
- return useTimeAgo(date, {
- fullDateFormatter: (date: Date) => date.toLocaleDateString(),
- messages: I18N_MESSAGES,
- });
+function getLocaleForDate() {
+ const localeCode = getLocaleCode();
+ const lang = localeCode.length > 1 ? localeCode.substring(0, 2) : localeCode;
+ const region = localeCode.length > 2 ? localeCode.substring(3) : "";
+ return Locales[(lang + region) as keyof typeof Locales] ?? Locales[lang as keyof typeof Locales] ?? Locales.enUS;
}
-export function fmtDate(value: string | Date, fmt: DateTimeFormat = "human"): string {
- const months = [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December",
- ];
+export function fmtDate(
+ value: string | Date | number,
+ fmt: DateTimeFormat = "human",
+ type: DateTimeType = "date"
+): string {
+ const dt = typeof value === "string" || typeof value === "number" ? new Date(value) : value;
- const dt = typeof value === "string" ? new Date(value) : value;
- if (!dt) {
+ if (!dt || !validDate(dt)) {
return "";
}
- if (!validDate(dt)) {
- return "";
+ const localeOptions = { locale: getLocaleForDate() };
+
+ if (fmt === "relative") {
+ return `${formatDistance(dt, new Date(), { ...localeOptions, addSuffix: true })} (${fmtDate(dt, "short", "date")})`;
}
+ if (type === "time") {
+ return format(dt, "p", localeOptions);
+ }
+
+ let formatStr = "";
+
switch (fmt) {
- case "relative":
- return useLocaleTimeAgo(dt).value + useDateFormat(dt, " (YYYY-MM-DD)").value;
- case "long":
- return useDateFormat(dt, "YYYY-MM-DD (dddd)").value;
- case "short":
- return useDateFormat(dt, "YYYY-MM-DD").value;
case "human":
- // January 1st, 2021
- return `${months[dt.getMonth()]} ${dt.getDate()}${ordinalIndicator(dt.getDate())}, ${dt.getFullYear()}`;
+ formatStr = "PPP";
+ break;
+ case "long":
+ formatStr = "PP";
+ break;
+ case "short":
+ formatStr = "P";
+ break;
default:
return "";
}
+ if (type === "datetime") {
+ formatStr += "p";
+ }
+
+ return format(dt, formatStr, localeOptions);
}
diff --git a/frontend/lib/datelib/datelib.ts b/frontend/lib/datelib/datelib.ts
index c70dbf9e..b87922c1 100644
--- a/frontend/lib/datelib/datelib.ts
+++ b/frontend/lib/datelib/datelib.ts
@@ -11,7 +11,9 @@ export function format(date: Date | string): string {
}
export function zeroTime(date: Date): Date {
- return new Date(date.getFullYear(), date.getMonth(), date.getDate());
+ return new Date(
+ new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - date.getTimezoneOffset() * 60000
+ );
}
export function factorRange(offset: number = 7): [Date, Date] {
diff --git a/frontend/pages/item/[id]/index/edit.vue b/frontend/pages/item/[id]/index/edit.vue
index a4c0b602..b86c9dc3 100644
--- a/frontend/pages/item/[id]/index/edit.vue
+++ b/frontend/pages/item/[id]/index/edit.vue
@@ -99,16 +99,12 @@
let purchasePrice = 0;
let soldPrice = 0;
- let purchaseTime = null;
if (item.value.purchasePrice) {
purchasePrice = item.value.purchasePrice;
}
if (item.value.soldPrice) {
soldPrice = item.value.soldPrice;
}
- if (item.value.purchaseTime && typeof item.value.purchaseTime !== "string") {
- purchaseTime = new Date(item.value.purchaseTime.getTime() - item.value.purchaseTime.getTimezoneOffset() * 60000);
- }
console.log((item.value.purchasePrice ??= 0));
console.log((item.value.soldPrice ??= 0));
@@ -121,7 +117,7 @@
assetId: item.value.assetId,
purchasePrice,
soldPrice,
- purchaseTime: purchaseTime as Date,
+ purchaseTime: item.value.purchaseTime as Date,
};
const { error } = await api.items.update(itemId.value, payload);
diff --git a/frontend/pages/profile.vue b/frontend/pages/profile.vue
index d13301b3..37eb503d 100644
--- a/frontend/pages/profile.vue
+++ b/frontend/pages/profile.vue
@@ -8,6 +8,7 @@
import MdiFill from "~icons/mdi/fill";
import MdiPencil from "~icons/mdi/pencil";
import MdiAccountMultiple from "~icons/mdi/account-multiple";
+ import { getLocaleCode } from "~/composables/use-formatters";
definePageMeta({
middleware: ["auth"],
@@ -52,12 +53,11 @@
});
const currencyExample = computed(() => {
- const formatter = new Intl.NumberFormat("en-US", {
- style: "currency",
- currency: currency.value ? currency.value.code : "USD",
- });
+ return fmtCurrency(1000, currency.value?.code ?? "USD", getLocaleCode());
+ });
- return formatter.format(1000);
+ const dateExample = computed(() => {
+ return fmtDate(new Date(Date.now() - 15 * 60000), "relative");
});
const { data: group } = useAsyncData(async () => {
@@ -389,6 +389,7 @@
{{ $t(`languages.${lang}`) }} ({{ $t(`languages.${lang}`, 1, { locale: lang }) }})
+ {{ $t("profile.example") }}: {{ $t("global.created") }} {{ dateExample }}
diff --git a/frontend/plugins/i18n.ts b/frontend/plugins/i18n.ts
index f6994dcb..f563fd13 100644
--- a/frontend/plugins/i18n.ts
+++ b/frontend/plugins/i18n.ts
@@ -30,6 +30,12 @@ export default defineNuxtPlugin(({ vueApp }) => {
messages: messages(),
});
vueApp.use(i18n);
+
+ return {
+ provide: {
+ i18nGlobal: i18n.global,
+ },
+ };
});
export const messages = () => {