Skip to content

Commit 2a45c24

Browse files
feat: indicate timezone in sf nodes ls times (#243)
* feat: add timezone indicator to nodes list START/END column header - Add timezone detection using dayjs timezone plugin - Display user's local timezone abbreviation (e.g., PST, EST) in START/END column - Use yellow color for timezone indicator to distinguish from other column headers - Apply to both table view and verbose VM History view - Show UTC when in UTC timezone, local timezone otherwise Co-authored-by: Daniel Tao <danieltaox@gmail.com> * chore: remove package-lock.json (using bun) Co-authored-by: Daniel Tao <danieltaox@gmail.com> * refactor: move getTimezoneAbbreviation function to utils and update return value - Moved the getTimezoneAbbreviation function from list.tsx to utils.ts for better modularity. - Updated the function to return null instead of "UTC" when unable to determine the timezone. - Adjusted the display logic in the VM table to conditionally render the timezone abbreviation. * style(web): run biome --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent fb18921 commit 2a45c24

2 files changed

Lines changed: 53 additions & 1 deletion

File tree

src/lib/nodes/list.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
DEFAULT_NODE_LS_LIMIT,
2424
getLastVM,
2525
getStatusColor,
26+
getTimezoneAbbreviation,
2627
getVMStatusColor,
2728
jsonOption,
2829
pluralizeNodes,
@@ -49,6 +50,9 @@ function VMTable({ vms }: { vms: NonNullable<SFCNodes.Node["vms"]>["data"] }) {
4950
const vmsToShow = sortedVms.slice(0, 5);
5051
const remainingVms = sortedVms.length - 5;
5152

53+
// Get timezone abbreviation for the header
54+
const timezoneAbbr = getTimezoneAbbreviation();
55+
5256
return (
5357
<Box flexDirection="column" padding={0}>
5458
{/* Build table as columns */}
@@ -143,10 +147,16 @@ function VMTable({ vms }: { vms: NonNullable<SFCNodes.Node["vms"]>["data"] }) {
143147
borderLeft={false}
144148
borderRight={false}
145149
borderColor="gray"
150+
gap={1}
146151
>
147152
<Text bold color="cyan">
148153
Start/End
149154
</Text>
155+
{timezoneAbbr && (
156+
<Text bold color="white">
157+
({timezoneAbbr})
158+
</Text>
159+
)}
150160
</Box>
151161
{vmsToShow.map((vm) => {
152162
const startDate = vm.start_at ? dayjs.unix(vm.start_at) : null;

src/lib/nodes/utils.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,49 @@ import chalk from "chalk";
44
import { parseDate } from "chrono-node";
55
import Table from "cli-table3";
66
import dayjs from "dayjs";
7+
import timezone from "dayjs/plugin/timezone.js";
8+
import utc from "dayjs/plugin/utc.js";
79
import { parseDurationArgument } from "../../helpers/duration.ts";
810
import { logAndQuit } from "../../helpers/errors.ts";
911
import { formatNullableDateRange } from "../../helpers/format-date.ts";
1012
import { parseStartDateOrNow } from "../../helpers/units.ts";
1113

14+
dayjs.extend(utc);
15+
dayjs.extend(timezone);
16+
17+
/**
18+
* Get the timezone abbreviation for display purposes
19+
* @param useUTC - If true, return "UTC" instead of local timezone
20+
* @returns Timezone abbreviation (e.g., "PST", "EST", "UTC")
21+
*/
22+
export function getTimezoneAbbreviation(useUTC = false) {
23+
if (useUTC) {
24+
return "UTC";
25+
}
26+
27+
try {
28+
// Get the user's local timezone
29+
const userTimezone = dayjs.tz.guess();
30+
31+
// Use Intl.DateTimeFormat to get the timezone abbreviation
32+
// This is more reliable than dayjs.format("z") in Node.js
33+
const dateFormatter = new Intl.DateTimeFormat("en-US", {
34+
timeZone: userTimezone,
35+
timeZoneName: "short",
36+
});
37+
const parts = dateFormatter.formatToParts(new Date());
38+
const timeZonePart = parts.find((part) => part.type === "timeZoneName");
39+
40+
if (timeZonePart?.value) {
41+
return timeZonePart.value;
42+
}
43+
} catch {
44+
// Fall through to return UTC
45+
}
46+
47+
return null;
48+
}
49+
1250
export function printNodeStatus(status: SFCNodes.Node["status"]): string {
1351
switch (status) {
1452
case "awaitingcapacity":
@@ -103,6 +141,9 @@ export function createNodesTable(
103141
nodes: SFCNodes.Node[],
104142
limit: number = DEFAULT_NODE_LS_LIMIT,
105143
): string {
144+
// Get timezone abbreviation for the header
145+
const timezoneAbbr = getTimezoneAbbreviation();
146+
106147
const table = new Table({
107148
head: [
108149
chalk.cyan("NAME"),
@@ -111,7 +152,8 @@ export function createNodesTable(
111152
chalk.cyan("CURRENT VM"),
112153
chalk.cyan("GPU"),
113154
chalk.cyan("ZONE"),
114-
chalk.cyan("START/END"),
155+
chalk.cyan("START/END") +
156+
(timezoneAbbr ? ` ${chalk.white(`(${timezoneAbbr})`)}` : ""),
115157
chalk.cyan("MAX PRICE"),
116158
],
117159
style: {

0 commit comments

Comments
 (0)