Skip to content

Commit d8121e4

Browse files
Weizhi Liclaude
andcommitted
refactor: replace global time.Local mutation with explicit cfg.Location
Store a *time.Location on Config instead of mutating the process-global time.Local. All time.Now() call sites now use .In(cfg.Location) and helpers that need a location receive it as a parameter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 58972d1 commit d8121e4

4 files changed

Lines changed: 26 additions & 24 deletions

File tree

config.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type Config struct {
4343
MondayCutoffTime string `yaml:"monday_cutoff_time"`
4444
Timezone string `yaml:"timezone"`
4545
TeamName string `yaml:"team_name"`
46+
47+
Location *time.Location `yaml:"-"` // computed from Timezone, not from YAML
4648
}
4749

4850
func LoadConfig() Config {
@@ -168,13 +170,13 @@ func LoadConfig() Config {
168170
}
169171

170172
if strings.EqualFold(cfg.Timezone, "Local") {
171-
cfg.Timezone = time.Local.String()
173+
cfg.Location = time.Local
172174
} else {
173175
loc, err := time.LoadLocation(cfg.Timezone)
174176
if err != nil {
175177
log.Fatalf("invalid timezone '%s': %v", cfg.Timezone, err)
176178
}
177-
time.Local = loc
179+
cfg.Location = loc
178180
}
179181

180182
if _, _, err := parseClock(cfg.MondayCutoffTime); err != nil {

models.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ type ReportSection struct {
3636
}
3737

3838
// CurrentWeekRange returns Monday 00:00:00 and next Monday 00:00:00 for the current calendar week.
39-
func CurrentWeekRange() (time.Time, time.Time) {
40-
now := time.Now()
39+
func CurrentWeekRange(loc *time.Location) (time.Time, time.Time) {
40+
now := time.Now().In(loc)
4141
return CurrentWeekRangeAt(now)
4242
}
4343

nudge.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func StartNudgeScheduler(cfg Config, api *slack.Client) {
5252

5353
go func() {
5454
for {
55-
now := time.Now()
55+
now := time.Now().In(cfg.Location)
5656
next := nextWeekday(now, weekday, hour, min)
5757
wait := next.Sub(now)
5858
log.Printf("Next nudge at %s (in %s)", next.Format("Mon Jan 2 15:04"), wait.Round(time.Minute))
@@ -76,7 +76,7 @@ func nextWeekday(now time.Time, day time.Weekday, hour, min int) time.Time {
7676
}
7777

7878
func sendNudges(api *slack.Client, cfg Config, memberIDs []string, reportChannelID string) {
79-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
79+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
8080
channelRef := ""
8181
if reportChannelID != "" {
8282
channelRef = fmt.Sprintf(" Please report in <#%s>.", reportChannelID)

slack.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ func handleReport(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashComm
175175
}
176176
}
177177

178-
items, parseErr := parseReportItems(reportText, author)
178+
items, parseErr := parseReportItems(reportText, author, cfg.Location)
179179
if parseErr != nil {
180180
postEphemeral(api, cmd, parseErr.Error())
181181
log.Printf("report parse error user=%s: %v", cmd.UserID, parseErr)
@@ -198,7 +198,7 @@ func handleReport(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashComm
198198
}
199199
}
200200

201-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
201+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
202202
weekItems, err := GetSlackItemsByAuthorAndDateRange(db, author, monday, nextMonday)
203203
if err != nil {
204204
log.Printf("report weekly items lookup error user=%s author=%s: %v", cmd.UserID, author, err)
@@ -233,7 +233,7 @@ func handleReport(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashComm
233233
log.Printf("report saved user=%s author=%s count=%d", cmd.UserID, author, len(items))
234234
}
235235

236-
func parseReportItems(reportText, author string) ([]WorkItem, error) {
236+
func parseReportItems(reportText, author string, loc *time.Location) ([]WorkItem, error) {
237237
lines := strings.Split(strings.ReplaceAll(reportText, "\r\n", "\n"), "\n")
238238
trimmed := make([]string, 0, len(lines))
239239
for _, line := range lines {
@@ -261,7 +261,7 @@ func parseReportItems(reportText, author string) ([]WorkItem, error) {
261261
return nil, fmt.Errorf("Usage: /report <description> (status)")
262262
}
263263

264-
now := time.Now()
264+
now := time.Now().In(loc)
265265
items := make([]WorkItem, 0, len(trimmed))
266266
for _, line := range trimmed {
267267
status := "done"
@@ -299,7 +299,7 @@ func handleFetchMRs(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashCo
299299
return
300300
}
301301

302-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
302+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
303303
log.Printf("fetch-mrs range %s - %s", monday.Format("2006-01-02"), nextMonday.Format("2006-01-02"))
304304

305305
postEphemeral(api, cmd, fmt.Sprintf("Fetching merged MRs for %s to %s...",
@@ -339,7 +339,7 @@ func handleFetchMRs(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashCo
339339
Source: "gitlab",
340340
SourceRef: mr.WebURL,
341341
Status: mapMRStatus(mr),
342-
ReportedAt: mrReportedAt(mr),
342+
ReportedAt: mrReportedAt(mr, cfg.Location),
343343
})
344344
}
345345

@@ -382,7 +382,7 @@ func handleGenerateReport(api *slack.Client, db *sql.DB, cfg Config, cmd slack.S
382382
postEphemeral(api, cmd, fmt.Sprintf("Generating report (mode: %s)...", mode))
383383
log.Printf("generate-report mode=%s", mode)
384384

385-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
385+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
386386
items, err := GetItemsByDateRange(db, monday, nextMonday)
387387
if err != nil {
388388
postEphemeral(api, cmd, fmt.Sprintf("Error loading items: %v", err))
@@ -516,7 +516,7 @@ func handleListItems(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashC
516516
}
517517

518518
func renderListItems(api *slack.Client, db *sql.DB, cfg Config, channelID, userID string, page int) {
519-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
519+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
520520
items, err := GetItemsByDateRange(db, monday, nextMonday)
521521
if err != nil {
522522
postEphemeralTo(api, channelID, userID, fmt.Sprintf("Error: %v", err))
@@ -664,7 +664,7 @@ func handleListMissing(api *slack.Client, db *sql.DB, cfg Config, cmd slack.Slas
664664
return
665665
}
666666

667-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
667+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
668668
authors, err := GetSlackAuthorsByDateRange(db, monday, nextMonday)
669669
if err != nil {
670670
postEphemeral(api, cmd, fmt.Sprintf("Error loading items: %v", err))
@@ -891,7 +891,7 @@ func handleViewSubmission(api *slack.Client, db *sql.DB, cfg Config, cb slack.In
891891
if status == "other" {
892892
status = item.Status
893893
}
894-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
894+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
895895
if !itemInRange(item, monday, nextMonday) {
896896
return
897897
}
@@ -937,7 +937,7 @@ func deleteItemAction(api *slack.Client, db *sql.DB, cfg Config, channelID, user
937937
postEphemeralTo(api, channelID, userID, "Item not found.")
938938
return
939939
}
940-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
940+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
941941
if !itemInRange(item, monday, nextMonday) {
942942
postEphemeralTo(api, channelID, userID, "You can only modify this week's items.")
943943
return
@@ -963,7 +963,7 @@ func openEditModal(api *slack.Client, db *sql.DB, cfg Config, triggerID, channel
963963
postEphemeralTo(api, channelID, userID, "Item not found.")
964964
return
965965
}
966-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
966+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
967967
if !itemInRange(item, monday, nextMonday) {
968968
postEphemeralTo(api, channelID, userID, "You can only modify this week's items.")
969969
return
@@ -1104,7 +1104,7 @@ func openDeleteModal(api *slack.Client, db *sql.DB, cfg Config, triggerID, chann
11041104
postEphemeralTo(api, channelID, userID, "Item not found.")
11051105
return
11061106
}
1107-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
1107+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
11081108
if !itemInRange(item, monday, nextMonday) {
11091109
postEphemeralTo(api, channelID, userID, "You can only modify this week's items.")
11101110
return
@@ -1224,7 +1224,7 @@ func handleNudge(api *slack.Client, db *sql.DB, cfg Config, cmd slack.SlashComma
12241224
}
12251225

12261226
// No parameter: nudge only members who haven't reported this week.
1227-
monday, nextMonday := ReportWeekRange(cfg, time.Now())
1227+
monday, nextMonday := ReportWeekRange(cfg, time.Now().In(cfg.Location))
12281228
authors, err := GetSlackAuthorsByDateRange(db, monday, nextMonday)
12291229
if err != nil {
12301230
postEphemeral(api, cmd, fmt.Sprintf("Error loading items: %v", err))
@@ -1390,7 +1390,7 @@ func mapMRStatus(mr GitLabMR) string {
13901390
return "done"
13911391
}
13921392

1393-
func mrReportedAt(mr GitLabMR) time.Time {
1393+
func mrReportedAt(mr GitLabMR, loc *time.Location) time.Time {
13941394
if mr.State == "opened" && !mr.UpdatedAt.IsZero() {
13951395
return mr.UpdatedAt
13961396
}
@@ -1400,13 +1400,13 @@ func mrReportedAt(mr GitLabMR) time.Time {
14001400
if !mr.CreatedAt.IsZero() {
14011401
return mr.CreatedAt
14021402
}
1403-
return time.Now()
1403+
return time.Now().In(loc)
14041404
}
14051405

14061406
// --- Correction helpers ---
14071407

14081408
func loadSectionOptionsForModal(cfg Config) []sectionOption {
1409-
template, _, err := loadTemplateForGeneration(cfg.ReportOutputDir, cfg.TeamName, time.Now())
1409+
template, _, err := loadTemplateForGeneration(cfg.ReportOutputDir, cfg.TeamName, time.Now().In(cfg.Location))
14101410
if err != nil {
14111411
log.Printf("edit modal load template error (non-fatal): %v", err)
14121412
return nil
@@ -1610,7 +1610,7 @@ func handleRetrospective(api *slack.Client, db *sql.DB, cfg Config, cmd slack.Sl
16101610
return
16111611
}
16121612

1613-
fourWeeksAgo := time.Now().AddDate(0, 0, -28)
1613+
fourWeeksAgo := time.Now().In(cfg.Location).AddDate(0, 0, -28)
16141614
corrections, err := GetRecentCorrections(db, fourWeeksAgo, 200)
16151615
if err != nil {
16161616
postEphemeral(api, cmd, fmt.Sprintf("Error loading corrections: %v", err))

0 commit comments

Comments
 (0)