Skip to content

Commit

Permalink
Use JS-native date/time formatting (#4488)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwerty287 authored Nov 30, 2024
1 parent 7309ba0 commit 9a8d7d8
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 100 deletions.
1 change: 0 additions & 1 deletion web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ node_modules
dist
dist-ssr
*.local
src/assets/dayjsLocales
1 change: 0 additions & 1 deletion web/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ coverage/
LICENSE
components.d.ts
src/assets/locales/*.json
src/assets/dayjsLocales/
!src/assets/locales/en.json
1 change: 0 additions & 1 deletion web/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ export default antfu(
'tsconfig.json',
'src/assets/locales/**/*',
'!src/assets/locales/en.json',
'src/assets/dayjsLocales/',
'components.d.ts',
],
},
Expand Down
2 changes: 0 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@mdi/js": "^7.4.47",
"@vueuse/core": "^12.0.0",
"ansi_up": "^6.0.2",
"dayjs": "^1.11.12",
"dompurify": "^3.2.0",
"fuse.js": "^7.0.0",
"js-base64": "^3.7.7",
Expand Down Expand Up @@ -57,7 +56,6 @@
"eslint-plugin-vue-scoped-css": "^2.8.1",
"jsdom": "^25.0.0",
"prettier": "^3.3.3",
"replace-in-file": "^8.1.0",
"tinycolor2": "^1.6.0",
"typescript": "5.6.3",
"vite": "^5.4.1",
Expand Down
28 changes: 0 additions & 28 deletions web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions web/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"not_found": "Server could not find requested object"
},
"time": {
"template": "MMM D, YYYY, HH:mm z",
"not_started": "not started yet"
"not_started": "not started yet",
"just_now": "just now"
},
"repo": {
"manual_pipeline": {
Expand Down
100 changes: 75 additions & 25 deletions web/src/compositions/useDate.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,94 @@
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { useI18n } from 'vue-i18n';

dayjs.extend(timezone);
dayjs.extend(utc);
dayjs.extend(advancedFormat);
dayjs.extend(relativeTime);
dayjs.extend(duration);
let currentLocale = 'en';

function splitDuration(durationMs: number) {
const totalSeconds = durationMs / 1000;
const totalMinutes = totalSeconds / 60;
const totalHours = totalMinutes / 60;

const seconds = Math.floor(totalSeconds) % 60;
const minutes = Math.floor(totalMinutes) % 60;
const hours = Math.floor(totalHours) % 24;

return {
seconds,
minutes,
hours,
totalHours,
totalMinutes,
totalSeconds,
};
}

function toLocaleString(date: Date) {
return dayjs(date).format(useI18n().t('time.template'));
return date.toLocaleString(currentLocale, {
dateStyle: 'short',
timeStyle: 'short',
});
}

function timeAgo(date: Date | string | number) {
return dayjs().to(dayjs(date));
function timeAgo(date: number) {
const seconds = Math.floor((new Date().getTime() - date) / 1000);

const formatter = new Intl.RelativeTimeFormat(currentLocale);

let interval = seconds / 31536000;
if (interval > 1) {
return formatter.format(-Math.round(interval), 'year');
}
interval = seconds / 2592000;
if (interval > 1) {
return formatter.format(-Math.round(interval), 'month');
}
interval = seconds / 86400;
if (interval > 1) {
return formatter.format(-Math.round(interval), 'day');
}
interval = seconds / 3600;
if (interval > 1) {
return formatter.format(-Math.round(interval), 'hour');
}
interval = seconds / 60;
if (interval > 0.5) {
return formatter.format(-Math.round(interval), 'minute');
}
return useI18n().t('time.just_now');
}

function prettyDuration(durationMs: number) {
return dayjs.duration(durationMs).humanize();
const t = splitDuration(durationMs);

if (t.totalHours > 1) {
return Intl.NumberFormat(currentLocale, { style: 'unit', unit: 'hour', unitDisplay: 'long' }).format(
Math.round(t.totalHours),
);
}
if (t.totalMinutes > 1) {
return Intl.NumberFormat(currentLocale, { style: 'unit', unit: 'minute', unitDisplay: 'long' }).format(
Math.round(t.totalMinutes),
);
}
return Intl.NumberFormat(currentLocale, { style: 'unit', unit: 'second', unitDisplay: 'long' }).format(
Math.round(t.totalSeconds),
);
}

function durationAsNumber(durationMs: number): string {
const dur = dayjs.duration(durationMs);
return dur.format(dur.hours() > 1 ? 'HH:mm:ss' : 'mm:ss');
const { seconds, minutes, hours } = splitDuration(durationMs);

const minSecFormat = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;

if (hours > 0) {
return `${hours.toString().padStart(2, '0')}:${minSecFormat}`;
}

return minSecFormat;
}

export function useDate() {
const addedLocales = ['en'];

async function setDayjsLocale(locale: string) {
if (!addedLocales.includes(locale)) {
const l = (await import(`~/assets/dayjsLocales/${locale}.js`)) as { default: string };
dayjs.locale(l.default);
} else {
dayjs.locale(locale);
}
currentLocale = locale;
}

return {
Expand Down
41 changes: 1 addition & 40 deletions web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { copyFile, existsSync, mkdirSync, readdirSync } from 'node:fs';
import { readdirSync } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import vue from '@vitejs/plugin-vue';
import { replaceInFileSync } from 'replace-in-file';
import type { Plugin } from 'vite';
import prismjs from 'vite-plugin-prismjs';
import WindiCSS from 'vite-plugin-windicss';
Expand Down Expand Up @@ -54,44 +53,6 @@ export default defineConfig({

const filenames = readdirSync('src/assets/locales/').map((filename) => filename.replace('.json', ''));

if (!existsSync('src/assets/dayjsLocales')) {
mkdirSync('src/assets/dayjsLocales');
}

filenames.forEach((name) => {
// English is always directly loaded (compiled by Vite) and thus not copied
if (name === 'en') {
return;
}
let langName = name;

// copy dayjs language
if (name === 'zh-Hans') {
// zh-Hans is called zh in dayjs
langName = 'zh';
} else if (name === 'zh-Hant') {
// zh-Hant is called zh-cn in dayjs
langName = 'zh-cn';
}

copyFile(
`node_modules/dayjs/esm/locale/${langName}.js`,
`src/assets/dayjsLocales/${name}.js`,
// eslint-disable-next-line promise/prefer-await-to-callbacks
(err) => {
if (err) {
throw err;
}
},
);
});
replaceInFileSync({
files: 'src/assets/dayjsLocales/*.js',
// remove any dayjs import and any dayjs.locale call
from: /(?:import dayjs.*'|dayjs\.locale.*);/g,
to: '',
});

return {
name: 'vue-i18n-supported-locales',

Expand Down

0 comments on commit 9a8d7d8

Please sign in to comment.