Skip to content

relativeTime outputs day value but month as unit (eg. 28d -> 28mo) #2956

@bjfresh

Description

@bjfresh

Describe the bug
We're customizing relativeTime from fromNow/toNow in a dayjs service we're using site-wide.

If we don't override the relativeTime config, the relativeTime outputs are correct but with more verbose output than desired.

The issue we're seeing is that once a date crosses the month threshold, it's outputting months as the unit but still outputting the day value as opposed to the month value. In a separate use case, it's outputting years as the unit while outputting the month value, eg. 15y as opposed to 15mo. Here are some example inputs / outputs:

Input / Output:

{createdAt: '2025-10-29T20:01:41.002Z', createdAgo: '6d'}
{createdAt: '2025-10-30T16:41:50.258Z', createdAgo: '5d'}
{createdAt: '2025-10-29T20:01:41.002Z', createdAgo: '6d'}
{createdAt: '2025-10-23T22:13:14.470Z', createdAgo: '12d'}
{createdAt: '2025-10-22T19:56:02.664Z', createdAgo: '13d'}
{createdAt: '2025-10-21T22:55:42.414Z', createdAgo: '14d'}
{createdAt: '2025-10-21T18:00:05.745Z', createdAgo: '14d'}
{createdAt: '2025-10-10T21:07:17.747Z', createdAgo: '25d'}
{createdAt: '2025-10-10T19:22:07.387Z', createdAgo: '25d'}
{createdAt: '2025-10-07T20:55:07.274Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:52:15.052Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:14:19.170Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:11:37.432Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:09:50.188Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:05:29.522Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T20:02:37.296Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T17:18:36.854Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T17:00:28.519Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T16:57:51.514Z', createdAgo: '28mo'}
{createdAt: '2025-10-07T16:39:52.905Z', createdAgo: '28mo'}
{createdAt: '2025-10-03T19:50:11.902Z', createdAgo: '32mo'}
{createdAt: '2025-10-03T19:43:51.780Z', createdAgo: '32mo'}
{createdAt: '2025-10-03T16:50:07.937Z', createdAgo: '32mo'}
{createdAt: '2025-10-02T20:03:07.614Z', createdAgo: '33mo'}
{createdAt: '2025-10-02T19:41:41.399Z', createdAgo: '33mo'}

dayjs config:

import dayjs from "dayjs"
import relativeTime from "dayjs/plugin/relativeTime"
import updateLocale from "dayjs/plugin/updateLocale"
import utc from "dayjs/plugin/utc"
import "dayjs/locale/en"

dayjs.extend(relativeTime)
dayjs.extend(updateLocale)
dayjs.extend(utc)

dayjs.updateLocale("en", {
	relativeTime: {
		...dayjs.Ls.en.relativeTime,
		future: "in %s",
		past: "%s ago",
		s: "%d s",
		m: "%d m",
		mm: "%d m",
		h: "%d h",
		hh: "%d h",
		d: "%d d",
		dd: "%d d",
		M: "%d mo",
		MM: "%d mo",
		y: "%d y",
		yy: "%d y",
	},
})

export { dayjs }
interface GetRelativeTimeProps {
	relativeTo?: "past" | "future"
	useSuffix?: boolean
	addSpace?: boolean
}

/**
 * Utility used to format relative datetime to now.
 * Falsy `time` values will return null.
 */
export function getRelativeTime(
	time: dayjs.Dayjs | string | number | null | undefined,
	{
		relativeTo = "past",
		useSuffix = false,
		addSpace = false,
	}: GetRelativeTimeProps = {
		relativeTo: "past",
		useSuffix: false,
		addSpace: false,
	},
) {
	if (!time) {
		return null
	}
	try {
		const date = dayjs(time).utc()

		const formattedDate =
			relativeTo === "past" ? date.fromNow(!useSuffix) : date.toNow(!useSuffix)

		return addSpace ? formattedDate : formattedDate.replace(" ", "")
	} catch {
		return null
	}
}

Expected behavior
If the # of days exceeds the 1 month threshold, the output should be 1mo (or 28 days), not 28mo

Information

  • Day.js Version 1.11.19
  • OS: MacOS
  • Browser Version 141.0.7390.77 (Official Build) (arm64)
  • Time zone: Vancouver / Pacific Time

Metadata

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