Remove Intl polyfill on connection and consistently resolve time zone (#19326)

This commit is contained in:
Steve Repsher 2024-01-09 09:06:50 -05:00 committed by GitHub
parent 96a41704ea
commit 88abeada44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 63 additions and 54 deletions

View File

@ -1,7 +1,8 @@
import { HassConfig } from "home-assistant-js-websocket"; import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData, DateFormat } from "../../data/translation"; import { DateFormat, FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
import { resolveTimeZone } from "./resolve-time-zone";
// Tuesday, August 10 // Tuesday, August 10
export const formatDateWeekdayDay = ( export const formatDateWeekdayDay = (
@ -16,7 +17,7 @@ const formatDateWeekdayDayMem = memoizeOne(
weekday: "long", weekday: "long",
month: "long", month: "long",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -33,7 +34,7 @@ const formatDateMem = memoizeOne(
year: "numeric", year: "numeric",
month: "long", month: "long",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -50,7 +51,7 @@ const formatDateShortMem = memoizeOne(
year: "numeric", year: "numeric",
month: "short", month: "short",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -105,7 +106,7 @@ const formatDateNumericMem = memoizeOne(
year: "numeric", year: "numeric",
month: "numeric", month: "numeric",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}); });
} }
@ -113,7 +114,7 @@ const formatDateNumericMem = memoizeOne(
year: "numeric", year: "numeric",
month: "numeric", month: "numeric",
day: "numeric", day: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}); });
} }
); );
@ -130,7 +131,7 @@ const formatDateVeryShortMem = memoizeOne(
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
day: "numeric", day: "numeric",
month: "short", month: "short",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -146,7 +147,7 @@ const formatDateMonthYearMem = memoizeOne(
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
month: "long", month: "long",
year: "numeric", year: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -161,7 +162,7 @@ const formatDateMonthMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
month: "long", month: "long",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -176,7 +177,7 @@ const formatDateYearMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
year: "numeric", year: "numeric",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -191,7 +192,7 @@ const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
weekday: "long", weekday: "long",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -206,6 +207,6 @@ const formatDateWeekdayShortMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) => (locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, { new Intl.DateTimeFormat(locale.language, {
weekday: "short", weekday: "short",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );

View File

@ -4,6 +4,7 @@ import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
import { formatDateNumeric } from "./format_date"; import { formatDateNumeric } from "./format_date";
import { formatTime } from "./format_time"; import { formatTime } from "./format_time";
import { resolveTimeZone } from "./resolve-time-zone";
import { useAmPm } from "./use_am_pm"; import { useAmPm } from "./use_am_pm";
// August 9, 2021, 8:23 AM // August 9, 2021, 8:23 AM
@ -22,7 +23,7 @@ const formatDateTimeMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -42,7 +43,7 @@ const formatShortDateTimeWithYearMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -61,7 +62,7 @@ const formatShortDateTimeMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -82,7 +83,7 @@ const formatDateTimeWithSecondsMem = memoizeOne(
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );

View File

@ -2,6 +2,7 @@ import { HassConfig } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation"; import { FrontendLocaleData } from "../../data/translation";
import "../../resources/intl-polyfill"; import "../../resources/intl-polyfill";
import { resolveTimeZone } from "./resolve-time-zone";
import { useAmPm } from "./use_am_pm"; import { useAmPm } from "./use_am_pm";
// 9:15 PM || 21:15 // 9:15 PM || 21:15
@ -17,7 +18,7 @@ const formatTimeMem = memoizeOne(
hour: "numeric", hour: "numeric",
minute: "2-digit", minute: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -35,7 +36,7 @@ const formatTimeWithSecondsMem = memoizeOne(
minute: "2-digit", minute: "2-digit",
second: "2-digit", second: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -53,7 +54,7 @@ const formatTimeWeekdayMem = memoizeOne(
hour: useAmPm(locale) ? "numeric" : "2-digit", hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit", minute: "2-digit",
hourCycle: useAmPm(locale) ? "h12" : "h23", hourCycle: useAmPm(locale) ? "h12" : "h23",
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );
@ -71,6 +72,6 @@ const formatTime24hMem = memoizeOne(
hour: "numeric", hour: "numeric",
minute: "2-digit", minute: "2-digit",
hour12: false, hour12: false,
timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
}) })
); );

View File

@ -1,4 +1,5 @@
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import "../../resources/intl-polyfill";
export const localizeWeekdays = memoizeOne( export const localizeWeekdays = memoizeOne(
(language: string, short: boolean): string[] => { (language: string, short: boolean): string[] => {

View File

@ -0,0 +1,15 @@
import { TimeZone } from "../../data/translation";
// Browser time zone can be determined from Intl, with fallback to UTC for polyfill or no support.
// Alternatively, we could fallback to a fixed offset IANA zone (e.g. "Etc/GMT+5") using
// Date.prototype.getTimeOffset(), but IANA only has whole hour Etc zones, and problems
// might occur with relative time due to DST.
// Use optional chain instead of polyfill import since polyfill will always return UTC
export const LOCAL_TIME_ZONE =
Intl.DateTimeFormat?.().resolvedOptions?.().timeZone ?? "UTC";
// Pick time zone based on user profile option. Core zone is used when local cannot be determined.
export const resolveTimeZone = (option: TimeZone, serverTimeZone: string) =>
option === TimeZone.local && LOCAL_TIME_ZONE !== "UTC"
? LOCAL_TIME_ZONE
: serverTimeZone;

View File

@ -9,6 +9,7 @@ import {
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { LOCAL_TIME_ZONE } from "../common/datetime/resolve-time-zone";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-alert"; import "../components/ha-alert";
@ -33,8 +34,7 @@ class OnboardingCoreConfig extends LitElement {
private _elevation = "0"; private _elevation = "0";
private _timeZone: ConfigUpdateValues["time_zone"] = private _timeZone: ConfigUpdateValues["time_zone"] = LOCAL_TIME_ZONE;
Intl.DateTimeFormat?.().resolvedOptions?.().timeZone;
private _language: ConfigUpdateValues["language"] = getLocalLanguage(); private _language: ConfigUpdateValues["language"] = getLocalLanguage();

View File

@ -25,6 +25,7 @@ import { renderRRuleAsText } from "./recurrence";
import { showConfirmEventDialog } from "./show-confirm-event-dialog-box"; import { showConfirmEventDialog } from "./show-confirm-event-dialog-box";
import { CalendarEventDetailDialogParams } from "./show-dialog-calendar-event-detail"; import { CalendarEventDetailDialogParams } from "./show-dialog-calendar-event-detail";
import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor"; import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor";
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
class DialogCalendarEventDetail extends LitElement { class DialogCalendarEventDetail extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -138,8 +139,10 @@ class DialogCalendarEventDetail extends LitElement {
} }
private _formatDateRange() { private _formatDateRange() {
// Parse a dates in the browser timezone const timeZone = resolveTimeZone(
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; this.hass.locale.time_zone,
this.hass.config.time_zone
);
const start = toDate(this._data!.dtstart, { timeZone: timeZone }); const start = toDate(this._data!.dtstart, { timeZone: timeZone });
const endValue = toDate(this._data!.dtend, { timeZone: timeZone }); const endValue = toDate(this._data!.dtend, { timeZone: timeZone });
// All day events should be displayed as a day earlier // All day events should be displayed as a day earlier

View File

@ -11,6 +11,7 @@ import { HassEntity } from "home-assistant-js-websocket";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { supportsFeature } from "../../common/entity/supports-feature"; import { supportsFeature } from "../../common/entity/supports-feature";
@ -32,7 +33,6 @@ import {
deleteCalendarEvent, deleteCalendarEvent,
updateCalendarEvent, updateCalendarEvent,
} from "../../data/calendar"; } from "../../data/calendar";
import { TimeZone } from "../../data/translation";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../lovelace/components/hui-generic-entity-row"; import "../lovelace/components/hui-generic-entity-row";
@ -68,7 +68,7 @@ class DialogCalendarEventEditor extends LitElement {
@state() private _submitting = false; @state() private _submitting = false;
// Dates are manipulated and displayed in the browser timezone // Dates are displayed in the timezone according to the user's profile
// which may be different from the Home Assistant timezone. When // which may be different from the Home Assistant timezone. When
// events are persisted, they are relative to the Home Assistant // events are persisted, they are relative to the Home Assistant
// timezone, but floating without a timezone. // timezone, but floating without a timezone.
@ -85,10 +85,10 @@ class DialogCalendarEventEditor extends LitElement {
computeStateDomain(stateObj) === "calendar" && computeStateDomain(stateObj) === "calendar" &&
supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT) supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT)
)?.entity_id; )?.entity_id;
this._timeZone = this._timeZone = resolveTimeZone(
this.hass.locale.time_zone === TimeZone.local this.hass.locale.time_zone,
? Intl.DateTimeFormat().resolvedOptions().timeZone this.hass.config.time_zone
: this.hass.config.time_zone; );
if (params.entry) { if (params.entry) {
const entry = params.entry!; const entry = params.entry!;
this._allDay = isDate(entry.dtstart); this._allDay = isDate(entry.dtstart);

View File

@ -294,10 +294,7 @@ class HaConfigSectionGeneral extends LitElement {
this._country = this.hass.config.country; this._country = this.hass.config.country;
this._language = this.hass.config.language; this._language = this.hass.config.language;
this._elevation = this.hass.config.elevation; this._elevation = this.hass.config.elevation;
this._timeZone = this._timeZone = this.hass.config.time_zone || "Etc/GMT";
this.hass.config.time_zone ||
Intl.DateTimeFormat?.().resolvedOptions?.().timeZone ||
"Etc/GMT";
this._name = this.hass.config.location_name; this._name = this.hass.config.location_name;
this._updateUnits = true; this._updateUnits = true;
} }

View File

@ -2,6 +2,7 @@ import "@material/mwc-list/mwc-list-item";
import { html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { formatDateTimeNumeric } from "../../common/datetime/format_date_time"; import { formatDateTimeNumeric } from "../../common/datetime/format_date_time";
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-card"; import "../../components/ha-card";
import "../../components/ha-select"; import "../../components/ha-select";
@ -48,10 +49,9 @@ class TimeZoneRow extends LitElement {
>${this.hass.localize( >${this.hass.localize(
`ui.panel.profile.time_zone.options.${format}`, `ui.panel.profile.time_zone.options.${format}`,
{ {
timezone: (format === "server" timezone: resolveTimeZone(
? this.hass.config.time_zone format,
: Intl.DateTimeFormat?.().resolvedOptions?.().timeZone || this.hass.config.time_zone
""
).replace("_", " "), ).replace("_", " "),
} }
)}</span )}</span

View File

@ -3,6 +3,7 @@ import { formatInTimeZone, toDate } from "date-fns-tz";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { supportsFeature } from "../../common/entity/supports-feature"; import { supportsFeature } from "../../common/entity/supports-feature";
import "../../components/ha-alert"; import "../../components/ha-alert";
@ -19,7 +20,6 @@ import {
deleteItems, deleteItems,
updateItem, updateItem,
} from "../../data/todo"; } from "../../data/todo";
import { TimeZone } from "../../data/translation";
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
import { haStyleDialog } from "../../resources/styles"; import { haStyleDialog } from "../../resources/styles";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
@ -54,10 +54,10 @@ class DialogTodoItemEditor extends LitElement {
public showDialog(params: TodoItemEditDialogParams): void { public showDialog(params: TodoItemEditDialogParams): void {
this._error = undefined; this._error = undefined;
this._params = params; this._params = params;
this._timeZone = this._timeZone = resolveTimeZone(
this.hass.locale.time_zone === TimeZone.local this.hass.locale.time_zone,
? Intl.DateTimeFormat().resolvedOptions().timeZone this.hass.config.time_zone
: this.hass.config.time_zone; );
if (params.item) { if (params.item) {
const entry = params.item; const entry = params.item;
this._checked = entry.status === TodoItemStatus.Completed; this._checked = entry.status === TodoItemStatus.Completed;

View File

@ -259,17 +259,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
} }
this._updateHass({ areas }); this._updateHass({ areas });
}); });
subscribeConfig(conn, (config) => { subscribeConfig(conn, (config) => this._updateHass({ config }));
if (this.hass?.config?.time_zone !== config.time_zone) {
import("../resources/intl-polyfill").then(() => {
if ("__setDefaultTimeZone" in Intl.DateTimeFormat) {
// @ts-ignore
Intl.DateTimeFormat.__setDefaultTimeZone(config.time_zone);
}
});
}
this._updateHass({ config });
});
subscribeServices(conn, (services) => this._updateHass({ services })); subscribeServices(conn, (services) => this._updateHass({ services }));
subscribePanels(conn, (panels) => this._updateHass({ panels })); subscribePanels(conn, (panels) => this._updateHass({ panels }));
subscribeFrontendUserData(conn, "core", (userData) => subscribeFrontendUserData(conn, "core", (userData) =>