From 922e95b895fcb369dc397db5c047c78baf8327dc Mon Sep 17 00:00:00 2001 From: "Christoph Wen, B.Sc" <43651938+christophwen@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:52:53 +0200 Subject: [PATCH] Fix time and date-time 12h formats (#16692) (#16805) * Fix time and date-time 12h formats (#16692) - am/pm check possible for other languages - adjusted date format gallery page for consistency - added gallery pages for date-time and time formats * Fix typo in am/pm check (#16692) --- gallery/src/data/date-options.ts | 24 ++++ .../date-time/date-time-numeric.markdown | 7 + .../src/pages/date-time/date-time-numeric.ts | 136 ++++++++++++++++++ .../date-time/date-time-seconds.markdown | 7 + .../src/pages/date-time/date-time-seconds.ts | 136 ++++++++++++++++++ .../date-time/date-time-short-year.markdown | 7 + .../pages/date-time/date-time-short-year.ts | 136 ++++++++++++++++++ .../pages/date-time/date-time-short.markdown | 7 + .../src/pages/date-time/date-time-short.ts | 136 ++++++++++++++++++ .../src/pages/date-time/date-time.markdown | 7 + gallery/src/pages/date-time/date-time.ts | 136 ++++++++++++++++++ gallery/src/pages/date-time/date.markdown | 2 +- .../src/pages/date-time/time-seconds.markdown | 7 + gallery/src/pages/date-time/time-seconds.ts | 135 +++++++++++++++++ .../src/pages/date-time/time-weekday.markdown | 7 + gallery/src/pages/date-time/time-weekday.ts | 135 +++++++++++++++++ gallery/src/pages/date-time/time.markdown | 7 + gallery/src/pages/date-time/time.ts | 136 ++++++++++++++++++ src/common/datetime/format_date_time.ts | 92 +++++------- src/common/datetime/format_time.ts | 55 +++---- src/common/datetime/use_am_pm.ts | 6 +- 21 files changed, 1227 insertions(+), 94 deletions(-) create mode 100644 gallery/src/data/date-options.ts create mode 100644 gallery/src/pages/date-time/date-time-numeric.markdown create mode 100644 gallery/src/pages/date-time/date-time-numeric.ts create mode 100644 gallery/src/pages/date-time/date-time-seconds.markdown create mode 100644 gallery/src/pages/date-time/date-time-seconds.ts create mode 100644 gallery/src/pages/date-time/date-time-short-year.markdown create mode 100644 gallery/src/pages/date-time/date-time-short-year.ts create mode 100644 gallery/src/pages/date-time/date-time-short.markdown create mode 100644 gallery/src/pages/date-time/date-time-short.ts create mode 100644 gallery/src/pages/date-time/date-time.markdown create mode 100644 gallery/src/pages/date-time/date-time.ts create mode 100644 gallery/src/pages/date-time/time-seconds.markdown create mode 100644 gallery/src/pages/date-time/time-seconds.ts create mode 100644 gallery/src/pages/date-time/time-weekday.markdown create mode 100644 gallery/src/pages/date-time/time-weekday.ts create mode 100644 gallery/src/pages/date-time/time.markdown create mode 100644 gallery/src/pages/date-time/time.ts diff --git a/gallery/src/data/date-options.ts b/gallery/src/data/date-options.ts new file mode 100644 index 0000000000..9bb856a26e --- /dev/null +++ b/gallery/src/data/date-options.ts @@ -0,0 +1,24 @@ +import type { ControlSelectOption } from "../../../src/components/ha-control-select"; + +export const timeOptions: ControlSelectOption[] = [ + { + value: "now", + label: "Now", + }, + { + value: "00:15:30", + label: "12:15:30 AM", + }, + { + value: "06:15:30", + label: "06:15:30 AM", + }, + { + value: "12:15:30", + label: "12:15:30 PM", + }, + { + value: "18:15:30", + label: "06:15:30 PM", + }, +]; diff --git a/gallery/src/pages/date-time/date-time-numeric.markdown b/gallery/src/pages/date-time/date-time-numeric.markdown new file mode 100644 index 0000000000..3310f315a1 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-numeric.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Numeric) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTimeNumeric: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-numeric.ts b/gallery/src/pages/date-time/date-time-numeric.ts new file mode 100644 index 0000000000..608b3fc152 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-numeric.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTimeNumeric } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-numeric") +export class DemoDateTimeDateTimeNumeric extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTimeNumeric( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-numeric": DemoDateTimeDateTimeNumeric; + } +} diff --git a/gallery/src/pages/date-time/date-time-seconds.markdown b/gallery/src/pages/date-time/date-time-seconds.markdown new file mode 100644 index 0000000000..01cfa6c729 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-seconds.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Seconds) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-seconds.ts b/gallery/src/pages/date-time/date-time-seconds.ts new file mode 100644 index 0000000000..5f5b48f989 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-seconds.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTimeWithSeconds } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-seconds") +export class DemoDateTimeDateTimeSeconds extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-seconds": DemoDateTimeDateTimeSeconds; + } +} diff --git a/gallery/src/pages/date-time/date-time-short-year.markdown b/gallery/src/pages/date-time/date-time-short-year.markdown new file mode 100644 index 0000000000..19e77b55b9 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short-year.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Short w/ Year) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatShortDateTimeWithYear: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-short-year.ts b/gallery/src/pages/date-time/date-time-short-year.ts new file mode 100644 index 0000000000..f132f2a047 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short-year.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatShortDateTimeWithYear } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-short-year") +export class DemoDateTimeDateTimeShortYear extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatShortDateTimeWithYear( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-short-year": DemoDateTimeDateTimeShortYear; + } +} diff --git a/gallery/src/pages/date-time/date-time-short.markdown b/gallery/src/pages/date-time/date-time-short.markdown new file mode 100644 index 0000000000..3564a7afa0 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format (Short) +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatShortDateTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time-short.ts b/gallery/src/pages/date-time/date-time-short.ts new file mode 100644 index 0000000000..21f7eb1294 --- /dev/null +++ b/gallery/src/pages/date-time/date-time-short.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatShortDateTime } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time-short") +export class DemoDateTimeDateTimeShort extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatShortDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time-short": DemoDateTimeDateTimeShort; + } +} diff --git a/gallery/src/pages/date-time/date-time.markdown b/gallery/src/pages/date-time/date-time.markdown new file mode 100644 index 0000000000..cef6195ab1 --- /dev/null +++ b/gallery/src/pages/date-time/date-time.markdown @@ -0,0 +1,7 @@ +--- +title: Date-Time Format +--- + +This pages lists all supported languages with their available date-time formats. + +Formatting function: `const formatDateTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/date-time.ts b/gallery/src/pages/date-time/date-time.ts new file mode 100644 index 0000000000..4bb4b75865 --- /dev/null +++ b/gallery/src/pages/date-time/date-time.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatDateTime } from "../../../../src/common/datetime/format_date_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-date-time") +export class DemoDateTimeDateTime extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatDateTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 900px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-date-time": DemoDateTimeDateTime; + } +} diff --git a/gallery/src/pages/date-time/date.markdown b/gallery/src/pages/date-time/date.markdown index 5eb64bb19e..599921e1c7 100644 --- a/gallery/src/pages/date-time/date.markdown +++ b/gallery/src/pages/date-time/date.markdown @@ -1,5 +1,5 @@ --- -title: (Numeric) Date Formatting +title: Date Format (Numeric) --- This pages lists all supported languages with their available (numeric) date formats. diff --git a/gallery/src/pages/date-time/time-seconds.markdown b/gallery/src/pages/date-time/time-seconds.markdown new file mode 100644 index 0000000000..23136c29f4 --- /dev/null +++ b/gallery/src/pages/date-time/time-seconds.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format (Seconds) +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTimeWithSeconds: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time-seconds.ts b/gallery/src/pages/date-time/time-seconds.ts new file mode 100644 index 0000000000..761bc6ed58 --- /dev/null +++ b/gallery/src/pages/date-time/time-seconds.ts @@ -0,0 +1,135 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTimeWithSeconds } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time-seconds") +export class DemoDateTimeTimeSeconds extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTimeWithSeconds( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 600px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time-seconds": DemoDateTimeTimeSeconds; + } +} diff --git a/gallery/src/pages/date-time/time-weekday.markdown b/gallery/src/pages/date-time/time-weekday.markdown new file mode 100644 index 0000000000..637be6afe3 --- /dev/null +++ b/gallery/src/pages/date-time/time-weekday.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format (Weekday) +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTimeWeekday: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time-weekday.ts b/gallery/src/pages/date-time/time-weekday.ts new file mode 100644 index 0000000000..8ed5c951f5 --- /dev/null +++ b/gallery/src/pages/date-time/time-weekday.ts @@ -0,0 +1,135 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTimeWeekday } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time-weekday") +export class DemoDateTimeTimeWeekday extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTimeWeekday( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 800px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time-weekday": DemoDateTimeTimeWeekday; + } +} diff --git a/gallery/src/pages/date-time/time.markdown b/gallery/src/pages/date-time/time.markdown new file mode 100644 index 0000000000..df90d8931a --- /dev/null +++ b/gallery/src/pages/date-time/time.markdown @@ -0,0 +1,7 @@ +--- +title: Time Format +--- + +This pages lists all supported languages with their available time formats. + +Formatting function: `const formatTime: (dateObj: Date, locale: FrontendLocaleData) => string` \ No newline at end of file diff --git a/gallery/src/pages/date-time/time.ts b/gallery/src/pages/date-time/time.ts new file mode 100644 index 0000000000..df7b101653 --- /dev/null +++ b/gallery/src/pages/date-time/time.ts @@ -0,0 +1,136 @@ +import { html, css, LitElement } from "lit"; +import { customElement, state } from "lit/decorators"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-control-select"; +import { translationMetadata } from "../../../../src/resources/translations-metadata"; +import { formatTime } from "../../../../src/common/datetime/format_time"; +import { timeOptions } from "../../data/date-options"; +import { demoConfig } from "../../../../src/fake_data/demo_config"; +import { + FrontendLocaleData, + NumberFormat, + TimeFormat, + DateFormat, + FirstWeekday, + TimeZone, +} from "../../../../src/data/translation"; + +@customElement("demo-date-time-time") +export class DemoDateTimeTime extends LitElement { + @state() private selection?: string = "now"; + + @state() private date: Date = new Date(); + + handleValueChanged(e: CustomEvent) { + this.selection = e.detail.value as string; + this.date = new Date(); + if (this.selection !== "now") { + const [hours, minutes, seconds] = this.selection.split(":").map(Number); + this.date.setHours(hours); + this.date.setMinutes(minutes); + this.date.setSeconds(seconds); + } + } + + protected render() { + const defaultLocale: FrontendLocaleData = { + language: "en", + number_format: NumberFormat.language, + time_format: TimeFormat.language, + date_format: DateFormat.language, + first_weekday: FirstWeekday.language, + time_zone: TimeZone.local, + }; + return html` + + + +
+
Language
+
Default (lang)
+
12 Hours
+
24 Hours
+
+ ${Object.entries(translationMetadata.translations) + .filter(([key, _]) => key !== "test") + .map( + ([key, value]) => html` +
+
${value.nativeName}
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.language, + }, + demoConfig + )} +
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.am_pm, + }, + demoConfig + )} +
+
+ ${formatTime( + this.date, + { + ...defaultLocale, + language: key, + time_format: TimeFormat.twenty_four, + }, + demoConfig + )} +
+
+ ` + )} +
+ `; + } + + static get styles() { + return css` + ha-control-select { + max-width: 800px; + margin: 12px auto; + } + .header { + font-weight: bold; + } + .center { + text-align: center; + } + .container { + max-width: 600px; + margin: 12px auto; + display: flex; + align-items: center; + justify-content: space-evenly; + } + + .container > div { + flex-grow: 1; + width: 20%; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "demo-date-time-time": DemoDateTimeTime; + } +} diff --git a/src/common/datetime/format_date_time.ts b/src/common/datetime/format_date_time.ts index d9b7a3a862..52700b4a23 100644 --- a/src/common/datetime/format_date_time.ts +++ b/src/common/datetime/format_date_time.ts @@ -15,20 +15,15 @@ export const formatDateTime = ( const formatDateTimeMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "long", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "long", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Aug 9, 2021, 8:23 AM @@ -40,20 +35,15 @@ export const formatShortDateTimeWithYear = ( const formatShortDateTimeWithYearMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "short", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "short", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Aug 9, 8:23 AM @@ -65,19 +55,14 @@ export const formatShortDateTime = ( const formatShortDateTimeMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - month: "short", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + month: "short", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // August 9, 2021, 8:23:15 AM @@ -89,21 +74,16 @@ export const formatDateTimeWithSeconds = ( const formatDateTimeWithSecondsMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - year: "numeric", - month: "long", - day: "numeric", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + year: "numeric", + month: "long", + day: "numeric", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 9/8/2021, 8:23 AM diff --git a/src/common/datetime/format_time.ts b/src/common/datetime/format_time.ts index 41827b4f8d..948eb553fe 100644 --- a/src/common/datetime/format_time.ts +++ b/src/common/datetime/format_time.ts @@ -13,17 +13,12 @@ export const formatTime = ( const formatTimeMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - hour: "numeric", - minute: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + hour: "numeric", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 9:15:24 PM || 21:15:24 @@ -35,18 +30,13 @@ export const formatTimeWithSeconds = ( const formatTimeWithSecondsMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // Tuesday 7:00 PM || Tuesday 19:00 @@ -58,18 +48,13 @@ export const formatTimeWeekday = ( const formatTimeWeekdayMem = memoizeOne( (locale: FrontendLocaleData, serverTimeZone: string) => - new Intl.DateTimeFormat( - locale.language === "en" && !useAmPm(locale) - ? "en-u-hc-h23" - : locale.language, - { - weekday: "long", - hour: useAmPm(locale) ? "numeric" : "2-digit", - minute: "2-digit", - hour12: useAmPm(locale), - timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, - } - ) + new Intl.DateTimeFormat(locale.language, { + weekday: "long", + hour: useAmPm(locale) ? "numeric" : "2-digit", + minute: "2-digit", + hourCycle: useAmPm(locale) ? "h12" : "h23", + timeZone: locale.time_zone === "server" ? serverTimeZone : undefined, + }) ); // 21:15 diff --git a/src/common/datetime/use_am_pm.ts b/src/common/datetime/use_am_pm.ts index 97bf2911b8..4510fee522 100644 --- a/src/common/datetime/use_am_pm.ts +++ b/src/common/datetime/use_am_pm.ts @@ -8,8 +8,10 @@ export const useAmPm = memoizeOne((locale: FrontendLocaleData): boolean => { ) { const testLanguage = locale.time_format === TimeFormat.language ? locale.language : undefined; - const test = new Date().toLocaleString(testLanguage); - return test.includes("AM") || test.includes("PM"); + const test = new Date("January 1, 2023 22:00:00").toLocaleString( + testLanguage + ); + return test.includes("10"); } return locale.time_format === TimeFormat.am_pm;