| ← 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:
- stores its local value in
data-original-value; - creates a hidden field with the same name and converted ISO value;
- disables the visible input; and
- 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
- Formatting and conversion methods return
''for empty input. - Formatting logs malformed dates.
- Invalid zone names generally reach an Intl catch/fallback.
- Callers should not submit
''as a successful conversion. - User-facing failure paths should set validation state and announce an error; console output alone is insufficient.
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.