diff --git a/packages/vant/src/calendar/CalendarMonth.tsx b/packages/vant/src/calendar/CalendarMonth.tsx
index f52448da1..5148e45f3 100644
--- a/packages/vant/src/calendar/CalendarMonth.tsx
+++ b/packages/vant/src/calendar/CalendarMonth.tsx
@@ -15,7 +15,7 @@ import {
createNamespace,
makeRequiredProp,
} from '../utils';
-import { getMonthEndDay } from '../datetime-picker/utils';
+import { getMonthEndDay } from '../date-picker/utils';
import {
t,
bem,
diff --git a/packages/vant/src/date-picker/DatePicker.tsx b/packages/vant/src/date-picker/DatePicker.tsx
index d77dde992..cc8a613c2 100644
--- a/packages/vant/src/date-picker/DatePicker.tsx
+++ b/packages/vant/src/date-picker/DatePicker.tsx
@@ -17,12 +17,7 @@ import {
makeArrayProp,
createNamespace,
} from '../utils';
-import {
- times,
- sharedProps,
- getMonthEndDay,
- pickerInheritKeys,
-} from '../datetime-picker/utils';
+import { times, sharedProps, getMonthEndDay, pickerInheritKeys } from './utils';
// Components
import { Picker } from '../picker';
@@ -88,8 +83,21 @@ export default defineComponent({
month === props.maxDate.getMonth() + 1;
const getValue = (type: DatePickerColumnType) => {
- const index = props.columnsType.indexOf(type);
- return +currentValues.value[index];
+ const { minDate, columnsType } = props;
+ const index = columnsType.indexOf(type);
+ const value = currentValues.value[index];
+ if (value) {
+ return +value;
+ }
+
+ switch (type) {
+ case 'year':
+ return minDate.getFullYear();
+ case 'month':
+ return minDate.getMonth() + 1;
+ case 'day':
+ return minDate.getDate();
+ }
};
const genMonthOptions = () => {
diff --git a/packages/vant/src/date-picker/test/__snapshots__/demo.spec.ts.snap b/packages/vant/src/date-picker/test/__snapshots__/demo.spec.ts.snap
new file mode 100644
index 000000000..38c6c2e0c
--- /dev/null
+++ b/packages/vant/src/date-picker/test/__snapshots__/demo.spec.ts.snap
@@ -0,0 +1,1016 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render demo and match snapshot 1`] = `
+
+
+
+
+
+
+ -
+
+ 2020
+
+
+ -
+
+ 2021
+
+
+ -
+
+ 2022
+
+
+ -
+
+ 2023
+
+
+ -
+
+ 2024
+
+
+ -
+
+ 2025
+
+
+
+
+
+
+ -
+
+ 01
+
+
+ -
+
+ 02
+
+
+ -
+
+ 03
+
+
+ -
+
+ 04
+
+
+ -
+
+ 05
+
+
+ -
+
+ 06
+
+
+ -
+
+ 07
+
+
+ -
+
+ 08
+
+
+ -
+
+ 09
+
+
+ -
+
+ 10
+
+
+ -
+
+ 11
+
+
+ -
+
+ 12
+
+
+
+
+
+
+ -
+
+ 01
+
+
+ -
+
+ 02
+
+
+ -
+
+ 03
+
+
+ -
+
+ 04
+
+
+ -
+
+ 05
+
+
+ -
+
+ 06
+
+
+ -
+
+ 07
+
+
+ -
+
+ 08
+
+
+ -
+
+ 09
+
+
+ -
+
+ 10
+
+
+ -
+
+ 11
+
+
+ -
+
+ 12
+
+
+ -
+
+ 13
+
+
+ -
+
+ 14
+
+
+ -
+
+ 15
+
+
+ -
+
+ 16
+
+
+ -
+
+ 17
+
+
+ -
+
+ 18
+
+
+ -
+
+ 19
+
+
+ -
+
+ 20
+
+
+ -
+
+ 21
+
+
+ -
+
+ 22
+
+
+ -
+
+ 23
+
+
+ -
+
+ 24
+
+
+ -
+
+ 25
+
+
+ -
+
+ 26
+
+
+ -
+
+ 27
+
+
+ -
+
+ 28
+
+
+ -
+
+ 29
+
+
+ -
+
+ 30
+
+
+ -
+
+ 31
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+ 2020
+
+
+ -
+
+ 2021
+
+
+ -
+
+ 2022
+
+
+ -
+
+ 2023
+
+
+ -
+
+ 2024
+
+
+ -
+
+ 2025
+
+
+
+
+
+
+ -
+
+ 01
+
+
+ -
+
+ 02
+
+
+ -
+
+ 03
+
+
+ -
+
+ 04
+
+
+ -
+
+ 05
+
+
+ -
+
+ 06
+
+
+ -
+
+ 07
+
+
+ -
+
+ 08
+
+
+ -
+
+ 09
+
+
+ -
+
+ 10
+
+
+ -
+
+ 11
+
+
+ -
+
+ 12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+ 2020 Year
+
+
+ -
+
+ 2021 Year
+
+
+ -
+
+ 2022 Year
+
+
+ -
+
+ 2023 Year
+
+
+ -
+
+ 2024 Year
+
+
+ -
+
+ 2025 Year
+
+
+
+
+
+
+ -
+
+ 01 Month
+
+
+ -
+
+ 02 Month
+
+
+ -
+
+ 03 Month
+
+
+ -
+
+ 04 Month
+
+
+ -
+
+ 05 Month
+
+
+ -
+
+ 06 Month
+
+
+ -
+
+ 07 Month
+
+
+ -
+
+ 08 Month
+
+
+ -
+
+ 09 Month
+
+
+ -
+
+ 10 Month
+
+
+ -
+
+ 11 Month
+
+
+ -
+
+ 12 Month
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+ 2020
+
+
+ -
+
+ 2021
+
+
+ -
+
+ 2022
+
+
+ -
+
+ 2023
+
+
+ -
+
+ 2024
+
+
+ -
+
+ 2025
+
+
+
+
+
+
+ -
+
+ 06
+
+
+ -
+
+ 12
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/vant/src/date-picker/test/date-picker.legacy.ts b/packages/vant/src/date-picker/test/date-picker.legacy.ts
new file mode 100644
index 000000000..a4dff6036
--- /dev/null
+++ b/packages/vant/src/date-picker/test/date-picker.legacy.ts
@@ -0,0 +1,220 @@
+// import DatePicker from '../../datetime-picker/DatePicker';
+// import { mount, later, triggerDrag } from '../../../test';
+
+// function filter(type: string, options: string[]): string[] {
+// const mod = type === 'year' ? 10 : 5;
+// return options.filter((option: string) => Number(option) % mod === 0);
+// }
+
+// function formatter(type: string, value: string): string {
+// return `${value} ${type}`;
+// }
+
+// test('filter prop', async () => {
+// const wrapper = mount(DatePicker, {
+// props: {
+// filter,
+// minDate: new Date(2020, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// modelValue: new Date(2020, 10, 1, 0, 0),
+// },
+// });
+
+// expect(wrapper.html()).toMatchSnapshot();
+// });
+
+// test('formatter prop', async () => {
+// const wrapper = mount(DatePicker, {
+// props: {
+// filter,
+// formatter,
+// minDate: new Date(2010, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// modelValue: new Date(2020, 10, 1, 0, 0),
+// },
+// });
+
+// expect(wrapper.html()).toMatchSnapshot();
+
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -100);
+// wrapper.find('.van-picker-column ul').trigger('transitionend');
+// await later();
+
+// expect((wrapper.vm as Record).getPicker().getValues()).toEqual([
+// '2020 year',
+// '05 month',
+// '05 day',
+// '00 hour',
+// '00 minute',
+// ]);
+// });
+
+// test('confirm event', () => {
+// const date = new Date(2020, 10, 1, 0, 0);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// modelValue: date,
+// minDate: new Date(2020, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2020);
+
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -100);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025);
+// });
+
+// test('year-month type', async () => {
+// const date = new Date(2020, 10, 1, 0, 0);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// type: 'year-month',
+// modelValue: date,
+// minDate: new Date(2020, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2020);
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10);
+
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -100);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025);
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(0);
+
+// triggerDrag(wrapper.findAll('.van-picker-column')[0], 0, -100);
+// await later();
+// triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -100);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getFullYear()).toEqual(2025);
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(10);
+// });
+
+// test('month-day type', async () => {
+// const date = new Date(2020, 10, 1, 0, 0);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// type: 'month-day',
+// modelValue: date,
+// minDate: new Date(2020, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10);
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getDate()).toEqual(1);
+
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -300);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(11);
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getDate()).toEqual(1);
+
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -300);
+// await later();
+// triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -300);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(11);
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getDate()).toEqual(31);
+// });
+
+// test('datehour type', () => {
+// const wrapper = mount(DatePicker, {
+// props: {
+// minDate: new Date(2010, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// modelValue: new Date(2020, 10, 1, 0, 0),
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getHours()).toEqual(0);
+
+// triggerDrag(wrapper.findAll('.van-picker-column')[3], 0, -300);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getHours()).toEqual(23);
+// });
+
+// test('cancel event', () => {
+// const wrapper = mount(DatePicker);
+
+// wrapper.find('.van-picker__cancel').trigger('click');
+// expect(wrapper.emitted('cancel')).toBeTruthy();
+// });
+
+// test('max-date prop', () => {
+// const maxDate = new Date(2010, 5, 0, 0, 0);
+// const wrapper = mount(DatePicker, {
+// props: {
+// modelValue: new Date(2020, 10, 30, 30, 30),
+// maxDate,
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(maxDate);
+// });
+
+// test('min-date prop', () => {
+// const minDate = new Date(2030, 0, 0, 0, 0);
+// const wrapper = mount(DatePicker, {
+// props: {
+// modelValue: new Date(2020, 0, 0, 0, 0),
+// minDate,
+// },
+// });
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(minDate);
+// });
+
+// test('dynamic set value', async () => {
+// const wrapper = mount(DatePicker, {
+// props: {
+// modelValue: new Date(2019, 1, 1),
+// },
+// });
+
+// await wrapper.setProps({ modelValue: new Date(2019, 1, 1) });
+// wrapper.find('.van-picker__confirm').trigger('click');
+// await wrapper.setProps({ modelValue: new Date(2025, 1, 1) });
+// wrapper.find('.van-picker__confirm').trigger('click');
+
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getFullYear()).toEqual(2019);
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getFullYear()).toEqual(2025);
+// });
+
+// test('use min-date with filter', async () => {
+// const minDate = new Date(2030, 0, 0, 0, 3);
+// const maxDate = new Date(2040, 0, 0, 0, 0);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// minDate,
+// maxDate,
+// modelValue: new Date(2020, 0, 0, 0, 0),
+// filter(type: string, values: string[]) {
+// if (type === 'minute') {
+// return values.filter((value) => Number(value) % 30 === 0);
+// }
+
+// return values;
+// },
+// },
+// });
+
+// await later();
+
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(
+// new Date(2030, 0, 0, 0, 30)
+// );
+// });
diff --git a/packages/vant/src/date-picker/test/datetime-picker.legacy.tsx b/packages/vant/src/date-picker/test/datetime-picker.legacy.tsx
new file mode 100644
index 000000000..1313053be
--- /dev/null
+++ b/packages/vant/src/date-picker/test/datetime-picker.legacy.tsx
@@ -0,0 +1,116 @@
+// import { DatetimePicker } from '../../datetime-picker';
+// import { mount, later } from '../../../test';
+// import { reactive } from 'vue';
+// import { useExpose } from '../../composables/use-expose';
+
+// test('should emit confirm event after clicking the confirm button', () => {
+// const onConfirm = jest.fn();
+// const wrapper = mount(DatetimePicker, {
+// props: {
+// onConfirm,
+// },
+// });
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(onConfirm).toHaveBeenCalledTimes(1);
+// });
+
+// test('should emit cancel event after clicking the confirm button', () => {
+// const onCancel = jest.fn();
+// const wrapper = mount(DatetimePicker, {
+// props: {
+// onCancel,
+// },
+// });
+// wrapper.find('.van-picker__cancel').trigger('click');
+// expect(onCancel).toHaveBeenCalledTimes(1);
+// });
+
+// test('should allow to call getPicker method', () => {
+// const wrapper = mount(DatetimePicker);
+
+// expect(wrapper.vm.getPicker()).toBeTruthy();
+// });
+
+// test('should render title slot correctly', () => {
+// const wrapper = mount(DatetimePicker, {
+// props: {
+// showToolbar: true,
+// },
+// slots: {
+// title: () => 'Custom title',
+// },
+// });
+
+// expect(wrapper.find('.van-picker__toolbar').html()).toMatchSnapshot();
+// });
+
+// test('should emit value correctly when dynamic change min-date', async () => {
+// const defaultValue = new Date(2020, 10, 2, 10, 30);
+// const wrapper = mount({
+// emits: ['confirm'],
+// setup(_, { emit }) {
+// const state = reactive({
+// date: defaultValue,
+// minDate: new Date(2010, 0, 1, 10, 30),
+// });
+
+// const onChange = () => {
+// state.minDate = state.date;
+// };
+
+// useExpose({
+// onChange,
+// });
+
+// return () => (
+// emit('confirm', value)}
+// />
+// );
+// },
+// });
+
+// await later();
+// (wrapper.vm as Record).onChange();
+// await later();
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(defaultValue);
+// });
+
+// test('should update value correctly after calling setColumnIndex method', async () => {
+// const onConfirm = jest.fn();
+// const defaultDate = new Date(2020, 0, 1);
+// const wrapper = mount(DatetimePicker, {
+// props: {
+// type: 'date',
+// minDate: defaultDate,
+// maxDate: new Date(2020, 0, 30),
+// modelValue: defaultDate,
+// onConfirm,
+// },
+// });
+
+// wrapper.vm.getPicker().setColumnIndex(2, 14);
+// await wrapper.find('.van-picker__confirm').trigger('click');
+// expect(onConfirm.mock.calls[0]).toEqual([new Date(2020, 0, 15)]);
+// });
+
+// test('should update value correctly after calling setColumnValue method', async () => {
+// const onConfirm = jest.fn();
+// const defaultDate = new Date(2020, 0, 1);
+// const wrapper = mount(DatetimePicker, {
+// props: {
+// type: 'date',
+// minDate: defaultDate,
+// maxDate: new Date(2020, 0, 30),
+// modelValue: defaultDate,
+// onConfirm,
+// },
+// });
+
+// wrapper.vm.getPicker().setColumnValue(2, '15');
+// await wrapper.find('.van-picker__confirm').trigger('click');
+// expect(onConfirm.mock.calls[0]).toEqual([new Date(2020, 0, 15)]);
+// });
diff --git a/packages/vant/src/date-picker/test/demo.spec.ts b/packages/vant/src/date-picker/test/demo.spec.ts
new file mode 100644
index 000000000..c0e0c95b9
--- /dev/null
+++ b/packages/vant/src/date-picker/test/demo.spec.ts
@@ -0,0 +1,4 @@
+import Demo from '../demo/index.vue';
+import { snapshotDemo } from '../../../test/demo';
+
+snapshotDemo(Demo);
diff --git a/packages/vant/src/date-picker/test/index.legacy.ts b/packages/vant/src/date-picker/test/index.legacy.ts
new file mode 100644
index 000000000..f490ec9c2
--- /dev/null
+++ b/packages/vant/src/date-picker/test/index.legacy.ts
@@ -0,0 +1,59 @@
+// import { later, mount, triggerDrag } from '../../../test';
+// import DatePicker from '../../datetime-picker/DatePicker';
+
+// test('month-day type', async () => {
+// const date = new Date(2020, 10, 1, 0, 0);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// type: 'month-day',
+// modelValue: date,
+// minDate: new Date(2020, 0, 1),
+// maxDate: new Date(2025, 10, 1),
+// },
+// });
+
+// await later();
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getMonth()).toEqual(10);
+// expect(wrapper.emitted<[Date]>('confirm')![0][0].getDate()).toEqual(1);
+
+// await later();
+// triggerDrag(wrapper.find('.van-picker-column'), 0, -300);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getMonth()).toEqual(11);
+// expect(wrapper.emitted<[Date]>('confirm')![1][0].getDate()).toEqual(1);
+
+// await later();
+// triggerDrag(wrapper.findAll('.van-picker-column')[1], 0, -300);
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getMonth()).toEqual(11);
+// expect(wrapper.emitted<[Date]>('confirm')![2][0].getDate()).toEqual(31);
+// });
+
+// test('v-model', async () => {
+// const minDate = new Date(2030, 0, 0, 0, 3);
+
+// const wrapper = mount(DatePicker, {
+// props: {
+// minDate,
+// },
+// });
+
+// await later();
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(minDate);
+// });
+
+// test('value has an initial value', async () => {
+// const defaultValue = new Date(2020, 0, 0, 0, 0);
+// const wrapper = mount(DatePicker, {
+// propsData: {
+// modelValue: defaultValue,
+// },
+// });
+
+// await later();
+// wrapper.find('.van-picker__confirm').trigger('click');
+// expect(wrapper.emitted<[Date]>('confirm')![0][0]).toEqual(defaultValue);
+// });
diff --git a/packages/vant/src/date-picker/utils.ts b/packages/vant/src/date-picker/utils.ts
new file mode 100644
index 000000000..4f2d701b0
--- /dev/null
+++ b/packages/vant/src/date-picker/utils.ts
@@ -0,0 +1,38 @@
+import { extend } from '../utils';
+import { pickerSharedProps } from '../picker/Picker';
+import type { PropType } from 'vue';
+import type { PickerOption } from '../picker';
+
+export const sharedProps = extend({}, pickerSharedProps, {
+ filter: Function as PropType<
+ (columnType: string, options: PickerOption[]) => PickerOption[]
+ >,
+ formatter: {
+ type: Function as PropType<
+ (type: string, option: PickerOption) => PickerOption
+ >,
+ default: (type: string, option: PickerOption) => option,
+ },
+});
+
+export const pickerInheritKeys = Object.keys(pickerSharedProps) as Array<
+ keyof typeof pickerSharedProps
+>;
+
+export function times(n: number, iteratee: (index: number) => T) {
+ if (n < 0) {
+ return [];
+ }
+
+ const result: T[] = Array(n);
+
+ let index = -1;
+ while (++index < n) {
+ result[index] = iteratee(index);
+ }
+
+ return result;
+}
+
+export const getMonthEndDay = (year: number, month: number): number =>
+ 32 - new Date(year, month - 1, 32).getDate();
diff --git a/packages/vant/src/time-picker/TimePicker.tsx b/packages/vant/src/time-picker/TimePicker.tsx
index 34d67b5a4..7086238cc 100644
--- a/packages/vant/src/time-picker/TimePicker.tsx
+++ b/packages/vant/src/time-picker/TimePicker.tsx
@@ -15,11 +15,7 @@ import {
createNamespace,
makeNumericProp,
} from '../utils';
-import {
- times,
- sharedProps,
- pickerInheritKeys,
-} from '../datetime-picker/utils';
+import { times, sharedProps, pickerInheritKeys } from '../date-picker/utils';
// Components
import { Picker } from '../picker';