Skip to content

Commit

Permalink
fix: Update localizedFormat plugin to support lowercase localizable f…
Browse files Browse the repository at this point in the history
…ormats (l, ll, lll, llll) (#546)
  • Loading branch information
mariomc authored and iamkun committed Apr 1, 2019
1 parent 3f58237 commit f2b5ebf
Show file tree
Hide file tree
Showing 19 changed files with 146 additions and 26 deletions.
7 changes: 6 additions & 1 deletion docs/en/I18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,12 @@ const localeObject = {
L: 'MM/DD/YYYY',
LL: 'MMMM D, YYYY',
LLL: 'MMMM D, YYYY h:mm A',
LLLL: 'dddd, MMMM D, YYYY h:mm A'
LLLL: 'dddd, MMMM D, YYYY h:mm A',
// lowercase/short, optional formats for localization
l: 'D/M/YYYY',
ll: 'D MMM, YYYY',
lll: 'D MMM, YYYY h:mm A',
llll: 'ddd, MMM D, YYYY h:mm A'
},
relativeTime: {
// relative time format strings, keep %s %d as the same
Expand Down
4 changes: 4 additions & 0 deletions docs/en/Plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ List of added formats:
| `LL` | MMMM D, YYYY | August 16, 2018 |
| `LLL` | MMMM D, YYYY h:mm A | August 16, 2018 8:02 PM |
| `LLLL` | dddd, MMMM D, YYYY h:mm A | Thursday, August 16, 2018 8:02 PM |
| `l` | M/D/YYYY | 8/16/2018 |
| `ll` | MMM D, YYYY | Aug 16, 2018 |
| `lll` | MMM D, YYYY h:mm A | Aug 16, 2018 8:02 PM |
| `llll` | ddd, MMM D, YYYY h:mm A | Thu, Aug 16, 2018 8:02 PM |

### RelativeTime

Expand Down
14 changes: 14 additions & 0 deletions docs/pt-br/I18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ const objetoLocale = {
months: 'Enero_Febrero ... '.split('_'), // meses: Array
monthsShort: 'Jan_F'.split('_'), // OPCIONAL, meses com nome curto: Array, utiliza as três primeiras letras se nenhuma for especificada
ordinal: n => `${n}º`, // ordinal: Function (number) => retorna number + saída
formats: {
// opções para formatos localizados
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',
// formatos localizados/curtos opcionais
l: 'D/M/YYYY',
ll: 'D MMM, YYYY',
lll: 'D MMM, YYYY h:mm A',
llll: 'ddd, MMM D, YYYY h:mm A'
},
relativeTime: {
// formato relativo de horas, mantém %s %d como o mesmo
future: 'em %s', // exemplo: em 2 horas, %s será substituído por 2 horas
Expand Down
2 changes: 1 addition & 1 deletion src/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export const INVALID_DATE_STRING = 'Invalid Date'

// regex
export const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/
export const REGEX_FORMAT = /\[.*?\]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g
export const REGEX_FORMAT = /\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g
5 changes: 1 addition & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,7 @@ class Dayjs {
Z: zoneStr // 'ZZ' logic below
}

return str.replace(C.REGEX_FORMAT, (match) => {
if (match.indexOf('[') > -1) return match.replace(/\[|\]/g, '')
return matches[match] || zoneStr.replace(':', '') // 'ZZ'
})
return str.replace(C.REGEX_FORMAT, (match, $1) => $1 || matches[match] || zoneStr.replace(':', '')) // 'ZZ'
}

utcOffset() {
Expand Down
5 changes: 4 additions & 1 deletion src/locale/ca.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ const locale = {
L: 'DD/MM/YYYY',
LL: 'D MMMM [de] YYYY',
LLL: 'D MMMM [de] YYYY [a les] H:mm',
LLLL: 'dddd D MMMM [de] YYYY [a les] H:mm'
LLLL: 'dddd D MMMM [de] YYYY [a les] H:mm',
ll: 'D MMM YYYY',
lll: 'D MMM YYYY, H:mm',
llll: 'ddd D MMM YYYY, H:mm'
},
relativeTime: {
future: 'en %s',
Expand Down
3 changes: 2 additions & 1 deletion src/locale/cs.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const locale = {
L: 'DD.MM.YYYY',
LL: 'D. MMMM YYYY',
LLL: 'D. MMMM YYYY H:mm',
LLLL: 'dddd D. MMMM YYYY H:mm'
LLLL: 'dddd D. MMMM YYYY H:mm',
l: 'D. M. YYYY'
},
relativeTime: {
future: 'za %s',
Expand Down
12 changes: 12 additions & 0 deletions src/locale/fi.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ const locale = {
MM: '%d kuukautta', // for past tense
y: 'vuosi', // for past tense
yy: '%d vuotta' // for past tense
},
formats: {
LT: 'HH.mm',
LTS: 'HH.mm.ss',
L: 'DD.MM.YYYY',
LL: 'Do MMMM[ta] YYYY',
LLL: 'Do MMMM[ta] YYYY, [klo] HH.mm',
LLLL: 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm',
l: 'D.M.YYYY',
ll: 'Do MMM YYYY',
lll: 'Do MMM YYYY, [klo] HH.mm',
llll: 'ddd, Do MMM YYYY, [klo] HH.mm'
}
}

Expand Down
14 changes: 13 additions & 1 deletion src/locale/he.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,19 @@ const locale = {
y: 'שנה',
yy: '%d שנים'
},
ordinal: n => n
ordinal: n => n,
format: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'DD/MM/YYYY',
LL: 'D [ב]MMMM YYYY',
LLL: 'D [ב]MMMM YYYY HH:mm',
LLLL: 'dddd, D [ב]MMMM YYYY HH:mm',
l: 'D/M/YYYY',
ll: 'D MMM YYYY',
lll: 'D MMM YYYY HH:mm',
llll: 'ddd, D MMM YYYY HH:mm'
}
}

dayjs.locale(locale, null, true)
Expand Down
6 changes: 5 additions & 1 deletion src/locale/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ const locale = {
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日 HH:mm',
LLLL: 'YYYY年M月D日 dddd HH:mm'
LLLL: 'YYYY年M月D日 dddd HH:mm',
l: 'YYYY/MM/DD',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日(ddd) HH:mm'
},
relativeTime: {
future: '%s後',
Expand Down
6 changes: 5 additions & 1 deletion src/locale/ko.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ const locale = {
L: 'YYYY.MM.DD.',
LL: 'YYYY년 MMMM D일',
LLL: 'YYYY년 MMMM D일 A h:mm',
LLLL: 'YYYY년 MMMM D일 dddd A h:mm'
LLLL: 'YYYY년 MMMM D일 dddd A h:mm',
l: 'YYYY.MM.DD.',
ll: 'YYYY년 MMMM D일',
lll: 'YYYY년 MMMM D일 A h:mm',
llll: 'YYYY년 MMMM D일 dddd A h:mm'
},
relativeTime: {
future: '%s 후',
Expand Down
12 changes: 12 additions & 0 deletions src/locale/lt.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ const locale = {
MM: '%d mėnesius',
y: 'metus',
yy: '%d metus'
},
format: {
LT: 'HH:mm',
LTS: 'HH:mm:ss',
L: 'YYYY-MM-DD',
LL: 'YYYY [m.] MMMM D [d.]',
LLL: 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
LLLL: 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]',
l: 'YYYY-MM-DD',
ll: 'YYYY [m.] MMMM D [d.]',
lll: 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
llll: 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]'
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/locale/sv.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const locale = {
L: 'YYYY-MM-DD',
LL: 'D MMMM YYYY',
LLL: 'D MMMM YYYY [kl.] HH:mm',
LLLL: 'dddd D MMMM YYYY [kl.] HH:mm'
LLLL: 'dddd D MMMM YYYY [kl.] HH:mm',
lll: 'D MMM YYYY HH:mm',
llll: 'ddd D MMM YYYY HH:mm'
},
relativeTime: {
future: 'om %s',
Expand Down
6 changes: 5 additions & 1 deletion src/locale/zh-cn.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const locale = {
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日Ah点mm分',
LLLL: 'YYYY年M月D日ddddAh点mm分'
LLLL: 'YYYY年M月D日ddddAh点mm分',
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm'
},
relativeTime: {
future: '%s内',
Expand Down
6 changes: 5 additions & 1 deletion src/locale/zh-tw.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const locale = {
L: 'YYYY/MM/DD',
LL: 'YYYY年M月D日',
LLL: 'YYYY年M月D日 HH:mm',
LLLL: 'YYYY年M月D日dddd HH:mm'
LLLL: 'YYYY年M月D日dddd HH:mm',
l: 'YYYY/M/D',
ll: 'YYYY年M月D日',
lll: 'YYYY年M月D日 HH:mm',
llll: 'YYYY年M月D日dddd HH:mm'
},
relativeTime: {
future: '%s內',
Expand Down
10 changes: 7 additions & 3 deletions src/plugin/localizedFormat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ export default (o, c, d) => {
L: 'MM/DD/YYYY',
LL: 'MMMM D, YYYY',
LLL: 'MMMM D, YYYY h:mm A',
LLLL: 'dddd, MMMM D, YYYY h:mm A'
LLLL: 'dddd, MMMM D, YYYY h:mm A',
l: 'M/D/YYYY',
ll: 'MMM D, YYYY',
lll: 'MMM D, YYYY h:mm A',
llll: 'ddd, MMM D, YYYY h:mm A'
}
d.en.formats = englishFormats
proto.format = function (formatStr) {
const locale = this.$locale()
const formats = locale.formats || {}
const str = formatStr || FORMAT_DEFAULT
const result = str.replace(/LTS|LT|L{1,4}/g, match =>
formats[match] || englishFormats[match])
const result = str.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, (_, a, b) =>
a || formats[b] || englishFormats[b])
return oldFormat.call(this, result)
}
}
Expand Down
4 changes: 3 additions & 1 deletion test/display.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ it('Format Complex with other string - : / ', () => {
})

it('Format Escaping characters', () => {
const string = '[Z] Z'
let string = '[Z] Z'
expect(dayjs().format(string)).toBe(moment().format(string))
string = '[Z] Z [Z]'
expect(dayjs().format(string)).toBe(moment().format(string))
})

Expand Down
35 changes: 30 additions & 5 deletions test/locale/keys.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import path from 'path'
import dayjs from '../../src'

const localeDir = '../../src/locale'
const L = []
const Locale = []

// load all locales from locale dir
fs.readdirSync(path.join(__dirname, localeDir))
.forEach((file) => {
// eslint-disable-next-line
L.push(require(path.join(__dirname, localeDir, file)).default)
Locale.push(require(path.join(__dirname, localeDir, file)).default)
})

it('Locale keys', () => {
L.forEach((l) => {
Locale.forEach((locale) => {
const {
name,
ordinal,
Expand All @@ -25,7 +25,7 @@ it('Locale keys', () => {
monthsShort,
weekdaysMin,
weekStart
} = l
} = locale
expect(name).toEqual(expect.any(String))
expect(weekdays).toEqual(expect.any(Array))

Expand All @@ -44,7 +44,32 @@ it('Locale keys', () => {

expect(dayjs().locale(name).$locale().name).toBe(name)
if (formats) {
expect(Object.keys(formats).sort()).toEqual(['L', 'LL', 'LLL', 'LLLL', 'LT', 'LTS'].sort())
const {
LT,
LTS,
L,
LL,
LLL,
LLLL,
l,
ll,
lll,
llll,
...remainingFormats
} = formats
expect(formats).toEqual(expect.objectContaining({
L: expect.any(String),
LL: expect.any(String),
LLL: expect.any(String),
LLLL: expect.any(String),
LT: expect.any(String),
LTS: expect.any(String)
}))
expect(Object.keys(remainingFormats).length).toEqual(0)
if (l) expect(l).toEqual(expect.any(String))
if (ll) expect(ll).toEqual(expect.any(String))
if (lll) expect(lll).toEqual(expect.any(String))
if (llll) expect(llll).toEqual(expect.any(String))
}
if (relativeTime) {
expect(Object.keys(relativeTime).sort()).toEqual(['d', 'dd', 'future', 'h', 'hh', 'm', 'mm', 'M', 'MM',
Expand Down
17 changes: 14 additions & 3 deletions test/plugin/localizableFormat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,33 @@ afterEach(() => {
it('Declares English localized formats', () => {
expect(dayjs.en).toBeDefined()
expect(dayjs.en.formats).toBeDefined();
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
expect(dayjs.en.formats[option]).toBeDefined())
})

it('Should not interpolate characters inside square brackets', () => {
const date = new Date(0)
const actualDate = dayjs(date)
const expectedDate = moment(date)

expect(actualDate.format('[l]')).toBe('l')
expect(actualDate.format('YYYY [l] YYYY')).toBe('1970 l 1970')
expect(actualDate.format('l [l] l')).toBe('1/1/1970 l 1/1/1970')
expect(actualDate.format('[L LL LLL LLLL]')).toBe(expectedDate.format('[L LL LLL LLLL]'))
})

it('Recognizes localized format options', () => {
const { formats } = dayjs.en
const date = dayjs();
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
expect(date.format(option)).toBe(date.format(formats[option])))
})

it('Uses correct English formats', () => {
const date = new Date()
const actualDate = dayjs(date)
const expectedDate = moment(date);
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
expect(actualDate.format(option)).toBe(expectedDate.format(option)))
})

Expand Down

0 comments on commit f2b5ebf

Please sign in to comment.