[{"data":1,"prerenderedAt":806},["ShallowReactive",2],{"navigation":3,"external-navigation":214,"external_ember:\u002Fember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub":443},[4,8,201],{"title":5,"path":6,"stem":7},"Documentation","\u002F","README",{"title":9,"path":10,"stem":11,"children":12},"EOS","\u002Feos","eos\u002FREADME",[13,44,121,181],{"title":14,"collapsed":15,"path":16,"stem":17,"children":18,"page":43},"Adrs",true,"\u002Feos\u002Fadrs","eos\u002Fadrs",[19,23,27,31,35,39],{"title":20,"path":21,"stem":22},"Use Turborepo for the EOS monorepo","\u002Feos\u002Fadrs\u002F0001-use-turborepo-for-eos","eos\u002Fadrs\u002F0001-use-turborepo-for-eos",{"title":24,"path":25,"stem":26},"Use Oxc for linting and formatting","\u002Feos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting","eos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting",{"title":28,"path":29,"stem":30},"Lint Markdown and validate links in CI","\u002Feos\u002Fadrs\u002F0004-lint-markdown-in-ci","eos\u002Fadrs\u002F0004-lint-markdown-in-ci",{"title":32,"path":33,"stem":34},"Use Lefthook for repository-managed Git hooks","\u002Feos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks","eos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks",{"title":36,"path":37,"stem":38},"Use Commitlint's conventional configuration","\u002Feos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration","eos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration",{"title":40,"path":41,"stem":42},"Use Nx for EOS task execution","\u002Feos\u002Fadrs\u002F0007-use-nx-for-eos","eos\u002Fadrs\u002F0007-use-nx-for-eos",false,{"title":45,"path":46,"stem":47,"children":48},"Harbourmaster","\u002Feos\u002Fharbourmaster","eos\u002Fharbourmaster\u002FREADME",[49,58,71,96],{"title":50,"path":51,"stem":52,"children":53,"page":43},"Decisions","\u002Feos\u002Fharbourmaster\u002Fdecisions","eos\u002Fharbourmaster\u002Fdecisions",[54],{"title":55,"path":56,"stem":57},"AI-50: GitHub auth and fork pull request policy","\u002Feos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy","eos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy",{"title":59,"path":60,"stem":61,"children":62,"page":43},"Explanation","\u002Feos\u002Fharbourmaster\u002Fexplanation","eos\u002Fharbourmaster\u002Fexplanation",[63,67],{"title":64,"path":65,"stem":66},"The Harbourmaster review pipeline","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline","eos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline",{"title":68,"path":69,"stem":70},"The Harbourmaster shared-action trust boundary","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary","eos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary",{"title":72,"path":73,"stem":74,"children":75,"page":43},"How To","\u002Feos\u002Fharbourmaster\u002Fhow-to","eos\u002Fharbourmaster\u002Fhow-to",[76,80,84,88,92],{"title":77,"path":78,"stem":79},"How to add Harbourmaster reviews to a repository","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository","eos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository",{"title":81,"path":82,"stem":83},"How to build a pilot dashboard","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard","eos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard",{"title":85,"path":86,"stem":87},"How to configure model routing","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing","eos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing",{"title":89,"path":90,"stem":91},"How to request a Harbourmaster review","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review",{"title":93,"path":94,"stem":95},"How to diagnose a Harbourmaster review failure","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review",{"title":97,"path":98,"stem":99,"children":100,"page":43},"Reference","\u002Feos\u002Fharbourmaster\u002Freference","eos\u002Fharbourmaster\u002Freference",[101,105,109,113,117],{"title":102,"path":103,"stem":104},"Harbourmaster command-line interface","\u002Feos\u002Fharbourmaster\u002Freference\u002Fcli","eos\u002Fharbourmaster\u002Freference\u002Fcli",{"title":106,"path":107,"stem":108},"Harbourmaster configuration","\u002Feos\u002Fharbourmaster\u002Freference\u002Fconfiguration","eos\u002Fharbourmaster\u002Freference\u002Fconfiguration",{"title":110,"path":111,"stem":112},"Harbourmaster GitHub Action","\u002Feos\u002Fharbourmaster\u002Freference\u002Fgithub-action","eos\u002Fharbourmaster\u002Freference\u002Fgithub-action",{"title":114,"path":115,"stem":116},"Harbourmaster review behavior","\u002Feos\u002Fharbourmaster\u002Freference\u002Freview-behavior","eos\u002Fharbourmaster\u002Freference\u002Freview-behavior",{"title":118,"path":119,"stem":120},"Harbourmaster telemetry","\u002Feos\u002Fharbourmaster\u002Freference\u002Ftelemetry","eos\u002Fharbourmaster\u002Freference\u002Ftelemetry",{"title":72,"path":122,"stem":123,"children":124,"page":43},"\u002Feos\u002Fhow-to","eos\u002Fhow-to",[125,129,133,137,141,145,149,153,157,161,165,169,173,177],{"title":126,"path":127,"stem":128},"How to add an external documentation source","\u002Feos\u002Fhow-to\u002Fadd-external-documentation-source","eos\u002Fhow-to\u002Fadd-external-documentation-source",{"title":130,"path":131,"stem":132},"How to connect SDLC integrations","\u002Feos\u002Fhow-to\u002Fconnect-sdlc-integrations","eos\u002Fhow-to\u002Fconnect-sdlc-integrations",{"title":134,"path":135,"stem":136},"How to consume the EOS plugin marketplace","\u002Feos\u002Fhow-to\u002Fconsume-eos-marketplace","eos\u002Fhow-to\u002Fconsume-eos-marketplace",{"title":138,"path":139,"stem":140},"How to create a spike ticket","\u002Feos\u002Fhow-to\u002Fcreate-spike-ticket","eos\u002Fhow-to\u002Fcreate-spike-ticket",{"title":142,"path":143,"stem":144},"How to finalise a repository change","\u002Feos\u002Fhow-to\u002Ffinalise-change","eos\u002Fhow-to\u002Ffinalise-change",{"title":146,"path":147,"stem":148},"How to install the EOS Docs MCP server","\u002Feos\u002Fhow-to\u002Finstall-eos-docs-mcp","eos\u002Fhow-to\u002Finstall-eos-docs-mcp",{"title":150,"path":151,"stem":152},"How to resolve a spike ticket","\u002Feos\u002Fhow-to\u002Fresolve-spike-ticket","eos\u002Fhow-to\u002Fresolve-spike-ticket",{"title":154,"path":155,"stem":156},"How to run Git hook checks","\u002Feos\u002Fhow-to\u002Frun-git-hook-checks","eos\u002Fhow-to\u002Frun-git-hook-checks",{"title":158,"path":159,"stem":160},"How to use the AI-enabled SDLC","\u002Feos\u002Fhow-to\u002Fuse-ai-enabled-sdlc","eos\u002Fhow-to\u002Fuse-ai-enabled-sdlc",{"title":162,"path":163,"stem":164},"Use the Aikido plugin","\u002Feos\u002Fhow-to\u002Fuse-aikido-plugin","eos\u002Fhow-to\u002Fuse-aikido-plugin",{"title":166,"path":167,"stem":168},"How to use Compass","\u002Feos\u002Fhow-to\u002Fuse-compass","eos\u002Fhow-to\u002Fuse-compass",{"title":170,"path":171,"stem":172},"How to use Fallow","\u002Feos\u002Fhow-to\u002Fuse-fallow","eos\u002Fhow-to\u002Fuse-fallow",{"title":174,"path":175,"stem":176},"How to use incremental mutation tests","\u002Feos\u002Fhow-to\u002Fuse-incremental-mutation-tests","eos\u002Fhow-to\u002Fuse-incremental-mutation-tests",{"title":178,"path":179,"stem":180},"How to use Scout","\u002Feos\u002Fhow-to\u002Fuse-scout","eos\u002Fhow-to\u002Fuse-scout",{"title":97,"path":182,"stem":183,"children":184,"page":43},"\u002Feos\u002Freference","eos\u002Freference",[185,189,193,197],{"title":186,"path":187,"stem":188},"Compass CLI","\u002Feos\u002Freference\u002Fcompass-cli","eos\u002Freference\u002Fcompass-cli",{"title":190,"path":191,"stem":192},"EOS marketplace skills","\u002Feos\u002Freference\u002Fmarketplace-skills","eos\u002Freference\u002Fmarketplace-skills",{"title":194,"path":195,"stem":196},"Nx task execution and cache","\u002Feos\u002Freference\u002Fnx-task-execution","eos\u002Freference\u002Fnx-task-execution",{"title":198,"path":199,"stem":200},"Scout CLI","\u002Feos\u002Freference\u002Fscout-cli","eos\u002Freference\u002Fscout-cli",{"title":202,"path":203,"stem":204,"children":205},"Product engineering","\u002Fproduct-engineering","product-engineering\u002FREADME",[206],{"title":14,"collapsed":15,"path":207,"stem":208,"children":209,"page":43},"\u002Fproduct-engineering\u002Fadrs","product-engineering\u002Fadrs",[210],{"title":211,"path":212,"stem":213},"Codify Engineering Standards as Skills","\u002Fproduct-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills","product-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills",[215,268],{"nav":216,"source":265},[217],{"title":218,"path":219,"stem":220,"children":221},"Ember","\u002Fember","ember",[222,225,257,261],{"title":223,"path":219,"stem":224},"StoreFront Documentation","ember\u002Findex",{"title":14,"path":226,"stem":227,"children":228,"page":43},"\u002Fember\u002Fadrs","ember\u002Fadrs",[229,233,237,241,245,249,253],{"title":230,"path":231,"stem":232},"Add Infection as a non-blocking mutation testing tool for Ember","\u002Fember\u002Fadrs\u002F0001-infection-mutation-testing","ember\u002Fadrs\u002F0001-infection-mutation-testing",{"title":234,"path":235,"stem":236},"Process DELETE scheduled changes before CREATE_UPDATE at the same instant","\u002Fember\u002Fadrs\u002F0002-scheduled-change-processing-order","ember\u002Fadrs\u002F0002-scheduled-change-processing-order",{"title":238,"path":239,"stem":240},"Standard rate scheduling is permanent until overridden","\u002Fember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent","ember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent",{"title":242,"path":243,"stem":244},"Promotions require an existing standard rate","\u002Fember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate","ember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate",{"title":246,"path":247,"stem":248},"UTC datetime contract between Storefront and Hub","\u002Fember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub","ember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub",{"title":250,"path":251,"stem":252},"Elapsed unprocessed scheduled changes do not block rate changes","\u002Fember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes","ember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes",{"title":254,"path":255,"stem":256},"ADR-0007: Delete ConfigureTenantAuth routing command once OIDC setup migrates to Hub","\u002Fember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command","ember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command",{"title":258,"path":259,"stem":260},"Domain Overview","\u002Fember\u002Fdomain_overview","ember\u002Fdomain_overview",{"title":262,"path":263,"stem":264},"Main Flows","\u002Fember\u002Fkey_flows","ember\u002Fkey_flows",{"collectionName":266,"label":267,"prefix":220},"external_ember","StoreFront (Ember)",{"nav":269,"source":441},[270],{"title":271,"path":272,"stem":273,"children":274},"Core","\u002Fcore","core",[275,278,306,310,332,336,346,363,420,424],{"title":276,"path":272,"stem":277},"Documentation Index","core\u002Findex",{"title":14,"path":279,"stem":280,"children":281,"page":43},"\u002Fcore\u002Fadrs","core\u002Fadrs",[282,286,290,294,298,302],{"title":283,"path":284,"stem":285},"ADR-XXXX: Short, descriptive title","\u002Fcore\u002Fadrs\u002F0000-template","core\u002Fadrs\u002F0000-template",{"title":287,"path":288,"stem":289},"ADR-1: Promotions endpoint","\u002Fcore\u002Fadrs\u002F0001-add-promotions-endpoint","core\u002Fadrs\u002F0001-add-promotions-endpoint",{"title":291,"path":292,"stem":293},"ADR-2: EOM partner report job dispatch","\u002Fcore\u002Fadrs\u002F0002-eom-report-job-dispatch","core\u002Fadrs\u002F0002-eom-report-job-dispatch",{"title":295,"path":296,"stem":297},"ADR-3: Replace PB fee waive flag with a Zero scheme override","\u002Fcore\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme","core\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme",{"title":299,"path":300,"stem":301},"ADR-4: No processor Digital Top-up capability gate in Reloadable Commercials","\u002Fcore\u002Fadrs\u002F0004-no-processor-top-up-capability-gate","core\u002Fadrs\u002F0004-no-processor-top-up-capability-gate",{"title":303,"path":304,"stem":305},"ADR-5: Sparse polymorphic sale fee override reason stamp","\u002Fcore\u002Fadrs\u002F0005-sale-fee-override-reason-stamp","core\u002Fadrs\u002F0005-sale-fee-override-reason-stamp",{"title":307,"path":308,"stem":309},"Agent Workflow","\u002Fcore\u002Fai-workflow","core\u002Fai-workflow",{"title":311,"path":312,"stem":313,"children":314},"Backend Guide","\u002Fcore\u002Fbackend","core\u002Fbackend\u002Findex",[315,316,320,324,328],{"title":311,"path":312,"stem":313},{"title":317,"path":318,"stem":319},"Backend AI Tooling","\u002Fcore\u002Fbackend\u002Fai-tooling","core\u002Fbackend\u002Fai-tooling",{"title":321,"path":322,"stem":323},"Backend Linting and Formatting","\u002Fcore\u002Fbackend\u002Flinting-and-formatting","core\u002Fbackend\u002Flinting-and-formatting",{"title":325,"path":326,"stem":327},"Backend Setup","\u002Fcore\u002Fbackend\u002Fsetup","core\u002Fbackend\u002Fsetup",{"title":329,"path":330,"stem":331},"Backend Testing","\u002Fcore\u002Fbackend\u002Ftesting","core\u002Fbackend\u002Ftesting",{"title":333,"path":334,"stem":335},"Documentation Health","\u002Fcore\u002Fdoc-health","core\u002Fdoc-health",{"title":337,"path":338,"stem":339,"children":340},"Frontend Guide","\u002Fcore\u002Ffrontend","core\u002Ffrontend\u002Findex",[341,342],{"title":337,"path":338,"stem":339},{"title":343,"path":344,"stem":345},"Frontend Development","\u002Fcore\u002Ffrontend\u002Fdevelopment","core\u002Ffrontend\u002Fdevelopment",{"title":347,"path":348,"stem":349,"children":350,"page":43},"Modules","\u002Fcore\u002Fmodules","core\u002Fmodules",[351,355],{"title":352,"path":353,"stem":354},"Connect API","\u002Fcore\u002Fmodules\u002Fconnect-api","core\u002Fmodules\u002Fconnect-api",{"title":356,"path":357,"stem":358,"children":359,"page":43},"Secure Links","\u002Fcore\u002Fmodules\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links",[360],{"title":356,"path":361,"stem":362},"\u002Fcore\u002Fmodules\u002Fsecure-links\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links\u002Fsecure-links",{"title":364,"path":365,"stem":366,"children":367,"page":43},"Processors","\u002Fcore\u002Fprocessors","core\u002Fprocessors",[368,372,376,380,384,388,392,396,400,404,408,412,416],{"title":369,"path":370,"stem":371},"Amazon Processor and API","\u002Fcore\u002Fprocessors\u002Famazon","core\u002Fprocessors\u002Famazon",{"title":373,"path":374,"stem":375},"Amilon Processor and API","\u002Fcore\u002Fprocessors\u002Familon","core\u002Fprocessors\u002Familon",{"title":377,"path":378,"stem":379},"Cadooz Processor and API","\u002Fcore\u002Fprocessors\u002Fcadooz","core\u002Fprocessors\u002Fcadooz",{"title":381,"path":382,"stem":383},"Choice Digital Processor and API","\u002Fcore\u002Fprocessors\u002Fchoice-digital","core\u002Fprocessors\u002Fchoice-digital",{"title":385,"path":386,"stem":387},"Diggecard processor reference","\u002Fcore\u002Fprocessors\u002Fdiggecard","core\u002Fprocessors\u002Fdiggecard",{"title":389,"path":390,"stem":391},"ePay Processor and API","\u002Fcore\u002Fprocessors\u002Fepay","core\u002Fprocessors\u002Fepay",{"title":393,"path":394,"stem":395},"GoGift v2 Processor and API","\u002Fcore\u002Fprocessors\u002Fgo-gift-v2","core\u002Fprocessors\u002Fgo-gift-v2",{"title":397,"path":398,"stem":399},"InComm Processor and API","\u002Fcore\u002Fprocessors\u002Fincomm","core\u002Fprocessors\u002Fincomm",{"title":401,"path":402,"stem":403},"Ogloba Processor and API","\u002Fcore\u002Fprocessors\u002Fogloba","core\u002Fprocessors\u002Fogloba",{"title":405,"path":406,"stem":407},"SVSECard Processor and API","\u002Fcore\u002Fprocessors\u002Fsvs-ecard","core\u002Fprocessors\u002Fsvs-ecard",{"title":409,"path":410,"stem":411},"Vananam Processor and API","\u002Fcore\u002Fprocessors\u002Fvananam","core\u002Fprocessors\u002Fvananam",{"title":413,"path":414,"stem":415},"Vouchers Depot processor reference","\u002Fcore\u002Fprocessors\u002Fvouchers-depot","core\u002Fprocessors\u002Fvouchers-depot",{"title":417,"path":418,"stem":419},"You Got a Gift Processor and API","\u002Fcore\u002Fprocessors\u002Fyou-got-a-gift","core\u002Fprocessors\u002Fyou-got-a-gift",{"title":421,"path":422,"stem":423},"Development Quickstart","\u002Fcore\u002Fsetup","core\u002Fsetup",{"title":425,"path":426,"stem":427,"children":428,"page":43},"Workflows","\u002Fcore\u002Fworkflows","core\u002Fworkflows",[429,433,437],{"title":430,"path":431,"stem":432},"Hub Endpoint Workflow","\u002Fcore\u002Fworkflows\u002Fhub-endpoints","core\u002Fworkflows\u002Fhub-endpoints",{"title":434,"path":435,"stem":436},"OpenAPI Tooling","\u002Fcore\u002Fworkflows\u002Fopenapi-tooling","core\u002Fworkflows\u002Fopenapi-tooling",{"title":438,"path":439,"stem":440},"Processor Integration Workflow","\u002Fcore\u002Fworkflows\u002Fprocessors","core\u002Fworkflows\u002Fprocessors",{"collectionName":442,"label":271,"prefix":273},"external_core",{"page":444,"surround":803},{"id":445,"title":246,"body":446,"config":798,"description":779,"extension":799,"meta":800,"navigation":15,"path":247,"seo":801,"stem":248,"__hash__":802},"external_ember\u002Fember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub.md",{"type":447,"value":448,"toc":778},"minimark",[449,454,458,462,465,468,557,560,564,567,666,670,690,694,699,705,709,712,716,719,723,726,732,735,739,743,754,758,766,770],[450,451,453],"h2",{"id":452},"status","Status",[455,456,457],"p",{},"accepted",[450,459,461],{"id":460},"context","Context",[455,463,464],{},"Storefront and Hub exchange datetime values across a backend\u002Ffrontend boundary. Storefront's backend is required to operate in UTC, while Hub presents tenant-aware date and time controls.",[455,466,467],{},"The current Hub implementation does not consistently encode one timezone on the wire:",[469,470,471,488,503,509,515,528,544],"ul",{},[472,473,474,475,479,480,483,484,487],"li",{},"Order-history filters use ",[476,477,478],"code",{},"formatDateToIsoStringWithTimezone",", which includes the browser's local UTC offset (",[476,481,482],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Futils\u002FtimezoneUtils.ts:84-108"," and ",[476,485,486],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Flayers\u002Fpartner\u002Fstorefront\u002Fstore.ts:48-58",").",[472,489,490,491,494,495,498,499,502],{},"Promotion creation sends ",[476,492,493],{},"YYYY-MM-DDTHH:mm:00"," without a ",[476,496,497],{},"Z"," suffix or numeric offset (",[476,500,501],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Flayers\u002Fpartner\u002Fpromotions\u002Futils\u002FcreateStorefrontPromotionFormSchema.ts:126-134","). The form validates against the tenant timezone, but does not encode that timezone in the payload.",[472,504,505,506,487],{},"Scheduled brand-rate changes likewise send a timezone-less datetime (",[476,507,508],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Flayers\u002Fpartner\u002Fbrands\u002Fcomponents\u002Fbrand-details\u002Fstorefront-details\u002FStorefrontRateForm.vue:177-192",[472,510,511,512,487],{},"The Storefront proxy forwards requests without datetime normalization (",[476,513,514],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Flayers\u002Fpartner\u002Fstorefront\u002Fserver\u002Fapi\u002Fstorefront\u002F[...storefrontProxy].ts",[472,516,517,518,521,522,483,525,487],{},"Ember's application timezone is UTC (",[476,519,520],{},"config\u002Fapp.php:172","), and scheduled-change persistence converts tenant-local input to UTC before storing it (",[476,523,524],{},"app\u002FHelpers\u002FDateHelper.php:23-29",[476,526,527],{},"app\u002FScheduledChange\u002FService\u002FScheduledChangeService.php:35-70",[472,529,530,531,533,534,537,538,483,541,487],{},"Backend response contracts are also mixed: scheduled changes are serialized as UTC with a ",[476,532,497],{}," suffix (",[476,535,536],{},"app\u002FHub\u002FProducts\u002FHttp\u002FResources\u002FScheduledChangeResource.php:26-31","), while promotions are serialized as tenant-local wall-clock values without an offset (",[476,539,540],{},"app\u002FHub\u002FProducts\u002FPromotions\u002FServices\u002FPromotionService.php:442-446",[476,542,543],{},"app\u002FHub\u002FProducts\u002FPromotions\u002FHttp\u002FResources\u002FPromotionResource.php:21-29",[472,545,546,547,483,550,553,554,487],{},"Backend validation treats promotion and scheduled-rate request values as tenant-local wall-clock values (",[476,548,549],{},"app\u002FRules\u002FAfterInTenantTimeRule.php:42-49",[476,551,552],{},"app\u002FScheduledChange\u002FService\u002FScheduledChangeService.php:49-70","), while order filters accept ISO values with an explicit offset (",[476,555,556],{},"app\u002FHub\u002FOrders\u002FHttp\u002FRequests\u002FOrdersRequest.php:17-24",[455,558,559],{},"Consequently, a consumer cannot reliably determine whether a timezone-less value represents UTC, browser time, or tenant time.",[450,561,563],{"id":562},"current-implementation-audit","Current implementation audit",[455,565,566],{},"Audited 12-08-2026 while wiring promotion conflict validation:",[469,568,569,584,587,601,613,623,626,647],{},[472,570,490,571,483,574,577,578,581,582,487],{},[476,572,573],{},"starts_at",[476,575,576],{},"ends_at"," as ",[476,579,580],{},"YYYY-MM-DDTHH:mm:ss",", without an offset, from tenant-local date\u002Ftime controls (",[476,583,501],{},[472,585,586],{},"Immediate standard-rate endpoints send no datetime.",[472,588,589,590,577,593,581,595,597,598,487],{},"Scheduled standard-rate creation and update send ",[476,591,592],{},"changing_at",[476,594,580],{},[476,596,508],{},", ",[476,599,600],{},"hub\u002Fapps\u002Fhub\u002Fapp\u002Flayers\u002Fpartner\u002Fbrands\u002Fcomponents\u002Fbrand-details\u002Fstorefront-details\u002FScheduledRateForm.vue:155-162",[472,602,603,604,597,607,597,610,487],{},"Ember request validation currently accepts that exact timezone-less format for promotion creation and scheduled-rate create\u002Fupdate (",[476,605,606],{},"app\u002FHub\u002FProducts\u002FPromotions\u002FHttp\u002FRequests\u002FStorePromotionRequest.php:30-42",[476,608,609],{},"app\u002FHub\u002FProducts\u002FHttp\u002FRequests\u002FStoreProductScheduledChangeRequest.php:27-31",[476,611,612],{},"app\u002FHub\u002FProducts\u002FHttp\u002FRequests\u002FUpdateProductScheduledChangeRequest.php:37-43",[472,614,615,618,619,622],{},[476,616,617],{},"PromotionController::store"," interprets those promotion values in the tenant timezone before passing UTC instants to conflict validation. ",[476,620,621],{},"ScheduledChangeService"," likewise converts tenant-local scheduled values to UTC before persistence.",[472,624,625],{},"Current wire behaviour is therefore tenant-local wall-clock input, UTC internal processing\u002Fstorage, and mixed response serialization. This is an implementation gap against the accepted UTC-on-wire decision, not a revision of that decision.",[472,627,628,629,631,632,635,636,639,640,643,644,487],{},"Scheduled-rate request validation originally compared ",[476,630,592],{}," against the server's ",[476,633,634],{},"now()"," value rather than an explicit tenant-timezone rule. Both scheduled-change requests now use ",[476,637,638],{},"AfterInTenantTimeRule",", matching ",[476,641,642],{},"StorePromotionRequest",", and the endpoint-specific timezone tests either side of UTC are in place (",[476,645,646],{},"tests\u002FFeature\u002FHub\u002FHttp\u002FControllers\u002FProducts\u002FProductScheduledChangeControllerTest.php",[472,648,649,650,653,654,657,658,665],{},"The gap has a correctness cost, not only an ambiguity one. A tenant-local wall-clock value is not always a single instant: under DST it may not exist, or may occur twice. ",[476,651,652],{},"DateHelper::convertDateStringFromTenantTimeToUtc"," resolves both cases silently rather than rejecting them — a non-existent spring-forward time is shifted forward an hour, and the first of an ambiguous fall-back pair is chosen — so a scheduled rate change can be stored up to an hour away from what the user asked for, with no signal to them. A UTC instant on the wire removes this class of defect by construction. Current behaviour is characterised (not endorsed) in ",[476,655,656],{},"tests\u002FFeature\u002FHelpers\u002FDateHelperTest.php",", and ",[659,660,664],"a",{"href":661,"rel":662},"https:\u002F\u002Fteamtillo.atlassian.net\u002Fbrowse\u002FEM-3961",[663],"nofollow","EM-3961"," covers closing the gap for the rate and promotion endpoints.",[450,667,669],{"id":668},"decision-drivers","Decision Drivers",[469,671,672,675,678,681,684,687],{},[472,673,674],{},"Storefront backend must always operate in UTC.",[472,676,677],{},"Ember's persisted scheduled-change instants are UTC, even where request values are tenant-local.",[472,679,680],{},"Hub and Storefront need an unambiguous backend\u002Ffrontend contract for datetime values.",[472,682,683],{},"Hub currently uses tenant timezone for some validation and display flows, but that timezone is not consistently represented in submitted values.",[472,685,686],{},"The current proxy does not normalize datetime values.",[472,688,689],{},"Backend responses currently expose both UTC values and tenant-local wall-clock values.",[450,691,693],{"id":692},"considered-options","Considered Options",[695,696,698],"h3",{"id":697},"utc-datetimes-on-the-wire","UTC datetimes on the wire",[455,700,701,702,704],{},"Exchange absolute datetimes as ISO 8601 UTC values, using a ",[476,703,497],{}," suffix. Hub converts tenant-local user input to UTC before sending it and converts UTC responses to the tenant timezone for display.",[695,706,708],{"id":707},"tenant-local-datetimes-on-the-wire","Tenant-local datetimes on the wire",[455,710,711],{},"Exchange wall-clock values interpreted using each tenant's configured IANA timezone.",[695,713,715],{"id":714},"mixed-or-implicit-timezone-handling","Mixed or implicit timezone handling",[455,717,718],{},"Allow each endpoint to choose between browser-local offsets, tenant-local wall-clock values, and UTC.",[450,720,722],{"id":721},"decision-outcome","Decision Outcome",[455,724,725],{},"Adopt UTC as the datetime contract between Storefront and Hub.",[455,727,728,729,731],{},"Storefront backend datetime values are UTC. Datetimes exchanged between Storefront and Hub must be unambiguous ISO 8601 UTC values with a ",[476,730,497],{}," suffix. Hub remains responsible for converting between tenant-local values in the UI and UTC values at the API boundary.",[455,733,734],{},"Timezone-less datetime strings and browser-local offsets are not valid substitutes for this contract. Existing Hub flows that send either representation must be migrated to the UTC contract.",[450,736,738],{"id":737},"consequences","Consequences",[695,740,742],{"id":741},"positive","Positive",[469,744,745,748,751],{},[472,746,747],{},"Storefront and Hub have one unambiguous representation for exchanged datetimes.",[472,749,750],{},"Backend processing remains aligned with Storefront's UTC requirement.",[472,752,753],{},"Tenant timezone remains a presentation and input concern in Hub rather than being implicit in the API payload.",[695,755,757],{"id":756},"negative","Negative",[469,759,760,763],{},[472,761,762],{},"Hub's existing order-filter, promotion, and scheduled-rate payloads require changes.",[472,764,765],{},"Hub must perform explicit tenant-timezone-to-UTC conversion when submitting tenant-local form values.",[695,767,769],{"id":768},"neutral","Neutral",[469,771,772,775],{},[472,773,774],{},"Existing tenant-timezone-aware validation and display behavior remains relevant; it must be paired with conversion at the request\u002Fresponse boundary.",[472,776,777],{},"Backend storage and runtime can remain UTC while API consumers receive or submit tenant-local values only at explicitly defined conversion boundaries.",{"title":779,"searchDepth":780,"depth":780,"links":781},"",3,[782,784,785,786,787,792,793],{"id":452,"depth":783,"text":453},2,{"id":460,"depth":783,"text":461},{"id":562,"depth":783,"text":563},{"id":668,"depth":783,"text":669},{"id":692,"depth":783,"text":693,"children":788},[789,790,791],{"id":697,"depth":780,"text":698},{"id":707,"depth":780,"text":708},{"id":714,"depth":780,"text":715},{"id":721,"depth":783,"text":722},{"id":737,"depth":783,"text":738,"children":794},[795,796,797],{"id":741,"depth":780,"text":742},{"id":756,"depth":780,"text":757},{"id":768,"depth":780,"text":769},{},"md",{},{"title":246,"description":779},"ATqSKY446MGfjjnKZmwZD-zysXCcm9TZ46p-HsL3A_E",[804,805],{"title":242,"path":243,"stem":244,"children":-1},{"title":250,"path":251,"stem":252,"children":-1},1789777548442]