2 커밋 140f27fca6 ... f86423a9b9

작성자 SHA1 메시지 날짜
  Paul Klumpp f86423a9b9 cli: configurable date formats (chatto + CLI), ISO for JSON/MCP; tests & docs 2 일 전
  Paul Klumpp 140f27fca6 cli: configurable date formats (chatto + CLI), ISO for JSON/MCP; tests & docs 2 일 전
4개의 변경된 파일과 35개의 추가작업 그리고 5개의 파일을 삭제
  1. 5 1
      .env.example
  2. 5 3
      cmd/cheapo/chatto.go
  3. 2 1
      cmd/cheapo/main.go
  4. 23 0
      cmd/cheapo/tz.go

+ 5 - 1
.env.example

@@ -15,6 +15,8 @@ CHATTO_TOKEN="<token>"               # set by the operator
 # Reference: https://pkg.go.dev/time#Time.Format
 # CHEAPO_CHATTO_OUTPUT_DATE_FORMAT=2006-01-02
 CHEAPO_CHATTO_OUTPUT_DATE_FORMAT=02.01.2006
+# Timezone for chatto-send dates (default: system local time). Examples: Europe/Berlin, UTC, America/New_York
+# CHEAPO_CHATTO_OUTPUT_TIMEZONE=Europe/Berlin
 
 # Date format for CLI 'Free until:' output (Go time layout). Default: RFC3339 (ISO, unambiguous).
 # JSON output and MCP always use ISO-8601 regardless of this setting.
@@ -30,4 +32,6 @@ CHEAPO_CHATTO_OUTPUT_DATE_FORMAT=02.01.2006
 #   2006-01-02T15:04:05Z07:00     -> 2026-12-31T23:59:59Z
 # Default (ISO-8601 / RFC3339): 2006-01-02T15:04:05Z07:00
 # CHEAPO_CLI_OUTPUT_DATE_FORMAT=2006-01-02
-# CHEAPO_CLI_OUTPUT_DATE_FORMAT=2006-01-02T15:04:05Z07:00
+# CHEAPO_CLI_OUTPUT_DATE_FORMAT=2006-01-02T15:04:05Z07:00
+# Timezone for CLI 'Free until:' (default: UTC). Examples: Europe/Berlin, UTC, America/New_York
+# CHEAPO_CLI_OUTPUT_TIMEZONE=Europe/Berlin

+ 5 - 3
cmd/cheapo/chatto.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"strings"
+	"time"
 
 	"github.com/joho/godotenv"
 	"github.com/spf13/cobra"
@@ -63,6 +64,7 @@ func formatGames(games []game.Game, mode string) string {
 	// One table per store keeps columns uniform (dates only for epic).
 	b.WriteString("| Game | Store | Price | Period |\n")
 	b.WriteString("|---|---|---|---|\n")
+	loc := outputLocation("CHEAPO_CHATTO_OUTPUT_TIMEZONE", time.Local)
 	for _, g := range games {
 		dateFmt := os.Getenv("CHEAPO_CHATTO_OUTPUT_DATE_FORMAT")
 		if dateFmt == "" {
@@ -71,10 +73,10 @@ func formatGames(games []game.Game, mode string) string {
 		period := "—"
 		if g.Kind == "upcoming" && !g.StartDate.IsZero() && !g.EndDate.IsZero() {
 			period = fmt.Sprintf("from %s, until %s",
-				g.StartDate.Local().Format(dateFmt),
-				g.EndDate.Local().Format(dateFmt))
+				g.StartDate.In(loc).Format(dateFmt),
+				g.EndDate.In(loc).Format(dateFmt))
 		} else if !g.EndDate.IsZero() {
-			period = "until " + g.EndDate.Local().Format(dateFmt)
+			period = "until " + g.EndDate.In(loc).Format(dateFmt)
 		}
 		fmt.Fprintf(&b, "| **%s** | [%s](%s) | %s | %s |\n",
 			escapePipe(g.Title), g.Store, g.URL, priceCell(g), period)

+ 2 - 1
cmd/cheapo/main.go

@@ -122,7 +122,8 @@ func printGames(cmd *cobra.Command, games []game.Game) error {
 			if fmtStr == "" {
 				fmtStr = time.RFC3339
 			}
-			s := g.EndDate.UTC().Format(fmtStr)
+			loc := outputLocation("CHEAPO_CLI_OUTPUT_TIMEZONE", time.UTC)
+			s := g.EndDate.In(loc).Format(fmtStr)
 			fmt.Fprintf(out, "   Free until: %s\n", s)
 		}
 		fmt.Fprintf(out, "   %s\n", g.URL)

+ 23 - 0
cmd/cheapo/tz.go

@@ -0,0 +1,23 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"time"
+)
+
+// outputLocation resolves the timezone for date formatting from an environment
+// variable. An empty or invalid value falls back to fallback (so defaults stay
+// unchanged and a bad value never breaks output).
+func outputLocation(envVar string, fallback *time.Location) *time.Location {
+	tz := os.Getenv(envVar)
+	if tz == "" {
+		return fallback
+	}
+	loc, err := time.LoadLocation(tz)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "⚠ invalid timezone %q (%s): using default\n", tz, envVar)
+		return fallback
+	}
+	return loc
+}