Skip to content

No platform object carries a timezone, so every app that computes a date boundary has to invent one — and each will invent it differently #14238

Description

@os-warren

Blocked-by: #15161discharged 2026-09-05: the write-path enforcement landed on origin/main (fa125f3bf), measured by content; see the discharge comment on this card.

A capability gap rather than a defect. Raised from building a recurring-obligation application (objectstack-ai/duly) on published @objectstack/* 17.2.0, where it turned a product decision into a platform-shaped one.

The gap

Neither sys_organization, sys_business_unit, nor sys_user carries a timezone, locale, region or country column. Measured on a live 17.2.0 boot — scanning each object's columns for anything matching time|zone|locale|lang|region|country|tz returns nothing on all three.

So an application that has to answer "when does this day end?" has nowhere to resolve it from, and must add a field of its own to whichever object it decides owns the answer.

Why that is a platform question and not an app one

A date boundary is not a display concern — it decides which record exists, not just how one is shown:

  • a monthly obligation "due on the 5th" expires at midnight; in UTC+8 that is 08:00 local, so work finished at 09:00 on the 5th is recorded as late;
  • a weekly period boundary shifts by the same offset, so Monday-morning work before the offset falls into the previous week's bucket — the record it should belong to does not exist yet;
  • anything built on those two — completion rates, escalation timing, "my week" — inherits both errors, silently, with nothing to report.

Only a deployment sitting on UTC is correct today.

Every app with periods, deadlines, SLAs, rosters, or billing cycles hits this, and each one solves it privately: one puts a timezone field on its own tenant object, another on its own team object, a third on the user. Two apps in the same deployment then disagree about when Tuesday ended, and neither is wrong by its own metadata.

What would close it

The organization hierarchy is the natural home, because it is the axis the platform already models and the one that matches how the answer is actually determined — work happens at a place, and the deadline usually belongs to that place's regulator or shift calendar:

  • a timezone on sys_business_unit, nullable, inheriting down the parent_business_unit_id chain;
  • a tenant-level default on sys_organization as the root of that chain;
  • ideally a resolver an app can call — "the effective zone for this record" — so apps do not each re-implement the walk up the hierarchy plus the fallbacks.

sys_user is the wrong home on its own: two people in different zones owning work in the same period would compute different period boundaries for what the business considers one period, so a shared deadline stops being shared. A per-user zone is a display preference on top of an org-resolved boundary, not a substitute for it.

Application impact

In the app that raised this, the product decision — which object owns a duty's timezone — has been deliberately parked rather than answered, because answering it means inventing a field the platform may well grow in a different place, and a migration off a privately-invented duly_* timezone field later is worse than waiting. Every deployment is UTC in the meantime, which is correct for exactly one set of customers.

Recorded app-side as objectstack-ai/duly#26.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions