Skip to content

Fix timezone/DST bugs in event workers using Temporal API#2143

Open
trillium wants to merge 14 commits intodevelopmentfrom
fix/temporal-timezone
Open

Fix timezone/DST bugs in event workers using Temporal API#2143
trillium wants to merge 14 commits intodevelopmentfrom
fix/temporal-timezone

Conversation

@trillium
Copy link
Copy Markdown
Member

Summary

  • Fix all timezone/DST bugs in event generation and checkin workers using the @js-temporal/polyfill API
  • Extract event time logic into backend/workers/lib/eventTime.js with 7 testable, timezone-correct functions
  • Add 8 tests covering day-of-week detection, event time generation, and checkin windows across DST transitions

Fixes #1872

Background

PR #2079 addressed OOM/batching issues but did not fix the root timezone cause: getDay(), getHours(), getFullYear(), etc. return values in the server's timezone, not America/Los_Angeles. On a UTC server, a Tuesday 7pm PST event (stored as Wednesday 3am UTC) gets getDay()=3 (Wednesday) and getHours()=3 instead of getDay()=2 (Tuesday) and getHours()=19.

PR #2116 attempted a fix using toLocaleString fake-UTC conversion, but this is a known antipattern that breaks during DST transitions. This PR supersedes that approach.

What changed

New file: backend/workers/lib/eventTime.js

  • getEventDayLA(event) — day-of-week in LA time using Temporal.ZonedDateTime
  • getTodayDayLA() — today's day-of-week in LA time
  • generateEventFromRecurring(filteredEvent, todayDate) — extracts LA time-of-day from stored UTC, combines with today's LA date. DST-safe: 7pm PST stays 7pm PDT after spring-forward.
  • checkIfSameDayLA(eventDate, referenceDate) — same-calendar-day comparison in LA time
  • isInOpenWindow(eventDate, now) — checkin opens within 30 min (plain ms arithmetic)
  • isPastCloseWindow(eventDate, now) — checkin closes after 3 hours (plain ms arithmetic)

Updated workers:

  • createRecurringEvents.js — uses getTodayDayLA(), getEventDayLA(), generateEventFromRecurring(), checkIfSameDayLA()
  • openCheckins.js — uses isInOpenWindow()
  • closeCheckins.js — uses isPastCloseWindow()

New dependency: @js-temporal/polyfill — the official TC39 polyfill. Temporal reached Stage 4; when Node ships it natively, the import changes one line.

Dependencies

This PR is based on chore/backend-esm-migration (#2134) and should be merged after #2134 lands.

PR #2116 should be closed in favor of this approach.

Test plan

  • TZ=UTC npx vitest run backend/workers/timezone.test.js — all 8 tests pass
  • Verify event generation produces correct LA times on a UTC server
  • Verify checkin open/close windows work across spring-forward and fall-back
  • QA with real recurring events to confirm correct day-of-week matching

trillium and others added 14 commits July 12, 2023 15:33
…tle caching, and commit tracking

- Python skill (verify_stale_issue.py) with issue type detection
- Bash implementations for single and batch issue verification
- Label-based scope detection (dev vs non-dev roles)
- Title consistency caching system
- Git commit history analysis for PR tracking
- Decision tree logic for issue verdict categorization
Convert all require() calls for third-party npm packages (express, mongoose, jsonwebtoken, etc.) to ESM import syntax as the first step of the CJS-to-ESM migration.
…ensions

Convert all require() calls for relative paths (./x, ../x) to ESM import syntax, adding explicit .js file extensions as required by the ESM module resolution algorithm.
Replace all module.exports assignments with ESM export syntax. Barrel files (models/index.js, controllers/index.js, middleware/index.js, validators/index.js) use named exports; individual modules use export default.
…check

Convert the CJS entry-point guard pattern to its ESM equivalent using fileURLToPath(import.meta.url), which is needed because ESM has no require.main.
…configs to .cjs

Flip the ESM switch by adding "type": "module" to backend/package.json. Rename jest.config.js and jest-mongodb-config.js to .cjs since Jest config files must remain CommonJS until the Vitest migration.
Replace Jest with Vitest: add vitest.config.js with node environment and 30s timeout, update package.json scripts (test -> vitest run, test:watch -> vitest), convert jest.setup.js to use vi.mock, and remove jest/jest-mongodb config files and dependencies.
… test files

Replace all Jest test APIs with their Vitest equivalents (jest.mock -> vi.mock, jest.fn -> vi.fn) and add explicit vitest imports (describe, it, expect, vi, etc.) to every test file.
…-test.js

Vitest does not support the done() callback pattern; remove done parameter and done() calls from all test functions, using async/await instead. Add vitest lifecycle imports (beforeAll, afterEach, afterAll) to setup-test.js.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate and Fix Incorrect Event Check-In Times in VRMS

1 participant