diff --git a/packages/core/useTimeAgo/index.md b/packages/core/useTimeAgo/index.md index b9de5b15eff..3fe98eb6868 100644 --- a/packages/core/useTimeAgo/index.md +++ b/packages/core/useTimeAgo/index.md @@ -4,7 +4,7 @@ category: Time # useTimeAgo -Reactive time ago. +Reactive time ago. Automatically update the time ago string when the time changes. ## Usage @@ -21,3 +21,14 @@ const timeAgo = useTimeAgo(new Date(2021, 0, 1)) Time Ago: {{ timeAgo }} ``` + +## Non-Reactivity Usage + +In case you don't need the reactivity, you can use the `formatTimeAgo` function to get the formatted string instead of a Ref. + +```js +import { formatTimeAgo } from '@vueuse/core' + +const timeAgo = formatTimeAgo(new Date(2021, 0, 1)) // string +``` + diff --git a/packages/core/useTimeAgo/index.test.ts b/packages/core/useTimeAgo/index.test.ts index 862f8a233fe..a9d516c106a 100644 --- a/packages/core/useTimeAgo/index.test.ts +++ b/packages/core/useTimeAgo/index.test.ts @@ -58,7 +58,7 @@ describe('useTimeAgo', () => { }) test('get undefined when time is invalid', () => { - expect(useTimeAgo('invalid date').value).toBeUndefined() + expect(useTimeAgo('invalid date').value).toBe('') }) describe('just now', () => { diff --git a/packages/core/useTimeAgo/index.ts b/packages/core/useTimeAgo/index.ts index 72f383043b2..5b53735452a 100644 --- a/packages/core/useTimeAgo/index.ts +++ b/packages/core/useTimeAgo/index.ts @@ -12,27 +12,14 @@ export interface UseTimeAgoMessagesBuiltIn { justNow: string past: string | UseTimeAgoFormatter future: string | UseTimeAgoFormatter + invalid: string } export type UseTimeAgoMessages = UseTimeAgoMessagesBuiltIn & Record> -export interface UseTimeAgoOptions { - /** - * Expose more controls - * - * @default false - */ - controls?: Controls - - /** - * Intervals to update, set 0 to disable auto update - * - * @default 30_000 - */ - updateInterval?: number - +export interface FormatTimeAgoOptions { /** * Maximum unit (of diff in milliseconds) to display the full date instead of relative * @@ -70,7 +57,23 @@ export interface UseTimeAgoOptions[] } -interface UseTimeAgoUnit { +export interface UseTimeAgoOptions extends FormatTimeAgoOptions { + /** + * Expose more controls + * + * @default false + */ + controls?: Controls + + /** + * Intervals to update, set 0 to disable auto update + * + * @default 30_000 + */ + updateInterval?: number +} + +export interface UseTimeAgoUnit { max: number value: number name: Unit @@ -113,6 +116,7 @@ const DEFAULT_MESSAGES: UseTimeAgoMessages = { hour: n => `${n} hour${n > 1 ? 's' : ''}`, minute: n => `${n} minute${n > 1 ? 's' : ''}`, second: n => `${n} second${n > 1 ? 's' : ''}`, + invalid: '', } const DEFAULT_FORMATTER = (date: Date) => date.toISOString().slice(0, 10) @@ -130,8 +134,26 @@ export function useTimeAgo(time: MaybeComputedRef, options: UseTimeAgoOptions = {}) { const { controls: exposeControls = false, - max, updateInterval = 30_000, + } = options + + const { now, ...controls } = useNow({ interval: updateInterval, controls: true }) + const timeAgo = computed(() => foramtTimeAgo(new Date(resolveUnref(time)), options, unref(now.value))) + + if (exposeControls) { + return { + timeAgo, + ...controls, + } + } + else { + return timeAgo + } +} + +export function foramtTimeAgo(from: Date, options: FormatTimeAgoOptions = {}, now: Date | number = Date.now()): string { + const { + max, messages = DEFAULT_MESSAGES as UseTimeAgoMessages, fullDateFormatter = DEFAULT_FORMATTER, units = DEFAULT_UNITS, @@ -139,33 +161,19 @@ export function useTimeAgo +n.toFixed(rounding) : Math[rounding] - const { now, ...controls } = useNow({ interval: updateInterval, controls: true }) - - function getTimeAgo(from: Date, now: Date) { - const diff = +now - +from - const absDiff = abs(diff) - // less than a minute - if (absDiff < 60000 && !showSecond) - return messages.justNow - - if (typeof max === 'number' && absDiff > max) - return fullDateFormatter(new Date(from)) + const diff = +now - +from + const absDiff = Math.abs(diff) - if (typeof max === 'string') { - const unitMax = units.find(i => i.name === max)?.max - if (unitMax && absDiff > unitMax) - return fullDateFormatter(new Date(from)) - } + function format(diff: number, unit: UseTimeAgoUnit) { + const val = roundFn(Math.abs(diff) / unit.value) + const past = diff > 0 - for (const unit of units) { - if (absDiff < unit.max) - return format(diff, unit) - } + const str = applyFormat(unit.name as UnitNames, val, past) + return applyFormat(past ? 'past' : 'future', str, past) } function applyFormat(name: UnitNames | keyof UseTimeAgoMessagesBuiltIn, val: number | string, isPast: boolean) { @@ -175,23 +183,23 @@ export function useTimeAgo 0 + // less than a minute + if (absDiff < 60000 && !showSecond) + return messages.justNow - const str = applyFormat(unit.name as UnitNames, val, past) - return applyFormat(past ? 'past' : 'future', str, past) - } + if (typeof max === 'number' && absDiff > max) + return fullDateFormatter(new Date(from)) - const timeAgo = computed(() => getTimeAgo(new Date(resolveUnref(time)), unref(now.value))) - - if (exposeControls) { - return { - timeAgo, - ...controls, - } + if (typeof max === 'string') { + const unitMax = units.find(i => i.name === max)?.max + if (unitMax && absDiff > unitMax) + return fullDateFormatter(new Date(from)) } - else { - return timeAgo + + for (const unit of units) { + if (absDiff < unit.max) + return format(diff, unit) } + + return messages.invalid }