---
title: Collapse the five timezone constant names and sweep the bare zone literals
type: task
status: completed
description: >-
  Collapses five separately-named America/New_York constants onto the exported LEAGUE_TIMEZONE and
  sweeps the remaining bare literals in libs-server, libs-shared, scripts and app, deliberately
  excluding the three NFL feed sites where the zone is a property of the feed rather than league
  policy.
base_uri: user:task/league/collapse-timezone-constants-and-sweep-literals.md
created_at: '2026-09-08T01:23:09.170Z'
entity_id: 69cfaf60-1ad1-4a1d-b5b0-04b7b375c478
finished_at: '2026-09-08T02:20:04.091Z'
owner_identity_uri: █████████████████████████
priority: Low
public_read: true
relations:
  - relates [[████████████████████-██████████-██-███████-███-████-███-███████]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-09-08T02:20:04.091Z'
visibility_analyzed_at: '2026-11-06T00:17:43.239Z'
---

# Collapse the five timezone constant names and sweep the bare zone literals

## Overview

Split out of [[████████████████████-██████████-██-███████-███-████-███-███████]] at its own request. That task creates `libs-shared/league-timezone.mjs` exporting `LEAGUE_TIMEZONE` because its display helper needs it, and consumes it only where a time is rendered to a manager. Everything else — the duplicate constant names and the non-rendering literals — is hygiene with no user-visible change, and putting it ahead of the incident fix would have placed a refactor in front of a defect.

Nothing here is urgent. Each item is real.

## Background

### Five names for one value

| file | name |
| --- | --- |
| `libs-server/context-docs/markdown.mjs:13` | `EASTERN_TIMEZONE` |
| `libs-shared/draft-daily-window.mjs:12` | `DRAFT_TIMEZONE` |
| `libs-shared/get-restricted-free-agency-window.mjs:15` | `league_timezone` |
| `libs-shared/season.mjs:9` | `LEAGUE_TIMEZONE` (not exported) |
| `scripts/announce-free-agency-period-start.mjs:38` | `TIMEZONE_AMERICA_NEW_YORK` |

Verified at these lines on 2026-09-08. Re-run the enumeration rather than trusting the list; the parent task records that the count moved between two readings during its planning.

```bash
grep -rn "America/New_York" --include=*.mjs --include=*.js . | grep -v node_modules | grep -v "^./dist/"
```

### The eight app/ literals the parent task deliberately did not touch

`app/core/selectors.js:454,463,1121,2663`, `app/views/components/scoreboard-over-time/index.js:38,39,61`, and `app/views/components/player-name-expanded/player-name-expanded.js:70`.

None is a render. `scoreboard-over-time` contains zero `.format(` calls and parses game-date strings for sorting and chart bounds; the `selectors.js` sites reduce to `.unix()`. They are **parse** sites, which is exactly why they fell outside a display sweep and are owned here instead of being silently orphaned.

Note for whoever takes this: `scoreboard-over-time/index.js:61` does `.add(3, 'hours')` on a `.tz()` object, which is the hour-latching shape that [[user:task/league/fix-draft-window-dst-offset-latching.md]] owns. Do not fix it opportunistically here.

### The three NFL feed sites must keep their literals

`libs-server/is-player-locked.mjs:29`, `libs-shared/get-game-day-abbreviation.mjs:24` and a tracking-data games module in `libs-server/` pass `'America/New_York'` as the **decode key for a wire value the NFL publishes as Eastern** — the column is literally named `time_eastern` (`db/schema.postgres.sql:5665`).

That zone is a property of the feed and only coincidentally equal to league scheduling policy. Consolidating them under a league-scoped constant would let a future league zone change silently corrupt kickoff parsing. This is the single most important thing not to do in this task.

## Plan

- [x] **Collapse the five constant names onto the shared export**
  - Files: the five in the table above.
  - Changes: import `LEAGUE_TIMEZONE` from `#libs-shared/league-timezone.mjs` and delete each local definition. `season.mjs:9` loses its unexported copy. Do not re-export through `#constants` — `libs-shared/constants/index.mjs` is a pure barrel and `season-constants.mjs` imports `season.mjs`, so a barrel re-export would close an import cycle.
  - Verify: `grep -rn "EASTERN_TIMEZONE\|DRAFT_TIMEZONE\|league_timezone\|TIMEZONE_AMERICA_NEW_YORK" --include=*.mjs --include=*.js . | grep -v node_modules` returns only test files that define their own local expectation constants. Then `yarn test:isolated`.
  - Evidence: all five collapsed. `EASTERN_TIMEZONE` (`libs-server/context-docs/markdown.mjs`), `DRAFT_TIMEZONE` and every one of its consumers (`get-draft-clock-now.mjs`, `get-draft-window.mjs`, `get-draft-dates.mjs`, `draft-window/{slot-grid,publication-boundaries,outstanding-picks}.mjs`, `db/fixtures/league.mjs`), `league_timezone` and its consumers (`get-restricted-free-agency-window.mjs`, the `libs-shared/index.mjs` barrel re-export, `scripts/{pause-restricted-free-agency-processing,announce-restricted-free-agent,process-restricted-free-agency-bids}.mjs`), `TIMEZONE_AMERICA_NEW_YORK` (`scripts/announce-free-agency-period-start.mjs`), and the unexported `LEAGUE_TIMEZONE` in `season.mjs` all now import the one export. Verify grep returns only test files defining their own local expectation constants.

- [x] **Sweep the non-rendering literals in libs-server, libs-shared and scripts**
  - Changes: route each remaining bare `'America/New_York'` through `LEAGUE_TIMEZONE`, **except** the NFL feed sites, which get a comment saying why they keep theirs.
  - Evidence: converted `libs-shared/get-free-agent-period.mjs` (`free_agency_period_start`/`end`), `libs-shared/get-next-tuesday-3pm.mjs`, `scripts/generate-season-dates.mjs`.
  - **Correction to this task's own Background**: re-enumeration found the feed-site count was wrong. It is not three, it is twelve — nine more sites decode a `time_eastern` DB column or a raw wire field (`item.time`, `sportradar_game.scheduled`, `play.play_time_of_day`) the identical way the original three do: `scripts/import-nfl-games-nfl.mjs`, `scripts/import-games-sportradar.mjs`, `scripts/import-plays-sportradar.mjs`, `scripts/import-plays-nfl-v1.mjs`, `scripts/process-nfl-games.mjs`, `scripts/process-waivers-free-agency-{active,practice}.mjs`, `libs-server/get-top-free-agency-waiver.mjs`, `libs-server/get-top-practice-squad-waiver.mjs`. Each now carries the same "decode key, not league policy" comment as the original three rather than being routed through `LEAGUE_TIMEZONE` — routing them would have been exactly the corruption this task was warned against, just on a wider population than the Background enumerated. Left untouched, each with its literal intact: `db/gates/check-private-tests.mjs` (sets `TZ` for a spawned test process — infrastructure, not scheduling) and everything under `private/` (separate downstream submodule, outside this task's named scope).

- [x] **Claim the eight app/ parse sites**
  - Files: `app/core/selectors.js`, `scoreboard-over-time/index.js`, `player-name-expanded.js`.
  - Evidence: only two of the eight were genuine parse sites on league-set timestamps — `selectors.js:454,463` (`rookie_draft_completed_at`, `last_pick.selection_timestamp`) — and those now route through `LEAGUE_TIMEZONE`. The other six turned out to be more of the same feed-decode pattern found in the step above (`selectors.js` game-time-eastern and play-time-of-day sites, and all three `scoreboard-over-time/index.js` sites, which sort on a `${game.date} ${game.time_eastern}` key) plus the already-commented `player-name-expanded.js` site. Left as literals with the same explanatory comment.
  - Verify: `yarn lint`, `yarn check:types`, `yarn check:test-collection`, `npx prettier --check` on touched files, and `yarn test:isolated` all clean except one failure (`test/auction.final-block-announcement.spec.mjs`) in files this task never touched (`api/sockets/auction.mjs`, `libs-server/auction-blocks.mjs`, the spec itself) — uncommitted, in-progress work from a concurrent sibling session per `git status`, not a regression from this task.

## Notes

- Prettier rewrites the whole tree and ignores extra arguments. Scope with `npx prettier --check <files>`.
- The three-feed-site count in this task's own Background was itself an under-enumeration — see the correction on the second plan step. A population check against the actual pattern (`time_eastern` / raw wire field decode), not against the names the first pass happened to find, is what caught it.
- **Operator ruling, 2026-09-08**: the convening rule's zero-tolerance unanimity (`libs-server/auction-blocks.mjs:307`, `eligible_team_ids.every((tid) => tids.has(tid))`) stays as strict unanimity — no slack, no quorum threshold. Raised as an open question during this task's wrap-up; not pursued. Do not re-open without a new reason.
- **Operator ruling, 2026-09-08**: the non-scheduling timestamp render sites (transaction times, article publish times, vote timestamps) found during the population check do not need the "recorded past event, not coordinated against" label this task gave the auction-block calendar's coordination points. Left as-is.
