- | {{ 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 ''
|