Skip to content

Performance problem: slow format() #2400

Open
@victor-homyakov

Description

Describe the bug

  • I want to render a page with a big list of some goods with some dates, e.g. expiration
  • I have code similar to
const now = dayjs();
const list = [];
for (const item of goods) {
    list.push({
        item,
        expires: now.add(item.daysLeft).format(`D MMMM YYYY`)
    });
}
// render the list

This takes too long, because every call to .format() does much of work under the hood:

dayjs/src/index.js

Lines 262 to 342 in a9d7d03

format(formatStr) {
const locale = this.$locale()
if (!this.isValid()) return locale.invalidDate || C.INVALID_DATE_STRING
const str = formatStr || C.FORMAT_DEFAULT
const zoneStr = Utils.z(this)
const { $H, $m, $M } = this
const {
weekdays, months, meridiem
} = locale
const getShort = (arr, index, full, length) => (
(arr && (arr[index] || arr(this, str))) || full[index].slice(0, length)
)
const get$H = num => (
Utils.s($H % 12 || 12, num, '0')
)
const meridiemFunc = meridiem || ((hour, minute, isLowercase) => {
const m = (hour < 12 ? 'AM' : 'PM')
return isLowercase ? m.toLowerCase() : m
})
const matches = (match) => {
switch (match) {
case 'YY':
return String(this.$y).slice(-2)
case 'YYYY':
return Utils.s(this.$y, 4, '0')
case 'M':
return $M + 1
case 'MM':
return Utils.s($M + 1, 2, '0')
case 'MMM':
return getShort(locale.monthsShort, $M, months, 3)
case 'MMMM':
return getShort(months, $M)
case 'D':
return this.$D
case 'DD':
return Utils.s(this.$D, 2, '0')
case 'd':
return String(this.$W)
case 'dd':
return getShort(locale.weekdaysMin, this.$W, weekdays, 2)
case 'ddd':
return getShort(locale.weekdaysShort, this.$W, weekdays, 3)
case 'dddd':
return weekdays[this.$W]
case 'H':
return String($H)
case 'HH':
return Utils.s($H, 2, '0')
case 'h':
return get$H(1)
case 'hh':
return get$H(2)
case 'a':
return meridiemFunc($H, $m, true)
case 'A':
return meridiemFunc($H, $m, false)
case 'm':
return String($m)
case 'mm':
return Utils.s($m, 2, '0')
case 's':
return String(this.$s)
case 'ss':
return Utils.s(this.$s, 2, '0')
case 'SSS':
return Utils.s(this.$ms, 3, '0')
case 'Z':
return zoneStr // 'ZZ' logic below
default:
break
}
return null
}
return str.replace(C.REGEX_FORMAT, (match, $1) => $1 || matches(match) || zoneStr.replace(':', '')) // 'ZZ'
}

The worst things performance-wise:

  • the local functions getShort, get$H, meridiemFunc, matches are created on every call, even if not needed
  • the same format string D MMMM YYYY is processed via str.replace(C.REGEX_FORMAT, ...) again and again on every call

Expected behavior
I'd like to have a way to preprocess/precompile format string in such a way that the following calls to format() will be very fast. E.g.

const now = dayjs();
const list = [];
const precompiledFormat = dayjs.precompileFormat(`D MMMM YYYY`);
for (const item of goods) {
    list.push({
        item,
        expires: now.add(item.daysLeft).format(precompiledFormat),
        // or maybe:
        expires: precompiledFormat(now.add(item.daysLeft))
    });
}
// render the list

Information

  • Day.js Version: 1.9.0
  • OS: Mac OS 12
  • Browser: Chrome 115

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions