← Back to Timezone Handling ← Back to Table of Contents

10.3.1 KMP_Timezone utility API

app/assets/js/timezone-utils.js exports a browser-facing timezone utility as window.KMP_Timezone. The shared index entry imports it, and the module automatically initializes matching datetime inputs on DOM ready.

This utility improves display and input UX. It is not a persistence, authorization, tenant-resolution, or validation boundary; normalize submitted values on the server.

Detection

detectTimezone(): string

Returns Intl.DateTimeFormat().resolvedOptions().timeZone. If detection throws, it logs a warning and returns UTC.

getTimezone(element): string

Returns element.dataset.timezone when present; otherwise calls detectTimezone().

Use valid IANA identifiers. This method does not itself validate a supplied data attribute before other Intl methods receive it.

Formatting

formatDateTime(utcDateTime, timezone?, options?): string

Formats a string or Date with Intl.DateTimeFormat('en-US', ...). Defaults to numeric date, hour, and minute with a 12-hour clock. Caller options are merged over those defaults.

An empty input or invalid date returns an empty string. An Intl formatting error is logged and falls back to Date#toLocaleString().

formatDate(utcDateTime, timezone?, options?): string

Formats year, long month, and day by default. An Intl error falls back to Date#toLocaleDateString().

formatTime(utcDateTime, timezone?, options?): string

Formats hour and minute with a 12-hour clock by default. An Intl error falls back to Date#toLocaleTimeString().

KMP_Timezone.formatDateTime(
    '2026-08-28T18:00:00Z',
    'America/Chicago',
    { month: 'short', timeZoneName: 'short' },
)

Keep locale-sensitive, durable formatting on the PHP side when the output must be consistent in exports, email, or background work.

Datetime-local conversion

toLocalInput(utcDateTime, timezone?): string

Returns YYYY-MM-DDTHH:mm for a UTC instant as observed in the target zone. Returns an empty string for empty or invalid input.

toUTC(localDateTime, timezone?): string

Accepts YYYY-MM-DDTHH:mm or YYYY-MM-DD HH:mm:ss, estimates the target zone’s offset for that date, and returns an ISO UTC string. Invalid input or a conversion error returns an empty string.

const local = KMP_Timezone.toLocalInput(
    '2026-08-28T18:00:00Z',
    'America/Chicago',
)
const utc = KMP_Timezone.toUTC(local, 'America/Chicago')

Browser conversion cannot fully communicate ambiguous or nonexistent local times around DST transitions. The server must validate the final instant and apply the workflow’s ambiguity rule.

getTimezoneOffset(timezone, date?): number

Returns the target zone offset from UTC in minutes for the supplied date. On error it logs and returns 0.

getAbbreviation(timezone?, date?): string

Returns the Intl short timezone-name part for the date, or an empty string on error. The result may be an abbreviation or a localized GMT offset depending on browser/locale; do not parse it.

Initialization and form helpers

initializeDatetimeInputs(container = document): void

Finds input[type="datetime-local"][data-utc-value] under container, determines each input’s data-timezone or browser timezone, and assigns toLocalInput(data-utc-value).

The module invokes this once on DOM ready. Turbo-inserted content should use the timezone-input Stimulus controller, whose lifecycle follows frame changes.

convertFormDatetimesToUTC(form, timezone?): void

For each non-empty datetime-local input:

  1. stores its local value in data-original-value;
  2. creates a hidden field with the same name and converted ISO value;
  3. disables the visible input; and
  4. appends the hidden field to the form.

If conversion fails, the visible input remains enabled and receives data-conversion-error="true". Invoke this direct helper only once per submission; the Stimulus controller has stronger duplicate-hidden-field and reset handling.

Element example

<input
    type="datetime-local"
    name="start_date"
    data-utc-value="2026-08-28T18:00:00Z"
    data-timezone="America/Chicago">

Do not choose a tenant by data attribute. The request host establishes tenant context; data-timezone only selects how an instant is presented.

Nulls and errors

Verification

Tests for the utility and the Stimulus controller live under app/tests/js. Run:

cd app
npm run test:js

Include winter/summer dates, invalid values, form reset/retry, and a browser round trip when changing conversion behavior.