diff --git a/frontend/src/utils/__tests__/formatDateTimeToMinute.spec.ts b/frontend/src/utils/__tests__/formatDateTimeToMinute.spec.ts
new file mode 100644
index 0000000000..1e881adb18
--- /dev/null
+++ b/frontend/src/utils/__tests__/formatDateTimeToMinute.spec.ts
@@ -0,0 +1,15 @@
+import { describe, expect, it } from 'vitest'
+
+import { formatDateTimeToMinute } from '../format'
+
+describe('formatDateTimeToMinute', () => {
+ it('formats local date and time without seconds', () => {
+ const value = new Date(2026, 6, 19, 20, 30, 45)
+
+ expect(formatDateTimeToMinute(value, 'en-GB')).toBe('19/07/2026, 20:30')
+ })
+
+ it('returns an empty string for an invalid date', () => {
+ expect(formatDateTimeToMinute(new Date('invalid'), 'en-GB')).toBe('')
+ })
+})
diff --git a/frontend/src/utils/format.ts b/frontend/src/utils/format.ts
index 481fe397ef..787bd4b545 100644
--- a/frontend/src/utils/format.ts
+++ b/frontend/src/utils/format.ts
@@ -149,6 +149,27 @@ export function formatDateTime(
return formatDate(date, options, localeOverride)
}
+/**
+ * 格式化日期时间(精确到分钟)
+ */
+export function formatDateTimeToMinute(
+ date: string | Date | null | undefined,
+ localeOverride?: string
+): string {
+ return formatDate(
+ date,
+ {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false
+ },
+ localeOverride
+ )
+}
+
/**
* 格式化为 date 控件值(YYYY-MM-DD,使用本地时间)
*/
diff --git a/frontend/src/views/admin/SubscriptionsView.vue b/frontend/src/views/admin/SubscriptionsView.vue
index 3f6e73a8cd..85ae5f0a80 100644
--- a/frontend/src/views/admin/SubscriptionsView.vue
+++ b/frontend/src/views/admin/SubscriptionsView.vue
@@ -351,7 +351,7 @@
: 'text-gray-700 dark:text-gray-300'
"
>
- {{ formatDateOnly(value) }}
+ {{ formatDateTimeToMinute(value) }}
{{ getDaysRemaining(value) }} {{ t('admin.subscriptions.daysRemaining') }}
@@ -598,7 +598,7 @@
{{
extendingSubscription.expires_at
- ? formatDateOnly(extendingSubscription.expires_at)
+ ? formatDateTimeToMinute(extendingSubscription.expires_at)
: t('admin.subscriptions.noExpiration')
}}
@@ -764,7 +764,7 @@ import { adminAPI } from '@/api/admin'
import type { UserSubscription, Group, GroupPlatform, SubscriptionType } from '@/types'
import type { SimpleUser } from '@/api/admin/usage'
import type { Column } from '@/components/common/types'
-import { formatDateOnly } from '@/utils/format'
+import { formatDateTimeToMinute } from '@/utils/format'
import { getPersistedPageSize } from '@/composables/usePersistedPageSize'
import AppLayout from '@/components/layout/AppLayout.vue'
import TablePageLayout from '@/components/layout/TablePageLayout.vue'
diff --git a/frontend/src/views/user/SubscriptionsView.vue b/frontend/src/views/user/SubscriptionsView.vue
index b76f87582b..744529848a 100644
--- a/frontend/src/views/user/SubscriptionsView.vue
+++ b/frontend/src/views/user/SubscriptionsView.vue
@@ -256,7 +256,7 @@ import subscriptionsAPI from '@/api/subscriptions'
import type { UserSubscription } from '@/types'
import AppLayout from '@/components/layout/AppLayout.vue'
import Icon from '@/components/icons/Icon.vue'
-import { formatDateOnly } from '@/utils/format'
+import { formatDateTimeToMinute } from '@/utils/format'
import { hasPeakRate, formatPeakRateWindow, serverTimezoneLabel } from '@/utils/peak-rate'
import { platformBorderClass, platformBadgeClass, platformButtonClass, platformLabel } from '@/utils/platformColors'
import { getRemainingDurationParts, isOneTimeDailyQuota, type RemainingDurationParts } from '@/utils/subscriptionQuota'
@@ -322,7 +322,7 @@ function formatExpirationDate(expiresAt: string): string {
return t('userSubscriptions.status.expired')
}
- const dateStr = formatDateOnly(expires)
+ const dateStr = formatDateTimeToMinute(expires)
if (days === 0) {
return `${dateStr} (${t('common.today')})`