Merge pull request #7499 from ouchihao/codex/bug-date-picker-midnight

fix(date-picker): refresh relative dates after midnight
This commit is contained in:
Wesley Liddick
2026-09-23 09:58:36 +08:00
committed by GitHub
2 changed files with 43 additions and 17 deletions
@@ -43,7 +43,7 @@
<input
type="date"
v-model="localStartDate"
:max="localEndDate || tomorrow"
:max="localEndDate || tomorrow()"
class="date-picker-input"
@change="onDateChange"
/>
@@ -57,7 +57,7 @@
type="date"
v-model="localEndDate"
:min="localStartDate"
:max="tomorrow"
:max="tomorrow()"
class="date-picker-input"
@change="onDateChange"
/>
@@ -108,22 +108,15 @@ const localStartDate = ref(props.startDate)
const localEndDate = ref(props.endDate)
const activePreset = ref<string | null>('last24Hours')
const today = computed(() => {
// Use local timezone to avoid UTC timezone issues
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
})
const today = () => formatDateToString(new Date())
// Tomorrow's date - used for max date to handle timezone differences
// When user is in a timezone behind the server, "today" on server might be "tomorrow" locally
const tomorrow = computed(() => {
const tomorrow = () => {
const d = new Date()
d.setDate(d.getDate() + 1)
return formatDateToString(d)
})
}
// Helper function to format date to YYYY-MM-DD using local timezone
const formatDateToString = (date: Date): string => {
@@ -138,7 +131,7 @@ const presets: DatePreset[] = [
labelKey: 'dates.today',
value: 'today',
getRange: () => {
const t = today.value
const t = today()
return { start: t, end: t }
}
},
@@ -168,7 +161,7 @@ const presets: DatePreset[] = [
labelKey: 'dates.last7Days',
value: '7days',
getRange: () => {
const end = today.value
const end = today()
const d = new Date()
d.setDate(d.getDate() - 6)
const start = formatDateToString(d)
@@ -179,7 +172,7 @@ const presets: DatePreset[] = [
labelKey: 'dates.last14Days',
value: '14days',
getRange: () => {
const end = today.value
const end = today()
const d = new Date()
d.setDate(d.getDate() - 13)
const start = formatDateToString(d)
@@ -190,7 +183,7 @@ const presets: DatePreset[] = [
labelKey: 'dates.last30Days',
value: '30days',
getRange: () => {
const end = today.value
const end = today()
const d = new Date()
d.setDate(d.getDate() - 29)
const start = formatDateToString(d)
@@ -203,7 +196,7 @@ const presets: DatePreset[] = [
getRange: () => {
const now = new Date()
const start = formatDateToString(new Date(now.getFullYear(), now.getMonth(), 1))
return { start, end: today.value }
return { start, end: today() }
}
},
{
@@ -0,0 +1,33 @@
import { enableAutoUnmount, mount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { ref } from 'vue'
import DateRangePicker from '../DateRangePicker.vue'
vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (key: string) => key, locale: ref('en') }) }))
enableAutoUnmount(afterEach)
beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 8, 30, 23, 59)) })
afterEach(() => vi.useRealTimers())
async function reopenNextDay() {
const w = mount(DateRangePicker, { props: { startDate: '2026-09-30', endDate: '2026-09-30' }, global: { stubs: { Icon: true } } })
await w.get('.date-picker-trigger').trigger('click')
expect(w.findAll('input[type="date"]')[1].attributes('max')).toBe('2026-10-01')
await w.get('.date-picker-trigger').trigger('click')
vi.setSystemTime(new Date(2026, 9, 1, 0, 1))
await w.get('.date-picker-trigger').trigger('click')
return w
}
describe('date presets after midnight', () => {
it.each([
['dates.today', '2026-10-01'],
['dates.last7Days', '2026-09-25'],
['dates.thisMonth', '2026-10-01'],
])('refreshes %s when the page stays mounted overnight', async (label, startDate) => {
const w = await reopenNextDay()
await w.findAll('.date-picker-preset').find(b => b.text() === label)!.trigger('click')
await w.get('.date-picker-apply').trigger('click')
expect(w.emitted('change')?.[0]?.[0]).toMatchObject({ startDate, endDate: '2026-10-01' })
})
it('updates the maximum selectable date when reopened', async () => {
const w = await reopenNextDay()
expect(w.findAll('input[type="date"]')[1].attributes('max')).toBe('2026-10-02')
})
})