Skip to content

Commit a5aea69

Browse files
authored
chore: add relative time utils (#1535)
just splitting out a chunk from another PR.... extracting some relative timestamp utils
1 parent d3cd62b commit a5aea69

File tree

3 files changed

+56
-47
lines changed

3 files changed

+56
-47
lines changed

apps/code/src/renderer/features/archive/components/ArchivedTasksView.tsx

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,39 +28,15 @@ import type { Task } from "@shared/types";
2828
import type { ArchivedTask } from "@shared/types/archive";
2929
import { useNavigationStore } from "@stores/navigationStore";
3030
import { useQuery, useQueryClient } from "@tanstack/react-query";
31+
import { formatRelativeTimeLong } from "@utils/time";
3132
import { toast } from "@utils/toast";
3233
import { useMemo, useState } from "react";
3334

3435
const BRANCH_NOT_FOUND_PATTERN = /Branch '(.+)' does not exist/;
3536

3637
function formatRelativeDate(isoDate: string | undefined): string {
3738
if (!isoDate) return "—";
38-
const date = new Date(isoDate);
39-
const now = new Date();
40-
const diffMs = now.getTime() - date.getTime();
41-
const diffSeconds = Math.floor(diffMs / 1000);
42-
const diffMinutes = Math.floor(diffSeconds / 60);
43-
const diffHours = Math.floor(diffMinutes / 60);
44-
const diffDays = Math.floor(diffHours / 24);
45-
46-
if (diffSeconds < 60) {
47-
return "just now";
48-
}
49-
if (diffMinutes < 60) {
50-
return diffMinutes === 1 ? "1 minute ago" : `${diffMinutes} minutes ago`;
51-
}
52-
if (diffHours < 24) {
53-
return diffHours === 1 ? "1 hour ago" : `${diffHours} hours ago`;
54-
}
55-
if (diffDays < 7) {
56-
return diffDays === 1 ? "1 day ago" : `${diffDays} days ago`;
57-
}
58-
59-
return date.toLocaleDateString(undefined, {
60-
month: "short",
61-
day: "numeric",
62-
year: "numeric",
63-
});
39+
return formatRelativeTimeLong(isoDate);
6440
}
6541

6642
function getRepoName(repository: string | null | undefined): string {

apps/code/src/renderer/features/sidebar/components/items/TaskItem.tsx

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
PushPin,
1313
} from "@phosphor-icons/react";
1414
import { selectIsFocusedOnWorktree, useFocusStore } from "@stores/focusStore";
15+
import { formatRelativeTimeShort } from "@utils/time";
1516
import { useCallback, useEffect, useRef, useState } from "react";
1617
import { SidebarItem } from "../SidebarItem";
1718

@@ -44,26 +45,6 @@ interface TaskItemProps {
4445
onEditCancel?: () => void;
4546
}
4647

47-
function formatRelativeTime(timestamp: number): string {
48-
const now = Date.now();
49-
const diff = now - timestamp;
50-
51-
const minutes = Math.floor(diff / (1000 * 60));
52-
const hours = Math.floor(diff / (1000 * 60 * 60));
53-
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
54-
const weeks = Math.floor(days / 7);
55-
const months = Math.floor(days / 30);
56-
const years = Math.floor(days / 365);
57-
58-
if (years > 0) return `${years}y`;
59-
if (months > 0) return `${months}mo`;
60-
if (weeks > 0) return `${weeks}w`;
61-
if (days > 0) return `${days}d`;
62-
if (hours > 0) return `${hours}h`;
63-
if (minutes > 0) return `${minutes}m`;
64-
return "now";
65-
}
66-
6748
interface TaskHoverToolbarProps {
6849
isPinned: boolean;
6950
onTogglePin?: () => void;
@@ -252,7 +233,7 @@ export function TaskItem({
252233

253234
const timestampNode = timestamp ? (
254235
<span className="shrink-0 text-[11px] text-gray-11 group-hover:hidden">
255-
{formatRelativeTime(timestamp)}
236+
{formatRelativeTimeShort(timestamp)}
256237
</span>
257238
) : null;
258239

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Format a timestamp as a short relative string (e.g. "3m", "2h", "5d").
3+
* Accepts either a Unix ms timestamp or an ISO date string.
4+
*/
5+
export function formatRelativeTimeShort(timestamp: number | string): string {
6+
const ms =
7+
typeof timestamp === "string" ? new Date(timestamp).getTime() : timestamp;
8+
const diff = Date.now() - ms;
9+
10+
const minutes = Math.floor(diff / 60_000);
11+
const hours = Math.floor(diff / 3_600_000);
12+
const days = Math.floor(diff / 86_400_000);
13+
const weeks = Math.floor(days / 7);
14+
const months = Math.floor(days / 30);
15+
const years = Math.floor(days / 365);
16+
17+
if (years > 0) return `${years}y`;
18+
if (months > 0) return `${months}mo`;
19+
if (weeks > 0) return `${weeks}w`;
20+
if (days > 0) return `${days}d`;
21+
if (hours > 0) return `${hours}h`;
22+
if (minutes > 0) return `${minutes}m`;
23+
return "now";
24+
}
25+
26+
/**
27+
* Format a timestamp as a longer relative string (e.g. "3 minutes ago", "1 day ago").
28+
* Falls back to a locale date for anything older than a week.
29+
* Accepts either a Unix ms timestamp or an ISO date string.
30+
*/
31+
export function formatRelativeTimeLong(timestamp: number | string): string {
32+
const date =
33+
typeof timestamp === "string" ? new Date(timestamp) : new Date(timestamp);
34+
const diff = Date.now() - date.getTime();
35+
36+
const seconds = Math.floor(diff / 1000);
37+
const minutes = Math.floor(seconds / 60);
38+
const hours = Math.floor(minutes / 60);
39+
const days = Math.floor(hours / 24);
40+
41+
if (seconds < 60) return "just now";
42+
if (minutes < 60)
43+
return minutes === 1 ? "1 minute ago" : `${minutes} minutes ago`;
44+
if (hours < 24) return hours === 1 ? "1 hour ago" : `${hours} hours ago`;
45+
if (days < 7) return days === 1 ? "1 day ago" : `${days} days ago`;
46+
47+
return date.toLocaleDateString(undefined, {
48+
month: "short",
49+
day: "numeric",
50+
year: "numeric",
51+
});
52+
}

0 commit comments

Comments
 (0)