diff --git a/package.json b/package.json index d1d80e521..baba5e570 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ }, "dependencies": { "classnames": "^2.2.1", + "dayjs": "^1.8.18", "moment": "^2.24.0", "rc-trigger": "^4.0.0-alpha.6", "rc-util": "^4.15.7" diff --git a/src/generate/dayjs.ts b/src/generate/dayjs.ts new file mode 100644 index 000000000..0be1d9cc7 --- /dev/null +++ b/src/generate/dayjs.ts @@ -0,0 +1,124 @@ +import dayjs, { Dayjs } from 'dayjs'; +import { noteOnce } from 'rc-util/lib/warning'; +import weekday from 'dayjs/plugin/weekday'; +import localeData from 'dayjs/plugin/localeData'; +import weekOfYear from 'dayjs/plugin/weekOfYear'; +import weekYear from 'dayjs/plugin/weekYear'; +import advancedFormat from 'dayjs/plugin/advancedFormat'; +import customParseFormat from 'dayjs/plugin/customParseFormat'; +import { GenerateConfig } from '.'; + +dayjs.extend(customParseFormat); +dayjs.extend(advancedFormat); +dayjs.extend(weekday); +dayjs.extend(localeData); +dayjs.extend(weekOfYear); +dayjs.extend(weekYear); + +dayjs.extend((o, c) => { + // todo support Wo (ISO week) + const proto = c.prototype; + const oldFormat = proto.format; + proto.format = function f(formatStr: string) { + const str = (formatStr || '').replace('Wo', 'wo'); + return oldFormat.bind(this)(str); + }; +}); + +type IlocaleMapObject = { [key: string]: string }; +const localeMap: IlocaleMapObject = { + en_GB: 'en-gb', + en_US: 'en', + zh_CN: 'zh-cn', + zh_TW: 'zh-tw', +}; + +const parseLocale = (locale: string) => { + const mapLocale = localeMap[locale]; + return mapLocale || locale.split('_')[0]; +}; + +const parseNoMatchNotice = () => { + /* istanbul ignore next */ + noteOnce( + false, + 'Not match any format. Please help to fire a issue about this.', + ); +} + +const generateConfig: GenerateConfig = { + // get + getNow: () => dayjs(), + getWeekDay: date => date.weekday(), + getYear: date => date.year(), + getMonth: date => date.month(), + getDate: date => date.date(), + getHour: date => date.hour(), + getMinute: date => date.minute(), + getSecond: date => date.second(), + + // set + addYear: (date, diff) => date.add(diff, 'year'), + addMonth: (date, diff) => date.add(diff, 'month'), + addDate: (date, diff) => date.add(diff, 'day'), + setYear: (date, year) => date.year(year), + setMonth: (date, month) => date.month(month), + setDate: (date, num) => date.date(num), + setHour: (date, hour) => date.hour(hour), + setMinute: (date, minute) => date.minute(minute), + setSecond: (date, second) => date.second(second), + + // Compare + isAfter: (date1, date2) => date1.isAfter(date2), + isValidate: date => date.isValid(), + + locale: { + getWeekFirstDay: locale => + dayjs() + .locale(parseLocale(locale)) + .localeData() + .firstDayOfWeek(), + getWeek: (locale, date) => date.locale(parseLocale(locale)).week(), + getShortWeekDays: locale => + dayjs() + .locale(parseLocale(locale)) + .localeData() + .weekdaysMin(), + getShortMonths: locale => + dayjs() + .locale(parseLocale(locale)) + .localeData() + .monthsShort(), + format: (locale, date, format) => + date.locale(parseLocale(locale)).format(format), + parse: (locale, text, formats) => { + const localeStr = parseLocale(locale) + for (let i = 0; i < formats.length; i += 1) { + const format = formats[i]; + const formatText = text; + if (format.includes('wo') || format.includes('Wo')) { // parse Wo + const year = formatText.split('-')[0] + const weekStr = formatText.split('-')[1] + const firstWeek = dayjs(year, 'YYYY').startOf('year').locale(localeStr) + for (let j = 0; j <= 52; j += 1) { + const nextWeek = firstWeek.add(j, 'week') + if (nextWeek.format('Wo') === weekStr) { + return nextWeek + } + } + parseNoMatchNotice() + return null; + } + const date = dayjs(formatText, format).locale(localeStr); + if (date.isValid()) { + return date; + } + } + + parseNoMatchNotice() + return null; + }, + }, +}; + +export default generateConfig; diff --git a/tests/generate.spec.tsx b/tests/generate.spec.tsx index ce0065092..2d25c8ede 100644 --- a/tests/generate.spec.tsx +++ b/tests/generate.spec.tsx @@ -1,7 +1,10 @@ import MockDate from 'mockdate'; import momentGenerateConfig from '../src/generate/moment'; +import dayjsGenerateConfig from '../src/generate/dayjs'; import { getMoment } from './util/commonUtil'; +import 'dayjs/locale/zh-cn'; + describe('Picker.Generate', () => { beforeAll(() => { MockDate.set(getMoment('1990-09-03 01:02:03').toDate()); @@ -11,132 +14,243 @@ describe('Picker.Generate', () => { MockDate.reset(); }); - [{ name: 'moment', generateConfig: momentGenerateConfig }].forEach( - ({ name, generateConfig }) => { - describe(name, () => { - it('get', () => { - const now = generateConfig.getNow(); - expect(generateConfig.getWeekDay(now)).toEqual(1); - expect(generateConfig.getSecond(now)).toEqual(3); - expect(generateConfig.getMinute(now)).toEqual(2); - expect(generateConfig.getHour(now)).toEqual(1); - expect(generateConfig.getDate(now)).toEqual(3); - expect(generateConfig.getMonth(now)).toEqual(8); - expect(generateConfig.getYear(now)).toEqual(1990); - }); - - it('set', () => { - let date = generateConfig.getNow(); - date = generateConfig.setYear(date, 2020); - date = generateConfig.setMonth(date, 9); - date = generateConfig.setDate(date, 23); - date = generateConfig.setHour(date, 2); - date = generateConfig.setMinute(date, 3); - date = generateConfig.setSecond(date, 5); - - expect( - generateConfig.locale.format('en_US', date, 'YYYY-MM-DD HH:mm:ss'), - ).toEqual('2020-10-23 02:03:05'); - }); - - it('add', () => { - let date = generateConfig.getNow(); - date = generateConfig.addYear(date, 2); - date = generateConfig.addMonth(date, 2); - date = generateConfig.addDate(date, 2); - expect( - generateConfig.locale.format('en_US', date, 'YYYY-MM-DD'), - ).toEqual('1992-11-05'); - }); + [ + { name: 'moment', generateConfig: momentGenerateConfig }, + { name: 'dayjs', generateConfig: dayjsGenerateConfig }, + ].forEach(({ name, generateConfig }) => { + describe(name, () => { + it('get', () => { + const now = generateConfig.getNow(); + expect(generateConfig.getWeekDay(now)).toEqual(1); + expect(generateConfig.getSecond(now)).toEqual(3); + expect(generateConfig.getMinute(now)).toEqual(2); + expect(generateConfig.getHour(now)).toEqual(1); + expect(generateConfig.getDate(now)).toEqual(3); + expect(generateConfig.getMonth(now)).toEqual(8); + expect(generateConfig.getYear(now)).toEqual(1990); + }); - it('isAfter', () => { - const now = generateConfig.getNow(); - const prev = generateConfig.addDate(now, -1); - const next = generateConfig.addDate(now, 1); - expect(generateConfig.isAfter(now, prev)).toBeTruthy(); - expect(generateConfig.isAfter(next, now)).toBeTruthy(); - }); + it('set', () => { + let date = generateConfig.getNow(); + date = generateConfig.setYear(date, 2020); + date = generateConfig.setMonth(date, 9); + date = generateConfig.setDate(date, 23); + date = generateConfig.setHour(date, 2); + date = generateConfig.setMinute(date, 3); + date = generateConfig.setSecond(date, 5); - it('isValidate', () => { - expect( - generateConfig.isValidate(generateConfig.getNow()), - ).toBeTruthy(); - }); + expect( + generateConfig.locale.format('en_US', date, 'YYYY-MM-DD HH:mm:ss'), + ).toEqual('2020-10-23 02:03:05'); + }); - describe('locale', () => { - describe('parse', () => { - it('basic', () => { - ['2000-01-02', '02/01/2000'].forEach(str => { - const date = generateConfig.locale.parse('en_US', str, [ - 'YYYY-MM-DD', - 'DD/MM/YYYY', - ]); - - expect( - generateConfig.locale.format('en_US', date!, 'YYYY-MM-DD'), - ).toEqual('2000-01-02'); - }); - }); + it('add', () => { + let date = generateConfig.getNow(); + date = generateConfig.addYear(date, 2); + date = generateConfig.addMonth(date, 2); + date = generateConfig.addDate(date, 2); + expect( + generateConfig.locale.format('en_US', date, 'YYYY-MM-DD'), + ).toEqual('1992-11-05'); + }); - it('week', () => { - expect( - generateConfig.locale.format( - 'en_US', - generateConfig.locale.parse('en_US', '2019-1st', [ - 'gggg-wo', - ])!, - 'gggg-wo', - ), - ).toEqual('2019-1st'); + it('isAfter', () => { + const now = generateConfig.getNow(); + const prev = generateConfig.addDate(now, -1); + const next = generateConfig.addDate(now, 1); + expect(generateConfig.isAfter(now, prev)).toBeTruthy(); + expect(generateConfig.isAfter(next, now)).toBeTruthy(); + }); - expect( - generateConfig.locale.format( - 'zh_CN', - generateConfig.locale.parse('zh_CN', '2019-45周', [ - 'gggg-wo', - ])!, - 'gggg-wo', - ), - ).toEqual('2019-45周'); - }); - }); + it('isValidate', () => { + expect(generateConfig.isValidate(generateConfig.getNow())).toBeTruthy(); + }); - it('getWeekFirstDay', () => { - expect(generateConfig.locale.getWeekFirstDay('en_US')).toEqual(0); - expect(generateConfig.locale.getWeekFirstDay('zh_CN')).toEqual(1); + describe('locale', () => { + describe('parse', () => { + it('basic', () => { + ['2000-01-02', '02/01/2000'].forEach(str => { + const date = generateConfig.locale.parse('en_US', str, [ + 'YYYY-MM-DD', + 'DD/MM/YYYY', + ]); - // Should keep same weekday - ['en_US', 'zh_CN'].forEach(() => { expect( - generateConfig.getWeekDay( - generateConfig.locale.parse('en_US', '2000-01-01', [ - 'YYYY-MM-DD', - ])!, - ), - ).toEqual(6); + generateConfig.locale.format('en_US', date!, 'YYYY-MM-DD'), + ).toEqual('2000-01-02'); }); }); - it('getWeek', () => { + it('week', () => { expect( - generateConfig.locale.getWeek( - 'zh_CN', - generateConfig.locale.parse('zh_CN', '2019-12-08', [ - 'YYYY-MM-DD', + generateConfig.locale.format( + 'en_US', + generateConfig.locale.parse('en_US', '2019-1st', [ + 'gggg-wo', ])!, + 'gggg-wo', ), - ).toEqual(49); + ).toEqual('2019-1st'); + expect( - generateConfig.locale.getWeek( - 'en_US', - generateConfig.locale.parse('en_US', '2019-12-08', [ - 'YYYY-MM-DD', + generateConfig.locale.format( + 'zh_CN', + generateConfig.locale.parse('zh_CN', '2019-45周', [ + 'gggg-wo', ])!, + 'gggg-wo', ), - ).toEqual(50); + ).toEqual('2019-45周'); + }); + }); + }); + + describe('locale', () => { + it('parse', () => { + ['2000-01-02', '02/01/2000'].forEach(str => { + const date = generateConfig.locale.parse('en_US', str, [ + 'YYYY-MM-DD', + 'DD/MM/YYYY', + ]); + + expect( + generateConfig.locale.format('en_US', date!, 'YYYY-MM-DD'), + ).toEqual('2000-01-02'); }); }); + }) + + it('getWeekFirstDay', () => { + expect(generateConfig.locale.getWeekFirstDay('en_US')).toEqual(0); + expect(generateConfig.locale.getWeekFirstDay('zh_CN')).toEqual(1); + + // Should keep same weekday + ['en_US', 'zh_CN'].forEach(() => { + expect( + generateConfig.getWeekDay( + generateConfig.locale.parse('en_US', '2000-01-01', [ + 'YYYY-MM-DD', + ])!, + ), + ).toEqual(6); + }); }); - }, - ); + + it('Parse format Wo', () => { + expect( + generateConfig.locale.parse( + 'en_US', + '2012-51st', + ['YYYY-Wo'], + )?.format('Wo'), + ).toEqual('51st'); + expect( + generateConfig.locale.parse( + 'zh_CN', + '2012-1周', + ['YYYY-Wo'], + )?.format('Wo'), + ).toEqual('1周'); + }); + + it('Parse format faild', () => { + expect( + generateConfig.locale.parse( + 'en_US', + 'invalid string', + ['invalid string'], + ), + ).toEqual(null); + expect( + generateConfig.locale.parse( + 'en_US', + 'invalid string', + ['invalid string-Wo'], + ), + ).toEqual(null); + }); + + it('getShortWeekDays', () => { + expect(generateConfig.locale.getShortWeekDays('zh_CN')).toEqual([ + '日', + '一', + '二', + '三', + '四', + '五', + '六', + ]); + expect(generateConfig.locale.getShortWeekDays('en_US')).toEqual([ + 'Su', + 'Mo', + 'Tu', + 'We', + 'Th', + 'Fr', + 'Sa', + ]); + }); + + it('getShortMonths', () => { + expect(generateConfig.locale.getShortMonths('zh_CN')).toEqual([ + '1月', + '2月', + '3月', + '4月', + '5月', + '6月', + '7月', + '8月', + '9月', + '10月', + '11月', + '12月', + ]); + expect(generateConfig.locale.getShortMonths('en_US')).toEqual([ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]); + }); + + it('getWeek', () => { + expect( + generateConfig.locale.getWeek( + 'zh_CN', + generateConfig.locale.parse('zh_CN', '2019-12-08', [ + 'YYYY-MM-DD', + ])!, + ), + ).toEqual(49); + }); + + it('getWeek', () => { + expect( + generateConfig.locale.getWeek( + 'zh_CN', + generateConfig.locale.parse('zh_CN', '2019-12-08', [ + 'YYYY-MM-DD', + ])!, + ), + ).toEqual(49); + expect( + generateConfig.locale.getWeek( + 'en_US', + generateConfig.locale.parse('en_US', '2019-12-08', [ + 'YYYY-MM-DD', + ])!, + ), + ).toEqual(50); + }); + }); + }); });