Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for lowercase localizable formats (l, ll, lll, llll) #546

Merged
merged 11 commits into from
Apr 1, 2019
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 @@ -89,6 +89,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 @@ -296,10 +296,7 @@ class Dayjs {
Z: zoneStr
}

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 @@ -11,7 +11,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 @@ -15,7 +15,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) =>
mariomc marked this conversation as resolved.
Show resolved Hide resolved
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