Files
social-app/docs/plans/2026-03-16-calendar-timezone-unification.md
T

10 KiB

Calendar Timezone Unification Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Eliminate calendar time mismatches by enforcing one end-to-end timezone policy across App input, Agent runtime context, tool execution, and UTC database storage.

Architecture: Keep database schema unchanged (start_at/end_at TIMESTAMPTZ + timezone) and enforce strict runtime normalization. Device timezone is injected from RunAgentInput.forwardedProps, resolved into a single effective_timezone, then written explicitly into tool arguments and persisted as event timezone while timestamps are stored in UTC. Calendar read responses include deterministic event-timezone-rendered values so frontend rendering is stable and no implicit toLocal() conversion remains.

Tech Stack: FastAPI, Pydantic v2, AgentScope runtime/tooling, Flutter (Dart), PostgreSQL TIMESTAMPTZ, pytest, Flutter test.


Chunk 1: Protocol and Backend Runtime Normalization

Task 1: Freeze protocol and timezone precedence contract

Files:

  • Modify: docs/protocols/agent/run-agent-input.md

  • Create: docs/protocols/calendar/timezone-policy.md

  • Step 1: Write protocol delta checklist in docs first

Document the exact policy:

  • event_timezone > device_timezone > profile.timezone > UTC

  • event_timezone must be present in final tool call

  • start_at/end_at must be timezone-aware

  • DB stores UTC timestamps and IANA timezone string

  • Step 2: Update RunAgentInput protocol with forwardedProps contract

Add canonical payload example:

{
  "forwardedProps": {
    "client_time": {
      "device_timezone": "America/Los_Angeles",
      "client_now_iso": "2026-03-16T09:12:33-07:00",
      "client_epoch_ms": 1773658353000
    }
  }
}
  • Step 3: Add calendar timezone policy protocol doc

Include:

  • accepted datetime formats

  • explicit error codes

  • write/read response semantics

  • DST handling rule

  • Step 4: Verify docs consistency

Run: cd backend && uv run python -m pytest tests/unit/core/agentscope/test_system_prompt.py -q Expected: PASS (no protocol-breaking prompt assumptions)

Task 2: Parse forwarded device time and compute effective timezone

Files:

  • Modify: backend/src/core/agentscope/schemas/agui_input.py

  • Modify: backend/src/core/agentscope/runtime/runner.py

  • Modify: backend/src/core/agentscope/prompts/system_prompt.py

  • Test: backend/tests/unit/core/agentscope/test_system_prompt.py

  • Step 1: Write failing tests for effective timezone resolution

Add tests covering:

  • forwarded device_timezone present -> selected

  • missing forwarded timezone -> fallback profile timezone

  • invalid forwarded timezone -> fallback profile timezone

  • Step 2: Run tests to confirm RED

Run: cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py -k timezone -v Expected: FAIL on new assertions

  • Step 3: Implement minimal runtime context extraction

Implement a typed helper in runner path to read:

  • run_input.forwarded_props.client_time.device_timezone
  • client_now_iso
  • client_epoch_ms

Compute effective_timezone using fixed precedence and pass it into build_system_prompt(...).

  • Step 4: Inject effective_timezone into ENV section

Update build_system_prompt env payload to include:

  • timezone_profile
  • timezone_device
  • timezone_effective

Update guidance sentence to resolve ambiguous time with timezone_effective.

  • Step 5: Run tests to confirm GREEN

Run: cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py -v Expected: PASS

Task 3: Remove timezone ambiguity and hidden fallbacks from calendar write

Files:

  • Modify: backend/src/core/agentscope/tools/utils/calendar_domain.py

  • Modify: backend/src/core/agentscope/tools/custom/calendar.py

  • Modify: backend/src/v1/schedule_items/schemas.py

  • Modify: backend/src/v1/schedule_items/service.py

  • Test: backend/tests/unit/core/agentscope/test_calendar_tools.py

  • Test: backend/tests/unit/v1/schedule_items/test_schemas.py

  • Test: backend/tests/unit/v1/schedule_items/test_service.py

  • Step 1: Write failing tests for forbidden naive datetime and required timezone

Add tests for:

  • naive start_at rejected

  • missing event_timezone rejected in tool path

  • parse failure does not fallback to now + 1h

  • Step 2: Run tests to confirm RED

Run: cd backend && uv run pytest tests/unit/core/agentscope/test_calendar_tools.py tests/unit/v1/schedule_items/test_schemas.py -v Expected: FAIL on new constraints

  • Step 3: Implement strict parsing and normalization

Implementation requirements:

  • parse_iso_datetime rejects naive input

  • remove default Asia/Shanghai in tool

  • remove fallback auto-generated start time

  • validate IANA timezone and normalize start_at/end_at to UTC before persistence

  • Step 4: Enforce service-level invariants

Service invariant set:

  • timezone non-empty and valid IANA

  • end_at is None or end_at >= start_at

  • Step 5: Run backend tests

Run: cd backend && uv run pytest tests/unit/core/agentscope/test_calendar_tools.py tests/unit/v1/schedule_items/test_schemas.py tests/unit/v1/schedule_items/test_service.py tests/integration/test_schedule_items_routes.py -v Expected: PASS

Task 4: Keep DB schema, add non-breaking constraint migration only

Files:

  • Create: backend/alembic/versions/20260316_000x_schedule_items_time_constraints.py

  • Test: backend/tests/integration/test_schedule_items_routes.py

  • Step 1: Write migration test expectation first

Add/extend integration assertion for invalid end_at < start_at returning 422.

  • Step 2: Run integration test to confirm RED

Run: cd backend && uv run pytest tests/integration/test_schedule_items_routes.py -k end_at -v Expected: FAIL

  • Step 3: Implement migration with CHECK only (no new columns)

Migration includes:

  • CHECK (end_at IS NULL OR end_at >= start_at)

  • Step 4: Run migration + integration test

Run: cd backend && uv run alembic upgrade head && uv run pytest tests/integration/test_schedule_items_routes.py -v Expected: PASS


Chunk 2: Frontend Deterministic Display and Agent Input Wiring

Task 5: Wire device timezone into RunAgentInput forwardedProps

Files:

  • Modify: apps/lib/features/chat/data/services/ag_ui_service.dart

  • Modify: apps/lib/features/chat/data/models/ag_ui_event.dart (only if serialization helper is needed)

  • Test: apps/test/features/chat/ag_ui_event_test.dart

  • Step 1: Write failing test for forwarded client_time payload

Assert outgoing run request contains:

  • forwardedProps.client_time.device_timezone

  • client_now_iso

  • client_epoch_ms

  • Step 2: Run test to confirm RED

Run: cd apps && flutter test test/features/chat/ag_ui_event_test.dart Expected: FAIL

  • Step 3: Implement payload injection in one place

Add a single helper to build client time context and attach it to run input requests.

  • Step 4: Run test to confirm GREEN

Run: cd apps && flutter test test/features/chat/ag_ui_event_test.dart Expected: PASS

Task 6: Remove implicit local-time rendering and render by event timezone

Files:

  • Modify: apps/lib/features/calendar/data/models/schedule_item_model.dart

  • Modify: apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart

  • Modify: apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart

  • Modify: apps/lib/features/calendar/ui/screens/calendar_month_screen.dart

  • Modify: apps/lib/features/messages/ui/widgets/calendar_message_card.dart

  • Modify: apps/lib/features/calendar/ui/widgets/create_event_sheet.dart

  • Test: apps/test/features/calendar/ui/calendar_time_utils_test.dart

  • Test: apps/test/features/calendar/ui/create_event_sheet_time_align_test.dart

  • Step 1: Write failing tests for timezone-specific rendering

Cover cases:

  • same UTC event shows different local clock time under different event.timezone

  • list/day/week/month are consistent for one event

  • create sheet sends explicit timezone in payload

  • Step 2: Run tests to confirm RED

Run: cd apps && flutter test test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart Expected: FAIL

  • Step 3: Implement deterministic time conversion utility

Implement one utility used by all calendar UI surfaces:

  • input: UTC datetime + IANA timezone
  • output: event-local datetime

Replace direct .toLocal() usage in calendar model/view with this utility.

  • Step 4: Enforce explicit timezone on create/update payload

Create/update must always include timezone field from selected event timezone.

  • Step 5: Run Flutter tests

Run: cd apps && flutter test test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart Expected: PASS

Task 7: End-to-end verification matrix and release checklist

Files:

  • Modify: docs/plans/timezone-e2e-checklist.md

  • Test: backend/tests/integration/test_schedule_items_routes.py

  • Test: apps/test/features/calendar/ui/create_event_sheet_time_align_test.dart

  • Step 1: Add reproducible matrix

Matrix axes:

  • device timezone: America/Los_Angeles, Asia/Shanghai

  • profile timezone: Asia/Shanghai, Europe/Paris

  • explicit event timezone: Asia/Tokyo

  • Step 2: Run backend + frontend verification commands

Run:

  • cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py tests/unit/core/agentscope/test_calendar_tools.py tests/integration/test_schedule_items_routes.py -v
  • cd apps && flutter test test/features/chat/ag_ui_event_test.dart test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart

Expected: all PASS

  • Step 3: Manual scenario check

Manual script:

  1. device timezone set to Los Angeles
  2. profile timezone set to Shanghai
  3. ask agent create "明天上午9点开会"
  4. verify assistant text, tool card, DB UTC value, and calendar detail all align to chosen event timezone semantics
  • Step 4: Capture release notes

Record:

  • removed hidden timezone defaults
  • deterministic precedence
  • no schema expansion

Plan complete and saved to docs/superpowers/plans/2026-03-16-calendar-timezone-unification.md. Ready to execute?