Skip to content

Instantly share code, notes, and snippets.

@julianwachholz
Created January 3, 2020 12:22
Show Gist options
  • Save julianwachholz/abec7b951508cce1b1e621b6afb65f70 to your computer and use it in GitHub Desktop.
Save julianwachholz/abec7b951508cce1b1e621b6afb65f70 to your computer and use it in GitHub Desktop.
Ant Design <DatePicker /> with native Date and date-fns

Native Date DatePicker component for Ant.Design

Use just like the regular DatePicker. Currently only uses a fixed locale.

import generatePicker from "antd/lib/date-picker/generatePicker";
import {
addDays,
addMonths,
addYears,
format,
getWeek,
isAfter,
isValid,
parse,
setDate,
setHours,
setMinutes,
setMonth,
setSeconds,
setYear
} from "date-fns";
import { enUS } from "date-fns/locale";
import { GenerateConfig } from "rc-picker/lib/generate";
const generateConfig: GenerateConfig<Date> = {
getNow: () => new Date(),
getWeekDay: date => date.getDay(),
getYear: date => date.getFullYear(),
getMonth: date => date.getMonth(),
getDate: date => date.getDate(),
getHour: date => date.getHours(),
getMinute: date => date.getMinutes(),
getSecond: date => date.getSeconds(),
addYear: (date, diff) => addYears(date, diff),
addMonth: (date, diff) => addMonths(date, diff),
addDate: (date, diff) => addDays(date, diff),
setYear: (date, year) => setYear(date, year),
setMonth: (date, month) => setMonth(date, month),
setDate: (date, num) => setDate(date, num),
setHour: (date, hour) => setHours(date, hour),
setMinute: (date, minute) => setMinutes(date, minute),
setSecond: (date, second) => setSeconds(date, second),
isAfter: (date1, date2) => isAfter(date1, date2),
isValidate: date => isValid(date),
locale: {
getWeekFirstDay: locale => 1,
getWeek: (locale, date) => getWeek(date),
getShortWeekDays: locale => {
const d = Array.from({ length: 7 }).map((_, day) => {
console.info("localize day ", day);
return enUS.localize.day(day, { width: "abbreviated" });
});
console.log("days", d);
return d;
},
getShortMonths: locale => {
const m = Array.from({ length: 12 }).map((_, month) =>
enUS.localize.month(month, { width: "abbreviated" })
);
console.log("months", m);
return m;
},
format: (locale, date, fmt) => {
fmt = fmt.replace("YYYY", "yyyy");
fmt = fmt.replace("DD", "dd");
return format(date, fmt, { locale: enUS });
},
parse: (locale, text, formats) => {
const fmt = formats[0].toLowerCase();
return parse(text, fmt, new Date());
}
}
};
const DatePicker = generatePicker<Date>(generateConfig);
export default DatePicker;
@asyncink
Copy link

asyncink commented Jan 21, 2023

The solutions above have a serious drawback - when entering an invalid date (for example, if you type a space in input field), the DatePicker crashes with the entire app.

We can fix this by referencing antd implementation: https://github.com/react-component/picker/blob/master/src/generate/dateFns.ts.

We can also modify the locale field so that the picker explicitly works with a specific locale:

import generatePicker from 'antd/lib/date-picker/generatePicker'

import type { GenerateConfig } from 'rc-picker/lib/generate'
import config from 'rc-picker/lib/generate/dateFns'

import {
  isValid,
  getWeek,
  startOfWeek,
  format as formatDate,
  parse as parseDate
} from 'date-fns'
import { ru } from 'date-fns/locale'

const localeParse = (format: string) => {
  return format
    .replace(/Y/g, 'y')
    .replace(/D/g, 'd')
    .replace(/gggg/, 'yyyy')
    .replace(/g/g, 'G')
    .replace(/([Ww])o/g, 'wo')
}

const locale: GenerateConfig<Date>['locale'] = {
  getWeekFirstDay: () => {
    const clone = ru

    return clone.options?.weekStartsOn ?? 1
  },
  getWeekFirstDate: (locale, date) => {
    return startOfWeek(date, { locale: ru })
  },
  getWeek: (locale, date) => {
    return getWeek(date, { locale: ru })
  },
  getShortWeekDays: () => {
    const clone = ru

    return Array.from({ length: 7 }).map((_, i) =>
      clone.localize?.day(i, { width: 'short' })
    )
  },
  getShortMonths: () => {
    const clone = ru

    return Array.from({ length: 12 }).map((_, i) =>
      clone.localize?.month(i, { width: 'abbreviated' })
    )
  },
  format: (locale, date, format) => {
    if (!isValid(date)) {
      /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
      return null as any
    }

    return formatDate(date, localeParse(format), {
      locale: ru
    })
  },
  parse: (locale, text, formats) => {
    for (let i = 0; i < formats.length; i += 1) {
      const format = localeParse(formats[i])
      const formatText = text
      const date = parseDate(formatText, format, new Date(), {
        locale: ru
      })

      if (isValid(date)) {
        return date
      }
    }

    return null
  }
}

export const DatePicker = generatePicker<Date>({ ...config, locale })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment