Skip to content

Commit f2b5ebf

Browse files
mariomciamkun
authored andcommitted
fix: Update localizedFormat plugin to support lowercase localizable formats (l, ll, lll, llll) (#546)
1 parent 3f58237 commit f2b5ebf

19 files changed

+146
-26
lines changed

docs/en/I18n.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ const localeObject = {
100100
L: 'MM/DD/YYYY',
101101
LL: 'MMMM D, YYYY',
102102
LLL: 'MMMM D, YYYY h:mm A',
103-
LLLL: 'dddd, MMMM D, YYYY h:mm A'
103+
LLLL: 'dddd, MMMM D, YYYY h:mm A',
104+
// lowercase/short, optional formats for localization
105+
l: 'D/M/YYYY',
106+
ll: 'D MMM, YYYY',
107+
lll: 'D MMM, YYYY h:mm A',
108+
llll: 'ddd, MMM D, YYYY h:mm A'
104109
},
105110
relativeTime: {
106111
// relative time format strings, keep %s %d as the same

docs/en/Plugin.md

+4
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ List of added formats:
135135
| `LL` | MMMM D, YYYY | August 16, 2018 |
136136
| `LLL` | MMMM D, YYYY h:mm A | August 16, 2018 8:02 PM |
137137
| `LLLL` | dddd, MMMM D, YYYY h:mm A | Thursday, August 16, 2018 8:02 PM |
138+
| `l` | M/D/YYYY | 8/16/2018 |
139+
| `ll` | MMM D, YYYY | Aug 16, 2018 |
140+
| `lll` | MMM D, YYYY h:mm A | Aug 16, 2018 8:02 PM |
141+
| `llll` | ddd, MMM D, YYYY h:mm A | Thu, Aug 16, 2018 8:02 PM |
138142

139143
### RelativeTime
140144

docs/pt-br/I18n.md

+14
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ const objetoLocale = {
9393
months: 'Enero_Febrero ... '.split('_'), // meses: Array
9494
monthsShort: 'Jan_F'.split('_'), // OPCIONAL, meses com nome curto: Array, utiliza as três primeiras letras se nenhuma for especificada
9595
ordinal: n => `${n}º`, // ordinal: Function (number) => retorna number + saída
96+
formats: {
97+
// opções para formatos localizados
98+
LTS: 'h:mm:ss A',
99+
LT: 'h:mm A',
100+
L: 'MM/DD/YYYY',
101+
LL: 'MMMM D, YYYY',
102+
LLL: 'MMMM D, YYYY h:mm A',
103+
LLLL: 'dddd, MMMM D, YYYY h:mm A',
104+
// formatos localizados/curtos opcionais
105+
l: 'D/M/YYYY',
106+
ll: 'D MMM, YYYY',
107+
lll: 'D MMM, YYYY h:mm A',
108+
llll: 'ddd, MMM D, YYYY h:mm A'
109+
},
96110
relativeTime: {
97111
// formato relativo de horas, mantém %s %d como o mesmo
98112
future: 'em %s', // exemplo: em 2 horas, %s será substituído por 2 horas

src/constant.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ export const INVALID_DATE_STRING = 'Invalid Date'
2727

2828
// regex
2929
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})?$/
30-
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
30+
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

src/index.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,7 @@ class Dayjs {
315315
Z: zoneStr // 'ZZ' logic below
316316
}
317317

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

324321
utcOffset() {

src/locale/ca.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ const locale = {
1111
L: 'DD/MM/YYYY',
1212
LL: 'D MMMM [de] YYYY',
1313
LLL: 'D MMMM [de] YYYY [a les] H:mm',
14-
LLLL: 'dddd D MMMM [de] YYYY [a les] H:mm'
14+
LLLL: 'dddd D MMMM [de] YYYY [a les] H:mm',
15+
ll: 'D MMM YYYY',
16+
lll: 'D MMM YYYY, H:mm',
17+
llll: 'ddd D MMM YYYY, H:mm'
1518
},
1619
relativeTime: {
1720
future: 'en %s',

src/locale/cs.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const locale = {
1212
L: 'DD.MM.YYYY',
1313
LL: 'D. MMMM YYYY',
1414
LLL: 'D. MMMM YYYY H:mm',
15-
LLLL: 'dddd D. MMMM YYYY H:mm'
15+
LLLL: 'dddd D. MMMM YYYY H:mm',
16+
l: 'D. M. YYYY'
1617
},
1718
relativeTime: {
1819
future: 'za %s',

src/locale/fi.js

+12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ const locale = {
3232
MM: '%d kuukautta', // for past tense
3333
y: 'vuosi', // for past tense
3434
yy: '%d vuotta' // for past tense
35+
},
36+
formats: {
37+
LT: 'HH.mm',
38+
LTS: 'HH.mm.ss',
39+
L: 'DD.MM.YYYY',
40+
LL: 'Do MMMM[ta] YYYY',
41+
LLL: 'Do MMMM[ta] YYYY, [klo] HH.mm',
42+
LLLL: 'dddd, Do MMMM[ta] YYYY, [klo] HH.mm',
43+
l: 'D.M.YYYY',
44+
ll: 'Do MMM YYYY',
45+
lll: 'Do MMM YYYY, [klo] HH.mm',
46+
llll: 'ddd, Do MMM YYYY, [klo] HH.mm'
3547
}
3648
}
3749

src/locale/he.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,19 @@ const locale = {
2121
y: 'שנה',
2222
yy: '%d שנים'
2323
},
24-
ordinal: n => n
24+
ordinal: n => n,
25+
format: {
26+
LT: 'HH:mm',
27+
LTS: 'HH:mm:ss',
28+
L: 'DD/MM/YYYY',
29+
LL: 'D [ב]MMMM YYYY',
30+
LLL: 'D [ב]MMMM YYYY HH:mm',
31+
LLLL: 'dddd, D [ב]MMMM YYYY HH:mm',
32+
l: 'D/M/YYYY',
33+
ll: 'D MMM YYYY',
34+
lll: 'D MMM YYYY HH:mm',
35+
llll: 'ddd, D MMM YYYY HH:mm'
36+
}
2537
}
2638

2739
dayjs.locale(locale, null, true)

src/locale/ja.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ const locale = {
1313
L: 'YYYY/MM/DD',
1414
LL: 'YYYY年M月D日',
1515
LLL: 'YYYY年M月D日 HH:mm',
16-
LLLL: 'YYYY年M月D日 dddd HH:mm'
16+
LLLL: 'YYYY年M月D日 dddd HH:mm',
17+
l: 'YYYY/MM/DD',
18+
ll: 'YYYY年M月D日',
19+
lll: 'YYYY年M月D日 HH:mm',
20+
llll: 'YYYY年M月D日(ddd) HH:mm'
1721
},
1822
relativeTime: {
1923
future: '%s後',

src/locale/ko.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ const locale = {
1212
L: 'YYYY.MM.DD.',
1313
LL: 'YYYY년 MMMM D일',
1414
LLL: 'YYYY년 MMMM D일 A h:mm',
15-
LLLL: 'YYYY년 MMMM D일 dddd A h:mm'
15+
LLLL: 'YYYY년 MMMM D일 dddd A h:mm',
16+
l: 'YYYY.MM.DD.',
17+
ll: 'YYYY년 MMMM D일',
18+
lll: 'YYYY년 MMMM D일 A h:mm',
19+
llll: 'YYYY년 MMMM D일 dddd A h:mm'
1620
},
1721
relativeTime: {
1822
future: '%s 후',

src/locale/lt.js

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ const locale = {
2020
MM: '%d mėnesius',
2121
y: 'metus',
2222
yy: '%d metus'
23+
},
24+
format: {
25+
LT: 'HH:mm',
26+
LTS: 'HH:mm:ss',
27+
L: 'YYYY-MM-DD',
28+
LL: 'YYYY [m.] MMMM D [d.]',
29+
LLL: 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
30+
LLLL: 'YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]',
31+
l: 'YYYY-MM-DD',
32+
ll: 'YYYY [m.] MMMM D [d.]',
33+
lll: 'YYYY [m.] MMMM D [d.], HH:mm [val.]',
34+
llll: 'YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]'
2335
}
2436
}
2537

src/locale/sv.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const locale = {
1919
L: 'YYYY-MM-DD',
2020
LL: 'D MMMM YYYY',
2121
LLL: 'D MMMM YYYY [kl.] HH:mm',
22-
LLLL: 'dddd D MMMM YYYY [kl.] HH:mm'
22+
LLLL: 'dddd D MMMM YYYY [kl.] HH:mm',
23+
lll: 'D MMM YYYY HH:mm',
24+
llll: 'ddd D MMM YYYY HH:mm'
2325
},
2426
relativeTime: {
2527
future: 'om %s',

src/locale/zh-cn.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ const locale = {
2222
L: 'YYYY/MM/DD',
2323
LL: 'YYYY年M月D日',
2424
LLL: 'YYYY年M月D日Ah点mm分',
25-
LLLL: 'YYYY年M月D日ddddAh点mm分'
25+
LLLL: 'YYYY年M月D日ddddAh点mm分',
26+
l: 'YYYY/M/D',
27+
ll: 'YYYY年M月D日',
28+
lll: 'YYYY年M月D日 HH:mm',
29+
llll: 'YYYY年M月D日dddd HH:mm'
2630
},
2731
relativeTime: {
2832
future: '%s内',

src/locale/zh-tw.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ const locale = {
1414
L: 'YYYY/MM/DD',
1515
LL: 'YYYY年M月D日',
1616
LLL: 'YYYY年M月D日 HH:mm',
17-
LLLL: 'YYYY年M月D日dddd HH:mm'
17+
LLLL: 'YYYY年M月D日dddd HH:mm',
18+
l: 'YYYY/M/D',
19+
ll: 'YYYY年M月D日',
20+
lll: 'YYYY年M月D日 HH:mm',
21+
llll: 'YYYY年M月D日dddd HH:mm'
1822
},
1923
relativeTime: {
2024
future: '%s內',

src/plugin/localizedFormat/index.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ export default (o, c, d) => {
99
L: 'MM/DD/YYYY',
1010
LL: 'MMMM D, YYYY',
1111
LLL: 'MMMM D, YYYY h:mm A',
12-
LLLL: 'dddd, MMMM D, YYYY h:mm A'
12+
LLLL: 'dddd, MMMM D, YYYY h:mm A',
13+
l: 'M/D/YYYY',
14+
ll: 'MMM D, YYYY',
15+
lll: 'MMM D, YYYY h:mm A',
16+
llll: 'ddd, MMM D, YYYY h:mm A'
1317
}
1418
d.en.formats = englishFormats
1519
proto.format = function (formatStr) {
1620
const locale = this.$locale()
1721
const formats = locale.formats || {}
1822
const str = formatStr || FORMAT_DEFAULT
19-
const result = str.replace(/LTS|LT|L{1,4}/g, match =>
20-
formats[match] || englishFormats[match])
23+
const result = str.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, (_, a, b) =>
24+
a || formats[b] || englishFormats[b])
2125
return oldFormat.call(this, result)
2226
}
2327
}

test/display.test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ it('Format Complex with other string - : / ', () => {
138138
})
139139

140140
it('Format Escaping characters', () => {
141-
const string = '[Z] Z'
141+
let string = '[Z] Z'
142+
expect(dayjs().format(string)).toBe(moment().format(string))
143+
string = '[Z] Z [Z]'
142144
expect(dayjs().format(string)).toBe(moment().format(string))
143145
})
144146

test/locale/keys.test.js

+30-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import path from 'path'
33
import dayjs from '../../src'
44

55
const localeDir = '../../src/locale'
6-
const L = []
6+
const Locale = []
77

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

1515
it('Locale keys', () => {
16-
L.forEach((l) => {
16+
Locale.forEach((locale) => {
1717
const {
1818
name,
1919
ordinal,
@@ -25,7 +25,7 @@ it('Locale keys', () => {
2525
monthsShort,
2626
weekdaysMin,
2727
weekStart
28-
} = l
28+
} = locale
2929
expect(name).toEqual(expect.any(String))
3030
expect(weekdays).toEqual(expect.any(Array))
3131

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

4545
expect(dayjs().locale(name).$locale().name).toBe(name)
4646
if (formats) {
47-
expect(Object.keys(formats).sort()).toEqual(['L', 'LL', 'LLL', 'LLLL', 'LT', 'LTS'].sort())
47+
const {
48+
LT,
49+
LTS,
50+
L,
51+
LL,
52+
LLL,
53+
LLLL,
54+
l,
55+
ll,
56+
lll,
57+
llll,
58+
...remainingFormats
59+
} = formats
60+
expect(formats).toEqual(expect.objectContaining({
61+
L: expect.any(String),
62+
LL: expect.any(String),
63+
LLL: expect.any(String),
64+
LLLL: expect.any(String),
65+
LT: expect.any(String),
66+
LTS: expect.any(String)
67+
}))
68+
expect(Object.keys(remainingFormats).length).toEqual(0)
69+
if (l) expect(l).toEqual(expect.any(String))
70+
if (ll) expect(ll).toEqual(expect.any(String))
71+
if (lll) expect(lll).toEqual(expect.any(String))
72+
if (llll) expect(llll).toEqual(expect.any(String))
4873
}
4974
if (relativeTime) {
5075
expect(Object.keys(relativeTime).sort()).toEqual(['d', 'dd', 'future', 'h', 'hh', 'm', 'mm', 'M', 'MM',

test/plugin/localizableFormat.test.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,33 @@ afterEach(() => {
1818
it('Declares English localized formats', () => {
1919
expect(dayjs.en).toBeDefined()
2020
expect(dayjs.en.formats).toBeDefined();
21-
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
21+
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
2222
expect(dayjs.en.formats[option]).toBeDefined())
2323
})
2424

25+
it('Should not interpolate characters inside square brackets', () => {
26+
const date = new Date(0)
27+
const actualDate = dayjs(date)
28+
const expectedDate = moment(date)
29+
30+
expect(actualDate.format('[l]')).toBe('l')
31+
expect(actualDate.format('YYYY [l] YYYY')).toBe('1970 l 1970')
32+
expect(actualDate.format('l [l] l')).toBe('1/1/1970 l 1/1/1970')
33+
expect(actualDate.format('[L LL LLL LLLL]')).toBe(expectedDate.format('[L LL LLL LLLL]'))
34+
})
35+
2536
it('Recognizes localized format options', () => {
2637
const { formats } = dayjs.en
2738
const date = dayjs();
28-
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
39+
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
2940
expect(date.format(option)).toBe(date.format(formats[option])))
3041
})
3142

3243
it('Uses correct English formats', () => {
3344
const date = new Date()
3445
const actualDate = dayjs(date)
3546
const expectedDate = moment(date);
36-
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL'].forEach(option =>
47+
['LT', 'LTS', 'L', 'LL', 'LLL', 'LLLL', 'l', 'll', 'lll', 'llll'].forEach(option =>
3748
expect(actualDate.format(option)).toBe(expectedDate.format(option)))
3849
})
3950

0 commit comments

Comments
 (0)