Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions packages/docs/src/pages/en/getting-started/upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,54 @@ export default createVuetify({

Theme colors now support transparency. `rgb(var(--v-theme-color))` will continue to work the same as before, but `rgba(var(--v-theme-color), 0.8)` should be changed to either `color-mix(in srgb, rgb(var(--v-theme-color)) 80%, transparent)` or `rgb(from rgb(var(--v-theme-color)) / 0.8)` when used with a transparent theme color.

## Date

The date system now uses `@vuetify/v0` under the hood. The consumer API (`useDate()`) is unchanged for most users.

### Default date type changed

The default adapter now uses `Temporal.PlainDateTime` instead of native `Date`. If your code handles date values from VDatePicker or VCalendar model values, update accordingly:

```diff
- const year = modelValue.getFullYear()
+ const year = modelValue.year
```

### Locale-derived week settings removed

The default adapter no longer automatically derives `firstDayOfWeek` from the locale. All locales default to Sunday (0). Pass `firstDayOfWeek` explicitly to week-related methods if you need locale-specific behavior:

```ts
adapter.startOfWeek(date, 1) // Monday
adapter.getWeekArray(date, 1)
```

### Format differences

- `toISO()` now returns `"YYYY-MM-DDTHH:mm:ss"` (includes time) instead of `"YYYY-MM-DD"`
- `getWeekdays()` defaults to `'short'` format (`Sun`, `Mon`) instead of `'narrow'` (`S`, `M`)
- Time formats include seconds (`13:00:00` instead of `13:00`)

### Custom adapter migration

Custom adapters must implement `@vuetify/v0`'s `DateAdapter<T>` interface. Old adapters are auto-detected and wrapped with a deprecation warning. Key signature changes:

- `getWeek(date, firstDayOfWeek?, minimalDays?)` — third parameter is now `minimalDays` (minimum days in first week) instead of `firstDayOfYear`
- Several new methods required: `parse`, `isNull`, `formatByString`, `getFormatHelperText`, `formatNumber`, `getMeridiemText`, and others

### Third-party adapters

Date library adapters (luxon, dayjs, date-fns) will be shipped by `@vuetify/v0`:

```diff
- import { VuetifyLuxonAdapter } from 'vuetify/date/adapters/luxon'
+ import { LuxonDateAdapter } from '@vuetify/v0/date'
```

### Direct symbol injection deprecated

Use `useDate()` instead of injecting `DateAdapterSymbol` directly.

## Components

### VBtn display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,9 @@ describe('week numbers with time zone', () => {
})

describe('range selection with time zone', () => {
// Temporal.PlainDateTime is timezone-agnostic, so TZ stubbing
// does not affect date calculations. Tests verify correct dates
// are selected regardless of system timezone.
beforeEach(() => vi.stubEnv('TZ', 'America/New_York'))
afterEach(() => vi.unstubAllEnvs())

Expand All @@ -815,10 +818,20 @@ describe('range selection with time zone', () => {

const btn1 = await wrapper.findByText('8') as HTMLElement
btn1.click()
expect(update).toHaveBeenNthCalledWith(1, [new Date('2025-03-08T05:00:00.000Z')])
expect(update).toHaveBeenCalledTimes(1)
const first = update.mock.calls[0][0]
expect(first).toHaveLength(1)
expect(first[0].day).toBe(8)
expect(first[0].month).toBe(3)
expect(first[0].year).toBe(2025)

const btn2 = await wrapper.findByText('9') as HTMLElement
btn2.click()
expect(update).toHaveBeenNthCalledWith(2, [new Date('2025-03-08T05:00:00.000Z'), new Date('2025-03-10T03:59:59.999Z')])
expect(update).toHaveBeenCalledTimes(2)
const second = update.mock.calls[1][0]
expect(second).toHaveLength(2)
expect(second[0].day).toBe(8)
expect(second[1].day).toBe(9)
})

it('should select correct dates near DST/ST transition', async () => {
Expand All @@ -833,9 +846,19 @@ describe('range selection with time zone', () => {

const btn1 = await wrapper.findByText('2') as HTMLElement
btn1.click()
expect(update).toHaveBeenNthCalledWith(1, [new Date('2025-10-02T04:00:00.000Z')])
expect(update).toHaveBeenCalledTimes(1)
const first = update.mock.calls[0][0]
expect(first).toHaveLength(1)
expect(first[0].day).toBe(2)
expect(first[0].month).toBe(10)
expect(first[0].year).toBe(2025)

const btn2 = await wrapper.findByText('3') as HTMLElement
btn2.click()
expect(update).toHaveBeenNthCalledWith(2, [new Date('2025-10-02T04:00:00.000Z'), new Date('2025-10-04T03:59:59.999Z')])
expect(update).toHaveBeenCalledTimes(2)
const second = update.mock.calls[1][0]
expect(second).toHaveLength(2)
expect(second[0].day).toBe(2)
expect(second[1].day).toBe(3)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ describe('VDatePicker', () => {
// Expect only 2 dates (first and last) to be selected
await expect.poll(() => model.value).toHaveLength(2)

// Verify the correct dates are selected (10th and 20th)
const dates = model.value as Date[]
// Model emits Temporal.PlainDateTime (v0 adapter)
const dates = model.value as any[]

expect(dates[0].getDate()).toBe(10)
expect(dates[1].getDate()).toBe(20)
expect(dates[0].day).toBe(10)
expect(dates[1].day).toBe(20)
})

it('selects a range of dates across month boundary', async () => {
Expand Down Expand Up @@ -53,15 +53,15 @@ describe('VDatePicker', () => {
// Expect only 2 dates (first and last) spanning across two months
await expect.poll(() => model.value).toHaveLength(2)

// Verify the correct dates are selected (2025-01-07 and 2025-02-07)
const dates = model.value as Date[]
// Model emits Temporal.PlainDateTime (v0 adapter)
const dates = model.value as any[]

expect(dates[0].getFullYear()).toBe(2025)
expect(dates[0].getMonth()).toBe(0)
expect(dates[0].getDate()).toBe(7)
expect(dates[1].getFullYear()).toBe(2025)
expect(dates[1].getMonth()).toBe(1)
expect(dates[1].getDate()).toBe(7)
expect(dates[0].year).toBe(2025)
expect(dates[0].month).toBe(1) // Temporal months are 1-based
expect(dates[0].day).toBe(7)
expect(dates[1].year).toBe(2025)
expect(dates[1].month).toBe(2) // Temporal months are 1-based
expect(dates[1].day).toBe(7)
})

it('does not trigger infinite loop when first-day-of-week is out of range', async () => {
Expand Down
52 changes: 0 additions & 52 deletions packages/vuetify/src/composables/date/DateAdapter.ts

This file was deleted.

Loading
Loading