diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd6e45..7f310b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ instead carried by the app description, the git tag, the release, and this file. which location, setup and size you used, and how it went. ### Changed +- Dates and times in the web interface follow the locale in the user's Nextcloud settings instead + of the browser's language, so a 24-hour locale shows 16:00 rather than 4:00 PM. - The list of backup runs (web interface and `occ backup:status`) shows the runs of the last 7 days, those still running, and at least the 5 newest; a failed run stays visible for that week. `occ backup:status --all` and "Show older runs" in the web interface list all of them. diff --git a/src/components/Backups.vue b/src/components/Backups.vue index 84c9087..bb500de 100644 --- a/src/components/Backups.vue +++ b/src/components/Backups.vue @@ -22,8 +22,8 @@

{{ t('ng_backup', 'Removed restore points can be brought back until they are deleted permanently.') }}

@@ -46,10 +46,10 @@ - {{ new Date(run.startedAt * 1000).toLocaleString() }} + {{ formatDateTime(run.startedAt) }} {{ targetName(run.targetId) }} {{ run.kind }} - {{ run.snapshot ? new Date(run.snapshot.createdAt * 1000).toLocaleString() + (run.snapshot.label ? ' – ' + run.snapshot.label : '') : '' }} + {{ run.snapshot ? formatDateTime(run.snapshot.createdAt) + (run.snapshot.label ? ' – ' + run.snapshot.label : '') : '' }} {{ run.status }}{{ run.status === 'running' ? ' (' + run.phase + percent(run) + ')' : '' }}{{ run.error ? ': ' + run.error : '' }} {{ run.stats?.files?.files ?? run.stats?.restored ?? '' }} {{ run.stats?.files ? formatSize(run.stats.files.bytes) : '' }} @@ -79,7 +79,7 @@ import NcNoteCard from '@nextcloud/vue/components/NcNoteCard' import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection' import NcTextField from '@nextcloud/vue/components/NcTextField' import api from '../api.js' -import { formatSize } from '../format.js' +import { formatDateTime, formatDate, formatSize } from '../format.js' import { confirmPassword } from '@nextcloud/password-confirmation' import { reactive } from 'vue' diff --git a/src/components/KeySetup.vue b/src/components/KeySetup.vue index abeec2e..24ab60f 100644 --- a/src/components/KeySetup.vue +++ b/src/components/KeySetup.vue @@ -35,7 +35,7 @@ {{ t('ng_backup', 'Slot {n}', { n: slot.slot }) }} {{ slot.label }} - {{ slot.created ? new Date(slot.created * 1000).toLocaleDateString() : '' }} + {{ slot.created ? formatDate(slot.created) : '' }} {{ t('ng_backup', 'New passphrase') }} {{ t('ng_backup', 'Remove') }} @@ -69,7 +69,7 @@ {{ status.confirmationPhrase }}

- {{ t('ng_backup', 'Recovery kit confirmed by {user} on {date}.', { user: status.key.confirmation.uid, date: new Date(status.key.confirmation.time * 1000).toLocaleString() }) }} + {{ t('ng_backup', 'Recovery kit confirmed by {user} on {date}.', { user: status.key.confirmation.uid, date: formatDateTime(status.key.confirmation.time) }) }} {{ t('ng_backup', 'Download the kit again') }} {{ t('ng_backup', 'Requires your password; all administrators are notified.') }}

@@ -90,6 +90,7 @@ import NcPasswordField from '@nextcloud/vue/components/NcPasswordField' import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection' import NcTextField from '@nextcloud/vue/components/NcTextField' import api from '../api.js' +import { formatDateTime, formatDate } from '../format.js' const props = defineProps({ status: { type: Object, required: true } }) const emit = defineEmits(['changed']) diff --git a/src/components/RestoreBrowser.vue b/src/components/RestoreBrowser.vue index cd89e77..6a2d612 100644 --- a/src/components/RestoreBrowser.vue +++ b/src/components/RestoreBrowser.vue @@ -52,7 +52,7 @@ import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection' import { confirmPassword } from '@nextcloud/password-confirmation' import '@nextcloud/password-confirmation/style.css' import api from '../api.js' -import { formatSize } from '../format.js' +import { formatDateTime, formatSize } from '../format.js' const props = defineProps({ status: { type: Object, required: true } }) const emit = defineEmits(['changed']) @@ -74,7 +74,7 @@ const modes = [ { id: 'replace', label: t('ng_backup', 'Into the original place, overwrite and move files that are not in the backup to the trash') }, ] -const snapshotLabel = (s) => new Date(s.createdAt * 1000).toLocaleString() + (s.label ? ' – ' + s.label : '') +const snapshotLabel = (s) => formatDateTime(s.createdAt) + (s.label ? ' – ' + s.label : '') const join = (name) => path.value + '/' + name const restorable = (p) => /^data\/[^/]+\/files(\/.*)?$/.test(p) const crumbs = computed(() => { diff --git a/src/format.js b/src/format.js index 222a1c4..add6404 100644 --- a/src/format.js +++ b/src/format.js @@ -1,5 +1,18 @@ // SPDX-FileCopyrightText: 2026 Dillard Blom // SPDX-License-Identifier: AGPL-3.0-or-later +import { getCanonicalLocale } from '@nextcloud/l10n' + +// The locale of the user's Nextcloud settings (Personal info > Locale), not the browser's: +// a browser set to English would otherwise show 4:00 PM to someone with a 24-hour locale. +const locale = getCanonicalLocale() + +// A number is a unix timestamp in seconds, a string an ISO 8601 date (snapshot times). +const toDate = (value) => typeof value === 'number' ? new Date(value * 1000) : new Date(value) + +export const formatDateTime = (value) => toDate(value).toLocaleString(locale) + +export const formatDate = (value) => toDate(value).toLocaleDateString(locale) + export function formatSize(bytes) { if (bytes === null || bytes === undefined) { return ''