

















@@ -120,6 +120,20 @@ function captureStderrWrites() {
120120return writes;
121121}
122122123+async function withTimeZone<T>(timeZone: string, run: () => Promise<T> | T): Promise<T> {
124+const previous = process.env.TZ;
125+process.env.TZ = timeZone;
126+try {
127+return await run();
128+} finally {
129+if (previous === undefined) {
130+delete process.env.TZ;
131+} else {
132+process.env.TZ = previous;
133+}
134+}
135+}
136+123137describe("logs cli", () => {
124138beforeEach(() => {
125139readSystemdServiceRuntime.mockResolvedValue({ status: "stopped" });
@@ -197,29 +211,73 @@ describe("logs cli", () => {
197211);
198212});
199213200-it("wires --local-time through CLI parsing and emits local timestamps", async () => {
201-callGatewayFromCli.mockResolvedValueOnce({
202-file: "/tmp/openclaw.log",
203-lines: [
204-JSON.stringify({
205-time: "2025-01-01T12:00:00.000Z",
206-_meta: { logLevelName: "INFO", name: JSON.stringify({ subsystem: "gateway" }) },
207-0: "line one",
208-}),
209-],
214+it("emits local timestamps by default", async () => {
215+await withTimeZone("America/New_York", async () => {
216+callGatewayFromCli.mockResolvedValueOnce({
217+file: "/tmp/openclaw.log",
218+lines: [
219+JSON.stringify({
220+time: "2025-01-01T12:00:00.000Z",
221+_meta: { logLevelName: "INFO", name: JSON.stringify({ subsystem: "gateway" }) },
222+0: "line one",
223+}),
224+],
225+});
226+227+const stdoutWrites = captureStdoutWrites();
228+229+await runLogsCli(["logs", "--plain"]);
230+231+const output = stdoutWrites.join("");
232+expect(output).toContain("line one");
233+expect(output).toContain("2025-01-01T07:00:00.000-05:00");
210234});
235+});
211236212-const stdoutWrites = captureStdoutWrites();
237+it("keeps --local-time accepted as the compatibility spelling", async () => {
238+await withTimeZone("America/New_York", async () => {
239+callGatewayFromCli.mockResolvedValueOnce({
240+file: "/tmp/openclaw.log",
241+lines: [
242+JSON.stringify({
243+time: "2025-01-01T12:00:00.000Z",
244+_meta: { logLevelName: "INFO", name: JSON.stringify({ subsystem: "gateway" }) },
245+0: "line one",
246+}),
247+],
248+});
249+250+const stdoutWrites = captureStdoutWrites();
213251214-await runLogsCli(["logs", "--local-time", "--plain"]);
252+ await runLogsCli(["logs", "--local-time", "--plain"]);
215253216-const output = stdoutWrites.join("");
217-expect(output).toContain("line one");
218-const timestamp = output.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?/u)?.[0];
219-if (timestamp === undefined) {
220-throw new Error("expected local timestamp in logs output");
221-}
222-expect(timestamp.endsWith("Z")).toBe(false);
254+const output = stdoutWrites.join("");
255+expect(output).toContain("line one");
256+expect(output).toContain("2025-01-01T07:00:00.000-05:00");
257+});
258+});
259+260+it("wires --utc through CLI parsing and emits UTC timestamps", async () => {
261+await withTimeZone("America/New_York", async () => {
262+callGatewayFromCli.mockResolvedValueOnce({
263+file: "/tmp/openclaw.log",
264+lines: [
265+JSON.stringify({
266+time: "2025-01-01T12:00:00.000Z",
267+_meta: { logLevelName: "INFO", name: JSON.stringify({ subsystem: "gateway" }) },
268+0: "line one",
269+}),
270+],
271+});
272+273+const stdoutWrites = captureStdoutWrites();
274+275+await runLogsCli(["logs", "--utc", "--plain"]);
276+277+const output = stdoutWrites.join("");
278+expect(output).toContain("line one");
279+expect(output).toContain("2025-01-01T12:00:00.000Z");
280+});
223281});
224282225283it("warns when the output pipe closes", async () => {
@@ -632,30 +690,42 @@ describe("logs cli", () => {
632690});
633691634692describe("formatLogTimestamp", () => {
635-it("formats UTC timestamp in plain mode by default", () => {
636-const result = formatLogTimestamp("2025-01-01T12:00:00.000Z");
693+it("formats local timestamp in plain mode by default", async () => {
694+await withTimeZone("America/New_York", () => {
695+const result = formatLogTimestamp("2025-01-01T12:00:00.000Z");
696+expect(result).toBe("2025-01-01T07:00:00.000-05:00");
697+});
698+});
699+700+it("formats local timestamp in pretty mode by default", async () => {
701+await withTimeZone("America/New_York", () => {
702+const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty");
703+expect(result).toBe("07:00:00-05:00");
704+});
705+});
706+707+it("formats UTC timestamp in plain mode when localTime is false", () => {
708+const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "plain", false);
637709expect(result).toBe("2025-01-01T12:00:00.000Z");
638710});
639711640-it("formats UTC timestamp in pretty mode", () => {
641-const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty");
712+it("formats UTC timestamp in pretty mode when localTime is false", () => {
713+const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty", false);
642714expect(result).toBe("12:00:00+00:00");
643715});
644716645-it("formats local time in plain mode when localTime is true", () => {
646-const utcTime = "2025-01-01T12:00:00.000Z";
647-const result = formatLogTimestamp(utcTime, "plain", true);
648-// Should be local time with explicit timezone offset (not 'Z' suffix).
649-expect(result).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}$/);
650-// The exact time depends on timezone, but should be different from UTC
651-expect(result).not.toBe(utcTime);
717+it("formats local time in plain mode when localTime is true", async () => {
718+await withTimeZone("America/New_York", () => {
719+const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "plain", true);
720+expect(result).toBe("2025-01-01T07:00:00.000-05:00");
721+});
652722});
653723654-it("formats local time in pretty mode when localTime is true", () => {
655-const utcTime = "2025-01-01T12:00:00.000Z";
656-const result = formatLogTimestamp(utcTime, "pretty", true);
657-// Should be HH:MM:SS±HH:MM format with timezone offset.
658-expect(result).toMatch(/^\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/);
724+it("formats local time in pretty mode when localTime is true", async () => {
725+await withTimeZone("America/New_York", () => {
726+ const result = formatLogTimestamp("2025-01-01T12:00:00.000Z", "pretty", true);
727+ expect(result).toBe("07:00:00-05:00");
728+});
659729});
660730661731it.each([
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。