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

Performance problem: slow format() #2400

Open
victor-homyakov opened this issue Aug 3, 2023 · 1 comment
Open

Performance problem: slow format() #2400

victor-homyakov opened this issue Aug 3, 2023 · 1 comment

Comments

@victor-homyakov
Copy link

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
@fernandodevelon
Copy link
Contributor

this pull request solves the problem #2313

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants