From db37dffdbb287dc456325bd5710bb393ef730280 Mon Sep 17 00:00:00 2001 From: Shane Qi Date: Tue, 29 Jun 2021 17:32:19 -0500 Subject: [PATCH] Fixed Logbook Card/Panel/Dialog Incorrect Entires for `input_datetime` Entities (#9399) Co-authored-by: Bram Kragten --- src/common/entity/compute_state_display.ts | 72 ++++++++++----- .../common/entity/compute_state_display.ts | 92 +++++++++++++++++++ 2 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index 8165f6daf7..c5eb00c2d7 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -29,37 +29,61 @@ export const computeStateDisplay = ( const domain = computeStateDomain(stateObj); if (domain === "input_datetime") { - let date: Date; - if (!stateObj.attributes.has_time) { + if (state) { + // If trying to display an explicit state, need to parse the explict state to `Date` then format. + // Attributes aren't available, we have to use `state`. + try { + const components = state.split(" "); + if (components.length === 2) { + // Date and time. + return formatDateTime(new Date(components.join("T")), locale); + } + if (components.length === 1) { + if (state.includes("-")) { + // Date only. + return formatDate(new Date(`${state}T00:00`), locale); + } + if (state.includes(":")) { + // Time only. + const now = new Date(); + return formatTime( + new Date(`${now.toISOString().split("T")[0]}T${state}`), + locale + ); + } + } + return state; + } catch { + // Formatting methods may throw error if date parsing doesn't go well, + // just return the state string in that case. + return state; + } + } else { + // If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format. + let date: Date; + if (!stateObj.attributes.has_time) { + date = new Date( + stateObj.attributes.year, + stateObj.attributes.month - 1, + stateObj.attributes.day + ); + return formatDate(date, locale); + } + if (!stateObj.attributes.has_date) { + date = new Date(); + date.setHours(stateObj.attributes.hour, stateObj.attributes.minute); + return formatTime(date, locale); + } + date = new Date( stateObj.attributes.year, stateObj.attributes.month - 1, - stateObj.attributes.day - ); - return formatDate(date, locale); - } - if (!stateObj.attributes.has_date) { - const now = new Date(); - date = new Date( - // Due to bugs.chromium.org/p/chromium/issues/detail?id=797548 - // don't use artificial 1970 year. - now.getFullYear(), - now.getMonth(), - now.getDay(), + stateObj.attributes.day, stateObj.attributes.hour, stateObj.attributes.minute ); - return formatTime(date, locale); + return formatDateTime(date, locale); } - - date = new Date( - stateObj.attributes.year, - stateObj.attributes.month - 1, - stateObj.attributes.day, - stateObj.attributes.hour, - stateObj.attributes.minute - ); - return formatDateTime(date, locale); } if (domain === "humidifier") { diff --git a/test-mocha/common/entity/compute_state_display.ts b/test-mocha/common/entity/compute_state_display.ts index 01246ec186..f91ced5926 100644 --- a/test-mocha/common/entity/compute_state_display.ts +++ b/test-mocha/common/entity/compute_state_display.ts @@ -236,6 +236,98 @@ describe("computeStateDisplay", () => { }); }); + describe("Localizes input_datetime state parameter with full date time", () => { + const stateObj: any = { + entity_id: "input_datetime.test", + state: "123", + attributes: { + has_date: true, + has_time: true, + year: 2021, + month: 6, + day: 13, + hour: 15, + minute: 26, + second: 36, + }, + }; + it("Uses am/pm time format", () => { + assert.strictEqual( + computeStateDisplay( + localize, + stateObj, + localeData, + "2021-07-04 15:40:03" + ), + "July 4, 2021, 3:40 PM" + ); + }); + it("Uses 24h time format", () => { + localeData.time_format = TimeFormat.twenty_four; + assert.strictEqual( + computeStateDisplay( + localize, + stateObj, + localeData, + "2021-07-04 15:40:03" + ), + "July 4, 2021, 15:40" + ); + }); + }); + + it("Localizes input_datetime state parameter with date", () => { + const stateObj: any = { + entity_id: "input_datetime.test", + state: "123", + attributes: { + has_date: true, + has_time: false, + year: 2021, + month: 6, + day: 13, + hour: 15, + minute: 26, + second: 36, + }, + }; + assert.strictEqual( + computeStateDisplay(localize, stateObj, localeData, "2021-07-04"), + "July 4, 2021" + ); + }); + + describe("Localizes input_datetime state parameter with time", () => { + const stateObj: any = { + entity_id: "input_datetime.test", + state: "123", + attributes: { + has_date: false, + has_time: true, + year: 2021, + month: 6, + day: 13, + hour: 15, + minute: 26, + second: 36, + }, + }; + it("Uses am/pm time format", () => { + localeData.time_format = TimeFormat.am_pm; + assert.strictEqual( + computeStateDisplay(localize, stateObj, localeData, "17:05:07"), + "5:05 PM" + ); + }); + it("Uses 24h time format", () => { + localeData.time_format = TimeFormat.twenty_four; + assert.strictEqual( + computeStateDisplay(localize, stateObj, localeData, "17:05:07"), + "17:05" + ); + }); + }); + it("Localizes unavailable", () => { const altLocalize = (message, ...args) => { if (message === "state.sensor.unavailable") {