Skip to content

Timezone & Time Display Plan (Admin + Widget)

This document defines the implementation plan to make date/time display consistent with the merchant’s timezone across admin screens and storefront chat widget.

  • Current server-side formatting uses date-fns with no timezone conversion, so timestamps render in server/runtime timezone (often UTC).
  • Merchants in other regions (for example Pakistan, UTC+5) see shifted times in screens like app/chat-logs.
  • We need timezone-aware display that:
    • auto-detects merchant/browser timezone on first use,
    • lets merchant override from Settings dropdown,
    • applies consistently across admin pages and frontend widget.
  • Show times in merchant-selected timezone by default.
  • Auto-capture timezone so most merchants do not need manual setup.
  • Allow manual override/change anytime in Settings.
  • Keep all DB timestamps in UTC (storage unchanged); convert only at display boundaries.
  • Keep behavior fail-safe: if timezone is missing/invalid, fall back to UTC or browser local in widget.

Add fields to Shop:

  • timeZone (time_zone, string, nullable initially, later defaulted)
    • IANA timezone identifier, e.g. Asia/Karachi, America/New_York.
  • timeZoneSource (time_zone_source, string, nullable)
    • expected values: auto, manual, shopify, unknown (optional but useful for debugging/support).
  • timeZoneUpdatedAt (time_zone_updated_at, datetime, nullable)
    • optional telemetry for when timezone last changed.

Notes:

  • Do not change existing createdAt/updatedAt semantics.
  • UTC remains canonical storage for all event timestamps.

Priority order at runtime:

  1. shops.timeZone (store-level value saved from Settings).
  2. Browser timezone (Intl.DateTimeFormat().resolvedOptions().timeZone) for first-time bootstrap only.
  3. Shopify shop timezone (fallback bootstrap source only when browser timezone is unavailable).
  4. UTC fallback.

5) Settings Screen UX Plan (app/routes/app.settings.jsx)

Section titled “5) Settings Screen UX Plan (app/routes/app.settings.jsx)”

Add a new Timezone section:

  • Dropdown/select labeled Timezone.
  • Options are IANA timezone list (grouped by region if possible).
  • Helper text: “Used for all date/time shown in admin and storefront chat widget.”
  • “Auto-detect from this browser” action:
    • Captures browser timezone and submits as new value.
  • Save behavior:
    • Persist selected value to shops.timeZone.
    • Set timeZoneSource = manual when chosen from dropdown.
    • Set timeZoneSource = auto when auto-detect action is used.

6) Auto-Capture on First Install / First Open

Section titled “6) Auto-Capture on First Install / First Open”

Two-stage approach:

  1. Primary bootstrap source: browser timezone (client side):
    • On first Settings page load (or app shell mount), detect browser timezone.
    • If DB value is empty, POST a lightweight endpoint to set timezone automatically.
  2. Fallback bootstrap source: Shopify timezone (server side):
    • During afterAuth, use Shopify timezone only if shops.timeZone is empty and browser bootstrap has not populated it.
  3. No silent overrides after initial bootstrap:
    • Once shops.timeZone is set, it must change only through explicit save in Settings.

Why both:

  • Browser-first better matches the actual admin using the app.
  • Shopify fallback covers cases where browser detection is unavailable.

Current formatter (app/lib/date-format.server.js) is timezone-naive.

Refactor to support timezone-aware format:

  • Introduce helper signatures such as:
    • formatDate(date, { timeZone, locale })
    • formatDateTime(date, { timeZone, locale })
  • Use Intl.DateTimeFormat with timeZone for timezone correctness.
  • Keep existing env format tokens optional for legacy compatibility, or migrate fully to Intl presets.

Recommendation:

  • Move to Intl-based formatting for timezone correctness and avoid token/timezone mismatch complexity.

Apply timezone-aware formatting to all pages that show times, including:

  • app/routes/app.chat-logs.jsx (thread and message times).
  • app/routes/app.data-sync.jsx (last synced timestamps).
  • app/routes/app.billing.jsx and related billing routes.
  • app/routes/app._index.jsx dashboard summaries.
  • app/routes/app.support.jsx (tickets, message timestamps, feature request timestamps).
  • app/routes/app.promo-send.jsx (recent send activity timestamps).
  • app/routes/app.promo-codes.jsx (created/claimed timestamps).
  • other pages using formatDate / formatDateTime.

Implementation pattern:

  • Each loader reads shop timezone once.
  • Pass timezone to formatter in serialization phase.
  • Return formatted strings ready for UI.
  • Replace direct new Date(...).toLocaleString() usage in admin routes with shared timezone formatter helpers so store timezone is applied consistently.

9) Storefront Widget Plan (extensions/appifire-chat/assets/chat-widget.js)

Section titled “9) Storefront Widget Plan (extensions/appifire-chat/assets/chat-widget.js)”

Widget time labels are currently generated client-side.

Plan:

  • Widget time display uses visitor local timezone (browser timezone), not store timezone.
  • Keep store timezone for admin-side timestamps only.
  • Ensure message timestamps and any future date labels in widget use one shared client helper based on visitor browser timezone.

Update GET /api/widget-settings payload to include:

  • timeZone (nullable string).

Optional:

  • timeZoneSource for debugging (not required for widget rendering).
  • Accept only valid IANA timezone IDs.
    • Validate against Intl.supportedValuesOf("timeZone") (Node/browser support permitting) or trusted timezone package list.
  • Reject invalid values in app.settings action and auto-detect endpoint.
  • Keep DB update idempotent.
  • Existing shops with null timezone:
    • continue working with UTC/browser fallback until first save/auto-capture.
  • No migration to recompute historical data needed (format at read-time only).

Functional:

  • New install: timezone auto-populates.
  • Existing shop with null timezone: first settings open auto-saves timezone.
  • Manual change in settings updates all relevant pages after refresh.
  • Chat logs show expected local time offset.
  • Widget timestamps match selected timezone.

Edge cases:

  • Invalid timezone submission rejected cleanly.
  • DST transition dates render correctly.
  • Missing timezone falls back safely.

Regression:

  • Date rendering still works in all routes currently using formatDateTime.
  • No break in sync/billing/chat logs loaders.
  1. Add migration + schema fields.
  2. Add settings UI and save logic.
  3. Add optional auto-detect endpoint/client trigger.
  4. Refactor date formatting helpers to timezone-aware API.
  5. Update admin routes incrementally.
  6. Expose timezone via widget settings API and update widget formatter.
  7. QA with multiple timezone test stores.

15) Open Questions (Need Product Decision)

Section titled “15) Open Questions (Need Product Decision)”

Resolved decisions:

  1. Default source preference: Browser timezone first, then Shopify timezone fallback.
  2. Admin scope: Store-level shared timezone for all admins.
  3. Widget behavior: Visitor local timezone.
  4. Display format: Keep current format (d MMM yyyy h:mm a and existing date format behavior).
  5. Auto-update policy: After initial bootstrap, timezone changes only on explicit Settings save.