From 66ce23f2e18c5506e8f1a7ef20d3483a4df80087 Mon Sep 17 00:00:00 2001 From: iamkun Date: Thu, 19 Dec 2019 14:11:22 +0800 Subject: [PATCH 1/6] fix: Fix CustomParseFormat Plugin to set correct locale --- src/plugin/customParseFormat/index.js | 1 + test/plugin/customParseFormat.test.js | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/plugin/customParseFormat/index.js b/src/plugin/customParseFormat/index.js index 08f67708e..a210b53ff 100644 --- a/src/plugin/customParseFormat/index.js +++ b/src/plugin/customParseFormat/index.js @@ -191,6 +191,7 @@ export default (o, C, d) => { locale = pl ? d.Ls[pl] : this.$locale() this.$d = parseFormattedInput(date, format, utc) this.init(cfg) + if (pl) this.$L = pl } else { oldParse.call(this, cfg) } diff --git a/test/plugin/customParseFormat.test.js b/test/plugin/customParseFormat.test.js index 8f7dc3bab..43e909b58 100644 --- a/test/plugin/customParseFormat.test.js +++ b/test/plugin/customParseFormat.test.js @@ -192,12 +192,20 @@ it('correctly parse ordinal', () => { const inputFalse = '7st March 2019' const inputZHCN = '7日 三月 2019' const format = 'Do MMMM YYYY' + const displayFormatWithLocale = 'MMMM dddd' expect(dayjs(input, format).valueOf()) .toBe(moment(input, format).valueOf()) expect(dayjs(input2, format).valueOf()) .toBe(moment(input2, format).valueOf()) expect(dayjs(inputFalse, format).valueOf()) .toBe(moment(inputFalse, format).valueOf()) - expect(dayjs(inputZHCN, format, 'zh-cn').valueOf()) - .toBe(moment(inputZHCN, format, 'zh-cn').valueOf()) + + const dayjsCN = dayjs(inputZHCN, format, 'zh-cn') + const momentCN = moment(inputZHCN, format, 'zh-cn') + expect(dayjsCN.valueOf()) + .toBe(momentCN.valueOf()) + expect(dayjsCN.format(displayFormatWithLocale)) + .toBe(momentCN.format(displayFormatWithLocale)) + expect(dayjsCN.locale()) + .toBe(momentCN.locale()) }) From 79b86dbbf3cfd3f1e2165b3d479a7061ad1b6925 Mon Sep 17 00:00:00 2001 From: iamkun Date: Mon, 30 Dec 2019 15:28:34 +0800 Subject: [PATCH 2/6] fix: Fix WeekOfYear Plugin wrong calender week number bug fix #760 --- src/plugin/weekOfYear/index.js | 17 +++-------------- test/plugin/weekOfYear.test.js | 1 + 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/plugin/weekOfYear/index.js b/src/plugin/weekOfYear/index.js index 9c7ae6951..7251d5af4 100644 --- a/src/plugin/weekOfYear/index.js +++ b/src/plugin/weekOfYear/index.js @@ -6,25 +6,14 @@ export default (o, c, d) => { if (week !== null) { return this.add((week - this.week()) * 7, 'day') } - const weekStart = this.$locale().weekStart || 0 - - // d(this) clone is for badMutable plugin - const endOfYear = d(this).endOf(Y) - if ( - weekStart === 0 && - endOfYear.day() !== 6 && - this.month() === 11 && - 31 - this.date() <= endOfYear.day() - ) { - return 1 - } - const startOfYear = d(this).startOf(Y) const compareDay = startOfYear.subtract(startOfYear.day() - weekStart, D).subtract(1, MS) const diffInWeek = this.diff(compareDay, W, true) - return Math.ceil(diffInWeek) + const result = Math.ceil(diffInWeek) + return result > 52 ? 1 : result } + proto.weeks = function (week = null) { return this.week(week) } diff --git a/test/plugin/weekOfYear.test.js b/test/plugin/weekOfYear.test.js index a0c446a86..980b4bc77 100644 --- a/test/plugin/weekOfYear.test.js +++ b/test/plugin/weekOfYear.test.js @@ -40,6 +40,7 @@ it('Week of year with locale', () => { // Edges expect(dayjs('2018-12-30').week()).toBe(moment('2018-12-30').week()) expect(dayjs('2019-12-29').week()).toBe(moment('2019-12-29').week()) + expect(dayjs('2019-12-30').week()).toBe(moment('2019-12-30').week()) }) it('Format w ww wo', () => { From ba709eca86a71ae648bc68bf67d9abdc229198d4 Mon Sep 17 00:00:00 2001 From: iamkun Date: Mon, 30 Dec 2019 16:13:44 +0800 Subject: [PATCH 3/6] fix: Fix CustomParseFormat Plugin 'YYYY-MM' use first day of the month fix #761 --- src/plugin/customParseFormat/index.js | 2 +- test/plugin/customParseFormat.test.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugin/customParseFormat/index.js b/src/plugin/customParseFormat/index.js index a210b53ff..ab09179d4 100644 --- a/src/plugin/customParseFormat/index.js +++ b/src/plugin/customParseFormat/index.js @@ -159,9 +159,9 @@ const parseFormattedInput = (input, format, utc) => { ) + (zone.offset * 60 * 1000)) } const now = new Date() + const d = day || ((!year && !month) ? now.getDate() : 1) const y = year || now.getFullYear() const M = month > 0 ? month - 1 : now.getMonth() - const d = day || now.getDate() const h = hours || 0 const m = minutes || 0 const s = seconds || 0 diff --git a/test/plugin/customParseFormat.test.js b/test/plugin/customParseFormat.test.js index 43e909b58..cafb94afd 100644 --- a/test/plugin/customParseFormat.test.js +++ b/test/plugin/customParseFormat.test.js @@ -161,6 +161,10 @@ it('return Invalid Date when parse corrupt short string', () => { expect(dayjs(input, format).format()).toBe('Invalid Date') }) +it('YYYY-MM set 1st day of the month', () => { + expect(dayjs('2019-02', 'YYYY-MM').format('YYYY-MM-DD')).toBe('2019-02-01') +}) + it('Invalid Dates', () => { expect(dayjs('10/12/2014', 'YYYY-MM-DD').format('MM-DD-YYYY')).toBe('Invalid Date') expect(dayjs('10-12-2014', 'YYYY-MM-DD').format('MM-DD-YYYY')).toBe('Invalid Date') From 82ce2ba8d7e402e40f6d005d400eb5356a0b0633 Mon Sep 17 00:00:00 2001 From: iamkun Date: Mon, 6 Jan 2020 15:30:33 +0800 Subject: [PATCH 4/6] fix: Add UpdateLocale plugin to update a locale's properties (#766) --- docs/en/API-reference.md | 6 +++ docs/en/I18n.md | 4 +- docs/en/Plugin.md | 13 ++++++ docs/es-es/API-reference.md | 6 +++ docs/es-es/I18n.md | 4 +- docs/es-es/Plugin.md | 13 ++++++ docs/ja/API-reference.md | 6 +++ docs/ja/I18n.md | 2 + docs/ja/Plugin.md | 13 ++++++ docs/ko/API-reference.md | 6 +++ docs/ko/I18n.md | 2 + docs/ko/Plugin.md | 13 ++++++ docs/pt-br/API-reference.md | 6 +++ docs/pt-br/I18n.md | 2 + docs/pt-br/Plugin.md | 13 ++++++ docs/zh-cn/API-reference.md | 6 +++ docs/zh-cn/I18n.md | 18 +++++++- docs/zh-cn/Plugin.md | 13 ++++++ src/plugin/updateLocale/index.js | 13 ++++++ test/plugin/updateLocale.test.js | 71 ++++++++++++++++++++++++++++++++ types/plugin/minMax.d.ts | 2 +- types/plugin/updateLocale.d.ts | 8 ++++ 22 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 src/plugin/updateLocale/index.js create mode 100644 test/plugin/updateLocale.test.js create mode 100644 types/plugin/updateLocale.d.ts diff --git a/docs/en/API-reference.md b/docs/en/API-reference.md index a4e97c914..8dd7d7979 100644 --- a/docs/en/API-reference.md +++ b/docs/en/API-reference.md @@ -514,3 +514,9 @@ plugin [`MinMax`](./Plugin.md#minmax) `.calendar` to display calendar time plugin [`Calendar`](./Plugin.md#calendar) + +### UpdateLocale + +`.updateLocale` to update a locale's properties + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/en/I18n.md b/docs/en/I18n.md index 045cb8c1e..401063aaf 100644 --- a/docs/en/I18n.md +++ b/docs/en/I18n.md @@ -77,7 +77,9 @@ dayjs() ## Customize -You could create your own locale. +You could update locale config via plugin [`UpdateLocale`](./Plugin.md#updateLocale) + +You could also create your own locale. Feel free to open a pull request to share your locale. diff --git a/docs/en/Plugin.md b/docs/en/Plugin.md index cc9688951..c8ba4fc7b 100644 --- a/docs/en/Plugin.md +++ b/docs/en/Plugin.md @@ -442,6 +442,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale adds `.updateLocale` API to update a locale's properties. + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## Customize You could build your own Day.js plugin to meet different needs. diff --git a/docs/es-es/API-reference.md b/docs/es-es/API-reference.md index 57b3b42d5..be0f30029 100644 --- a/docs/es-es/API-reference.md +++ b/docs/es-es/API-reference.md @@ -514,3 +514,9 @@ plugin [`MinMax`](./Plugin.md#minmax) `.calendar` to display calendar time plugin [`Calendar`](./Plugin.md#calendar) + +### UpdateLocale + +`.updateLocale` to update a locale's properties + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/es-es/I18n.md b/docs/es-es/I18n.md index 551f38787..989269708 100644 --- a/docs/es-es/I18n.md +++ b/docs/es-es/I18n.md @@ -81,7 +81,9 @@ dayjs() ## Personalización -You could create your own locale. +You could update locale config via plugin [`UpdateLocale`](./Plugin.md#updateLocale) + +You could also create your own locale. Feel free to open a pull request to share your locale. diff --git a/docs/es-es/Plugin.md b/docs/es-es/Plugin.md index 7d44c3ad9..e5a7c1469 100644 --- a/docs/es-es/Plugin.md +++ b/docs/es-es/Plugin.md @@ -438,6 +438,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale adds `.updateLocale` API to update a locale's properties. + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## Personalización Puedes construir tu propio complemento de Day.js para cubrir tus necesidades. diff --git a/docs/ja/API-reference.md b/docs/ja/API-reference.md index 8a1152c16..0eba8f216 100644 --- a/docs/ja/API-reference.md +++ b/docs/ja/API-reference.md @@ -514,3 +514,9 @@ UTC でパースや表示をしたい場合は、[`UTC`](./Plugin.md#utc)プラ `.calendar`で与えた日付のカレンダー上の情報が得られます。 プラグイン [`Calendar`](./Plugin.md#calendar) + +### UpdateLocale + +`.updateLocale` to update a locale's properties + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/ja/I18n.md b/docs/ja/I18n.md index 70f013500..a0dab94f0 100644 --- a/docs/ja/I18n.md +++ b/docs/ja/I18n.md @@ -77,6 +77,8 @@ dayjs() ## カスタマイズ +You could update locale config via plugin [`UpdateLocale`](./Plugin.md#updateLocale) + 独自のロケールを作成することもできます。 あなたのプラグインを共有する pull request を是非送ってみてください。 diff --git a/docs/ja/Plugin.md b/docs/ja/Plugin.md index d0f4a7a23..b8e7580e4 100644 --- a/docs/ja/Plugin.md +++ b/docs/ja/Plugin.md @@ -447,6 +447,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale adds `.updateLocale` API to update a locale's properties. + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## カスタマイズ さまざまなニーズに合わせて独自の Day.js プラグインを構築することができます。 diff --git a/docs/ko/API-reference.md b/docs/ko/API-reference.md index e15ffafd9..70cb5d67d 100644 --- a/docs/ko/API-reference.md +++ b/docs/ko/API-reference.md @@ -513,3 +513,9 @@ plugin [`MinMax`](./Plugin.md#minmax) `.calendar` to display calendar time plugin [`Calendar`](./Plugin.md#calendar) + +### UpdateLocale + +`.updateLocale` to update a locale's properties + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/ko/I18n.md b/docs/ko/I18n.md index 421b05295..5df968cdc 100644 --- a/docs/ko/I18n.md +++ b/docs/ko/I18n.md @@ -77,6 +77,8 @@ dayjs() ## Customize +You could update locale config via plugin [`UpdateLocale`](./Plugin.md#updateLocale) + 당신만의 locale을 만들 수 있습니다. locale을 공휴하기위해 풀 리퀘스트를 여십시오. diff --git a/docs/ko/Plugin.md b/docs/ko/Plugin.md index dba61fdc6..90a9e3423 100644 --- a/docs/ko/Plugin.md +++ b/docs/ko/Plugin.md @@ -439,6 +439,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale adds `.updateLocale` API to update a locale's properties. + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## Customize 다양한 요구를 충족하기위해 자신만의 Day.js 플러그인을 만들 수 있습니다. diff --git a/docs/pt-br/API-reference.md b/docs/pt-br/API-reference.md index 23cb01dac..18c165e81 100644 --- a/docs/pt-br/API-reference.md +++ b/docs/pt-br/API-reference.md @@ -512,3 +512,9 @@ plugin [`MinMax`](./Plugin.md#minmax) `.calendar` to display calendar time plugin [`Calendar`](./Plugin.md#calendar) + +### UpdateLocale + +`.updateLocale` to update a locale's properties + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/pt-br/I18n.md b/docs/pt-br/I18n.md index f072f95e4..4a383ecf2 100644 --- a/docs/pt-br/I18n.md +++ b/docs/pt-br/I18n.md @@ -77,6 +77,8 @@ dayjs() ## Customizar +You could update locale config via plugin [`UpdateLocale`](./Plugin.md#updateLocale) + Você pode criar o seu próprio _locale_. Sinta-se a vontade para abrir uma pull request e compartilhar sua _locale_. diff --git a/docs/pt-br/Plugin.md b/docs/pt-br/Plugin.md index 22ebe9e8a..8e16f5f58 100644 --- a/docs/pt-br/Plugin.md +++ b/docs/pt-br/Plugin.md @@ -438,6 +438,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale adds `.updateLocale` API to update a locale's properties. + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## Customizar Você também pode construir seu próprio plugin Day.js para diferentes necessidades. diff --git a/docs/zh-cn/API-reference.md b/docs/zh-cn/API-reference.md index d5cd5753d..02465a8a1 100644 --- a/docs/zh-cn/API-reference.md +++ b/docs/zh-cn/API-reference.md @@ -564,3 +564,9 @@ plugin [`MinMax`](./Plugin.md#minmax) `.calendar` 来显示日历时间 plugin [`Calendar`](./Plugin.md#calendar) + +### 更新语言配置 + +`.updateLocale` 来更新语言配置的属性 + +plugin [`UpdateLocale`](./Plugin.md#updateLocale) diff --git a/docs/zh-cn/I18n.md b/docs/zh-cn/I18n.md index ca9ebf702..55b4aa108 100644 --- a/docs/zh-cn/I18n.md +++ b/docs/zh-cn/I18n.md @@ -77,7 +77,9 @@ dayjs() ## 自定义 -你可以根据需要自由的编写一个 Day.js 语言配置 +你可以使用 [`UpdateLocale`](./Plugin.md#updateLocale) 插件来更新语言配置 + +你还可以根据需要自由的编写一个 Day.js 语言配置 同时欢迎提交 PR 与大家分享你的语言配置 @@ -93,6 +95,20 @@ const localeObject = { months: 'Enero_Febrero ... '.split('_'), // 月份 Array monthsShort: 'Jan_F'.split('_'), // 可选, 短的月份 Array, 如果没提供则使用前三个字符 ordinal: n => `${n}º`, // 序号生成工厂函数 Function (number) => return number + output + formats: { + // 时间日期格式 - 长 + LTS: 'h:mm:ss A', + LT: 'h:mm A', + L: 'MM/DD/YYYY', + LL: 'MMMM D, YYYY', + LLL: 'MMMM D, YYYY h:mm A', + LLLL: 'dddd, MMMM D, YYYY h:mm A', + // 时间日期格式 - 短 + l: 'D/M/YYYY', + ll: 'D MMM, YYYY', + lll: 'D MMM, YYYY h:mm A', + llll: 'ddd, MMM D, YYYY h:mm A' + }, relativeTime: { // 相对时间, %s %d 不用翻译 future: 'in %s', // e.g. in 2 hours, %s been replaced with 2hours diff --git a/docs/zh-cn/Plugin.md b/docs/zh-cn/Plugin.md index 57838c742..89bdf56ce 100644 --- a/docs/zh-cn/Plugin.md +++ b/docs/zh-cn/Plugin.md @@ -441,6 +441,19 @@ dayjs().calendar(null, { }) ``` +### UpdateLocale + +- UpdateLocale 增加了 `.updateLocale` API 来更新语言配置的属性。 + +```javascript +import updateLocale from 'dayjs/plugin/updateLocale' +dayjs.extend(updateLocale) + +dayjs.updateLocale('en', { + months : String[] +}) +``` + ## 自定义 你可以根据需要自由的编写一个 Day.js 插件 diff --git a/src/plugin/updateLocale/index.js b/src/plugin/updateLocale/index.js new file mode 100644 index 000000000..7126f7e1f --- /dev/null +++ b/src/plugin/updateLocale/index.js @@ -0,0 +1,13 @@ +export default (option, Dayjs, dayjs) => { + dayjs.updateLocale = function (locale, customConfig) { + const localeList = dayjs.Ls + const localeConfig = localeList[locale] + if (!localeConfig) return + const customConfigKeys = customConfig ? Object.keys(customConfig) : [] + customConfigKeys.forEach((c) => { + localeConfig[c] = customConfig[c] + }) + return localeConfig // eslint-disable-line consistent-return + } +} + diff --git a/test/plugin/updateLocale.test.js b/test/plugin/updateLocale.test.js new file mode 100644 index 000000000..ecbcbd41c --- /dev/null +++ b/test/plugin/updateLocale.test.js @@ -0,0 +1,71 @@ +import MockDate from 'mockdate' +import moment from 'moment' +import dayjs from '../../src' +import updateLocale from '../../src/plugin/updateLocale' +import localizedFormat from '../../src/plugin/localizedFormat' +import '../../src/locale/zh-cn' + +dayjs.extend(updateLocale) +dayjs.extend(localizedFormat) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +const newLocale = { + months: new Array(12).fill('testMonth'), + formats: { // formats for dayjs and longDateFormat for momentjs + LT: '[testFormat]' + }, + longDateFormat: { + LT: '[testFormat]' + } +} + +const formatString = 'MMMM LT' + +describe('Update locale', () => { + it('Invalid argument', () => { + const result = dayjs.updateLocale('InvalidLocaleName', {}) + expect(result) + .toEqual(undefined) + expect(dayjs().format(formatString)) + .toEqual(moment().format(formatString)) + }) + + it('Return value', () => { + const result1 = dayjs.updateLocale('en') + expect(typeof result1).toEqual('object') + const result2 = dayjs.updateLocale('en', {}) + expect(typeof result2).toEqual('object') + const result3 = dayjs.updateLocale('en', newLocale) + expect(typeof result3).toEqual('object') + }) + + it('Update build-in en locale', () => { + moment.updateLocale('en', newLocale) + dayjs.updateLocale('en', newLocale) + + expect(dayjs().format(formatString)) + .toEqual('testMonth testFormat') + + expect(dayjs().format(formatString)) + .toEqual(moment().format(formatString)) + }) + + it('Update imported zh-cn locale', () => { + moment.updateLocale('zh-cn', newLocale) + dayjs.updateLocale('zh-cn', newLocale) + dayjs.locale('zh-cn') + moment.locale('zh-cn') + expect(dayjs().format(formatString)) + .toEqual('testMonth testFormat') + + expect(dayjs().format(formatString)) + .toEqual(moment().format(formatString)) + }) +}) diff --git a/types/plugin/minMax.d.ts b/types/plugin/minMax.d.ts index 1cca06ef2..f1673500c 100644 --- a/types/plugin/minMax.d.ts +++ b/types/plugin/minMax.d.ts @@ -1,4 +1,4 @@ -import { PluginFunc, ConfigType } from 'dayjs' +import { PluginFunc } from 'dayjs' declare const plugin: PluginFunc export = plugin diff --git a/types/plugin/updateLocale.d.ts b/types/plugin/updateLocale.d.ts new file mode 100644 index 000000000..44bce470d --- /dev/null +++ b/types/plugin/updateLocale.d.ts @@ -0,0 +1,8 @@ +import { PluginFunc } from 'dayjs' + +declare const plugin: PluginFunc +export = plugin + +declare module 'dayjs' { + export function updateLocale(localeName: String, customConfig: Object): any +} From 4bd9250fbe7131e2fddfb5fa1b3350e8c2262ca9 Mon Sep 17 00:00:00 2001 From: iamkun Date: Mon, 6 Jan 2020 16:38:21 +0800 Subject: [PATCH 5/6] fix: Update RelativeTime plugin to support function to make additional processing (#767) * fix: Update RelativeTime plugin to support function to make additional processing * chore update fix * fix: Update ru && uk locale file to support relativeTime with plural --- src/locale/ru.js | 32 ++++++++++++++++++++++------ src/locale/uk.js | 36 +++++++++++++++++++++++++------- src/plugin/relativeTime/index.js | 11 ++++++++-- test/locale/ru.test.js | 24 +++++++++++++++++++++ test/locale/uk.test.js | 36 ++++++++++++++++++++++++++++++++ test/plugin/relativeTime.test.js | 10 +++++++++ 6 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 test/locale/uk.test.js diff --git a/src/locale/ru.js b/src/locale/ru.js index d8911c717..9b5d6d4b8 100644 --- a/src/locale/ru.js +++ b/src/locale/ru.js @@ -7,6 +7,26 @@ const monthShortFormat = 'янв._февр._мар._апр._мая_июня_ию const monthShortStandalone = 'янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.'.split('_') const MONTHS_IN_FORMAT = /D[oD]?(\[[^[\]]*\]|\s)+MMMM?/ + +function plural(word, num) { + const forms = word.split('_') + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]) // eslint-disable-line +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + const format = { + mm: withoutSuffix ? 'минута_минуты_минут' : 'минуту_минуты_минут', + hh: 'час_часа_часов', + dd: 'день_дня_дней', + MM: 'месяц_месяца_месяцев', + yy: 'год_года_лет' + } + if (key === 'm') { + return withoutSuffix ? 'минута' : 'минуту' + } + + return `${number} ${plural(format[key], +number)}` +} + const locale = { name: 'ru', weekdays: 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), @@ -37,16 +57,16 @@ const locale = { future: 'через %s', past: '%s назад', s: 'несколько секунд', - m: 'минута', - mm: '%d минут', + m: relativeTimeWithPlural, + mm: relativeTimeWithPlural, h: 'час', - hh: '%d часов', + hh: relativeTimeWithPlural, d: 'день', - dd: '%d дней', + dd: relativeTimeWithPlural, M: 'месяц', - MM: '%d месяцев', + MM: relativeTimeWithPlural, y: 'год', - yy: '%d лет' + yy: relativeTimeWithPlural }, ordinal: n => n } diff --git a/src/locale/uk.js b/src/locale/uk.js index 270a8483f..45d4b0c04 100644 --- a/src/locale/uk.js +++ b/src/locale/uk.js @@ -1,5 +1,25 @@ import dayjs from 'dayjs' +function plural(word, num) { + const forms = word.split('_') + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]) // eslint-disable-line +} +function relativeTimeWithPlural(number, withoutSuffix, key) { + const format = { + ss: withoutSuffix ? 'секунда_секунди_секунд' : 'секунду_секунди_секунд', + mm: withoutSuffix ? 'хвилина_хвилини_хвилин' : 'хвилину_хвилини_хвилин', + hh: withoutSuffix ? 'година_години_годин' : 'годину_години_годин', + dd: 'день_дні_днів', + MM: 'місяць_місяці_місяців', + yy: 'рік_роки_років' + } + if (key === 'm') { + return withoutSuffix ? 'хвилина' : 'хвилину' + } + + return `${number} ${plural(format[key], +number)}` +} + const locale = { name: 'uk', weekdays: 'неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота'.split('_'), @@ -9,19 +29,19 @@ const locale = { monthsShort: 'сiч_лют_бер_квiт_трав_черв_лип_серп_вер_жовт_лист_груд'.split('_'), weekStart: 1, relativeTime: { - future: 'через %s', + future: 'за %s', past: '%s тому', s: 'декілька секунд', - m: 'хвилина', - mm: '%d хвилин', - h: 'година', - hh: '%d годин', + m: relativeTimeWithPlural, + mm: relativeTimeWithPlural, + h: 'годину', + hh: relativeTimeWithPlural, d: 'день', - dd: '%d днів', + dd: relativeTimeWithPlural, M: 'місяць', - MM: '%d місяців', + MM: relativeTimeWithPlural, y: 'рік', - yy: '%d роки' + yy: relativeTimeWithPlural }, ordinal: n => n, formats: { diff --git a/src/plugin/relativeTime/index.js b/src/plugin/relativeTime/index.js index 07d585c19..2a68737fe 100644 --- a/src/plugin/relativeTime/index.js +++ b/src/plugin/relativeTime/index.js @@ -35,6 +35,7 @@ export default (o, c, d) => { const Tl = T.length let result let out + let isFuture for (let i = 0; i < Tl; i += 1) { let t = T[i] @@ -44,14 +45,20 @@ export default (o, c, d) => { : instance.diff(input, t.d, true) } const abs = Math.round(Math.abs(result)) + isFuture = result > 0 if (abs <= t.r || !t.r) { if (abs === 1 && i > 0) t = T[i - 1] // 1 minutes -> a minute - out = loc[t.l].replace('%d', abs) + const format = loc[t.l] + if (typeof format === 'string') { + out = format.replace('%d', abs) + } else { + out = format(abs, withoutSuffix, t.l, isFuture) + } break } } if (withoutSuffix) return out - return ((result > 0) ? loc.future : loc.past).replace('%s', out) + return (isFuture ? loc.future : loc.past).replace('%s', out) } proto.to = function (input, withoutSuffix) { return fromTo(input, withoutSuffix, this, true) diff --git a/test/locale/ru.test.js b/test/locale/ru.test.js index 3960c78d0..9044eb59a 100644 --- a/test/locale/ru.test.js +++ b/test/locale/ru.test.js @@ -1,8 +1,11 @@ import moment from 'moment' import MockDate from 'mockdate' import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' import '../../src/locale/ru' +dayjs.extend(relativeTime) + beforeEach(() => { MockDate.set(new Date()) }) @@ -23,3 +26,24 @@ it('Format Month with locale function', () => { expect(dayjsRU.format(testFormat3)).toEqual(momentRU.format(testFormat3)) } }) + +it('RelativeTime: Time from X', () => { + const T = [ + [44.4, 'second'], // a few seconds + [89.5, 'second'], // a minute + [43, 'minute'], // 44 minutes + [21, 'hour'], // 21 hours + [25, 'day'], // 25 days + [10, 'month'], // 2 month + [18, 'month'] // 2 years + ] + + T.forEach((t) => { + dayjs.locale('ru') + moment.locale('ru') + expect(dayjs().from(dayjs().add(t[0], t[1]))) + .toBe(moment().from(moment().add(t[0], t[1]))) + expect(dayjs().from(dayjs().add(t[0], t[1]), true)) + .toBe(moment().from(moment().add(t[0], t[1]), true)) + }) +}) diff --git a/test/locale/uk.test.js b/test/locale/uk.test.js new file mode 100644 index 000000000..bb734cde1 --- /dev/null +++ b/test/locale/uk.test.js @@ -0,0 +1,36 @@ +import moment from 'moment' +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/uk' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('RelativeTime: Time from X', () => { + const T = [ + [44.4, 'second'], // a few seconds + [89.5, 'second'], // a minute + [43, 'minute'], // 44 minutes + [21, 'hour'], // 21 hours + [25, 'day'], // 25 days + [10, 'month'], // 2 month + [18, 'month'] // 2 years + ] + + T.forEach((t) => { + dayjs.locale('uk') + moment.locale('uk') + expect(dayjs().from(dayjs().add(t[0], t[1]))) + .toBe(moment().from(moment().add(t[0], t[1]))) + expect(dayjs().from(dayjs().add(t[0], t[1]), true)) + .toBe(moment().from(moment().add(t[0], t[1]), true)) + }) +}) diff --git a/test/plugin/relativeTime.test.js b/test/plugin/relativeTime.test.js index de9e10d10..ab4a6c8c1 100644 --- a/test/plugin/relativeTime.test.js +++ b/test/plugin/relativeTime.test.js @@ -3,6 +3,7 @@ import moment from 'moment' import dayjs from '../../src' import relativeTime from '../../src/plugin/relativeTime' import utc from '../../src/plugin/utc' +import '../../src/locale/ru' dayjs.extend(relativeTime) @@ -83,6 +84,15 @@ it('Time to X', () => { expect(dayjs().to(dayjs().subtract(3, 'year'))).toBe(moment().to(moment().subtract(3, 'year'))) }) +it('Locale Fonction', () => { + // e.g. in ru locale, m: x minute require additional processing + // and provides as a function instead of a string + const str0 = '2020-01-06 15:53:00' + const str = '2020-01-06 15:52:15' + const result = dayjs(str0).locale('ru').to(str) + expect(result).toEqual(expect.any(String)) +}) + // https://github.com/iamkun/dayjs/issues/646 it('Time from now with UTC', () => { dayjs.extend(utc) From 3f080f7d6bfdc4018cbb7c4d0112ff1ead4ef6b8 Mon Sep 17 00:00:00 2001 From: iamkun Date: Mon, 6 Jan 2020 17:03:03 +0800 Subject: [PATCH 6/6] fix: Update ru, uk, cs locale to support relativeTime with plural --- src/locale/cs.js | 69 +++++++++++++++++++++++++++----- test/locale/cs.test.js | 52 ++++++++++++++++++++++++ test/plugin/relativeTime.test.js | 2 +- 3 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 test/locale/cs.test.js diff --git a/src/locale/cs.js b/src/locale/cs.js index 3910b8dcf..1c44b31e3 100644 --- a/src/locale/cs.js +++ b/src/locale/cs.js @@ -1,5 +1,52 @@ import dayjs from 'dayjs' +function plural(n) { + return (n > 1) && (n < 5) && (~~(n / 10) !== 1) // eslint-disable-line +} +/* eslint-disable */ +function translate(number, withoutSuffix, key, isFuture) { + const result = `${number} ` + switch (key) { + case 's': // a few seconds / in a few seconds / a few seconds ago + return (withoutSuffix || isFuture) ? 'pár sekund' : 'pár sekundami' + case 'm': // a minute / in a minute / a minute ago + return withoutSuffix ? 'minuta' : (isFuture ? 'minutu' : 'minutou') + case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'minuty' : 'minut') + } + return `${result}minutami` + case 'h': // an hour / in an hour / an hour ago + return withoutSuffix ? 'hodina' : (isFuture ? 'hodinu' : 'hodinou') + case 'hh': // 9 hours / in 9 hours / 9 hours ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'hodiny' : 'hodin') + } + return `${result}hodinami` + case 'd': // a day / in a day / a day ago + return (withoutSuffix || isFuture) ? 'den' : 'dnem' + case 'dd': // 9 days / in 9 days / 9 days ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'dny' : 'dní') + } + return `${result}dny` + case 'M': // a month / in a month / a month ago + return (withoutSuffix || isFuture) ? 'měsíc' : 'měsícem' + case 'MM': // 9 months / in 9 months / 9 months ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'měsíce' : 'měsíců') + } + return `${result}měsíci` + case 'y': // a year / in a year / a year ago + return (withoutSuffix || isFuture) ? 'rok' : 'rokem' + case 'yy': // 9 years / in 9 years / 9 years ago + if (withoutSuffix || isFuture) { + return result + (plural(number) ? 'roky' : 'let') + } + return `${result}lety` + } +} +/* eslint-enable */ const locale = { name: 'cs', weekdays: 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'), @@ -21,17 +68,17 @@ const locale = { relativeTime: { future: 'za %s', past: 'před %s', - s: 'několik sekund', - m: 'minuta', - mm: '%d minut', - h: 'hodina', - hh: '%d hodin', - d: 'den', - dd: '%d dnů', - M: 'měsíc', - MM: '%d měsíců', - y: 'rok', - yy: '%d roků' + s: translate, + m: translate, + mm: translate, + h: translate, + hh: translate, + d: translate, + dd: translate, + M: translate, + MM: translate, + y: translate, + yy: translate } } diff --git a/test/locale/cs.test.js b/test/locale/cs.test.js new file mode 100644 index 000000000..8b2ce216d --- /dev/null +++ b/test/locale/cs.test.js @@ -0,0 +1,52 @@ +import moment from 'moment' +import MockDate from 'mockdate' +import dayjs from '../../src' +import relativeTime from '../../src/plugin/relativeTime' +import '../../src/locale/cs' + +dayjs.extend(relativeTime) + +beforeEach(() => { + MockDate.set(new Date()) +}) + +afterEach(() => { + MockDate.reset() +}) + +it('RelativeTime: Time from X', () => { + const T = [ + [44.4, 'second'], // a few seconds + [89.5, 'second'], // a minute + [2, 'minute'], // 2 minutes + [43, 'minute'], // 44 minutes + [45, 'minute'], // an hour + [3, 'hour'], // 3 hours + [21, 'hour'], // 21 hours + [1, 'day'], // a day + [3, 'day'], // 3 day + [25, 'day'], // 25 days + [1, 'month'], // a month + [2, 'month'], // 2 month + [10, 'month'], // 10 month + [1, 'year'], // a year + [2, 'year'], // 2 year + [5, 'year'], // 5 year + [18, 'month'] // 2 years + ] + + T.forEach((t) => { + dayjs.locale('cs') + moment.locale('cs') + const dayjsDay = dayjs() + const momentDay = moment() + const dayjsCompare = dayjs().add(t[0], t[1]) + const momentCompare = moment().add(t[0], t[1]) + expect(dayjsDay.from(dayjsCompare)) + .toBe(momentDay.from(momentCompare)) + expect(dayjsDay.to(dayjsCompare)) + .toBe(momentDay.to(momentCompare)) + expect(dayjsDay.from(dayjsCompare, true)) + .toBe(momentDay.from(momentCompare, true)) + }) +}) diff --git a/test/plugin/relativeTime.test.js b/test/plugin/relativeTime.test.js index ab4a6c8c1..11b1d5d55 100644 --- a/test/plugin/relativeTime.test.js +++ b/test/plugin/relativeTime.test.js @@ -51,7 +51,7 @@ it('Time from X', () => { [1, 'month'], // a month [45, 'day'], // a month [47, 'day'], // 2 month - [10, 'month'], // 2 month + [10, 'month'], // 10 month [11, 'month'], // a year [1, 'year'], // a year [17, 'month'], // a year