Use browser default time and number formatting with polyfills if needed (#9481)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Bram Kragten 2021-09-30 01:34:52 +02:00 committed by GitHub
parent d7f00df391
commit 1bccbd4173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 952 additions and 974 deletions

View File

@ -12,7 +12,7 @@ on:
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
lint:
@ -53,8 +53,8 @@ jobs:
run: yarn install
env:
CI: true
- name: Run Mocha
run: yarn run mocha
- name: Run Tests
run: yarn run test
build:
runs-on: ubuntu-latest
needs: [lint, test]

View File

@ -7,7 +7,7 @@ on:
env:
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
deploy:

View File

@ -8,7 +8,7 @@ on:
env:
PYTHON_VERSION: 3.8
NODE_VERSION: 14
NODE_OPTIONS: --max_old_space_size=4096
NODE_OPTIONS: --max_old_space_size=6144
jobs:
release:

View File

@ -1,4 +0,0 @@
module.exports = {
require: "test-mocha/testconf.js",
timeout: 10000,
};

View File

@ -82,6 +82,7 @@ module.exports.babelOptions = ({ latestBuild }) => ({
// Only support the syntax, Webpack will handle it.
"@babel/plugin-syntax-import-meta",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-top-level-await",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],

View File

@ -6,6 +6,7 @@ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const paths = require("./paths.js");
const bundle = require("./bundle.js");
const log = require("fancy-log");
const WebpackBar = require("webpackbar");
class LogStartCompilePlugin {
ignoredFirst = false;
@ -74,6 +75,7 @@ const createWebpackConfig = ({
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
},
plugins: [
new WebpackBar({ fancy: !isProdBuild }),
new WebpackManifestPlugin({
// Only include the JS of entrypoints
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
@ -149,6 +151,9 @@ const createWebpackConfig = ({
// To silence warning in worker plugin
globalObject: "self",
},
experiments: {
topLevelAwait: true,
},
};
};

View File

@ -14,7 +14,7 @@ import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import relativeTime from "../../../src/common/datetime/relative_time";
import { relativeTime } from "../../../src/common/datetime/relative_time";
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
import {
DataTableColumnContainer,
@ -133,7 +133,7 @@ export class HassioBackups extends LitElement {
filterable: true,
sortable: true,
template: (entry: string) =>
relativeTime(new Date(entry), this.hass.localize),
relativeTime(new Date(entry), this.hass.locale),
},
secondary: {
title: "",

View File

@ -1,3 +1,3 @@
[build.environment]
YARN_VERSION = "1.22.11"
NODE_OPTIONS = "--max_old_space_size=4096"
NODE_OPTIONS = "--max_old_space_size=6144"

View File

@ -16,8 +16,7 @@
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
"format": "yarn run format:eslint && yarn run format:prettier",
"mocha": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
"test": "yarn run mocha"
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.js \"test/**/*.ts\""
},
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
"license": "Apache-2.0",
@ -34,9 +33,13 @@
"@codemirror/stream-parser": "^0.19.1",
"@codemirror/text": "^0.19.2",
"@codemirror/view": "^0.19.4",
"@formatjs/intl-datetimeformat": "^4.2.4",
"@formatjs/intl-getcanonicallocales": "^1.7.3",
"@formatjs/intl-locale": "^2.4.37",
"@formatjs/intl-pluralrules": "^4.1.3",
"@formatjs/intl-locale": "^2.4.38",
"@formatjs/intl-numberformat": "^7.2.4",
"@formatjs/intl-pluralrules": "^4.1.4",
"@formatjs/intl-relativetimeformat": "^9.3.1",
"@formatjs/intl-utils": "^3.8.4",
"@fullcalendar/common": "5.9.0",
"@fullcalendar/core": "5.9.0",
"@fullcalendar/daygrid": "5.9.0",
@ -103,7 +106,6 @@
"date-fns": "^2.23.0",
"deep-clone-simple": "^1.1.1",
"deep-freeze": "^0.0.1",
"fecha": "^4.2.0",
"fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2",
"hls.js": "^1.0.10",
@ -152,6 +154,7 @@
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-syntax-import-meta": "^7.10.4",
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/preset-env": "^7.14.7",
"@babel/preset-typescript": "^7.14.5",
"@koa/cors": "^3.1.0",
@ -197,6 +200,7 @@
"gulp-zopfli-green": "^3.0.1",
"html-minifier": "^4.0.0",
"husky": "^1.3.1",
"instant-mocha": "^1.3.1",
"lint-staged": "^11.0.1",
"lit-analyzer": "^1.2.1",
"lodash.template": "^4.5.0",
@ -216,16 +220,16 @@
"sinon": "^11.0.0",
"source-map-url": "^0.4.0",
"systemjs": "^6.3.2",
"terser-webpack-plugin": "^5.1.4",
"terser-webpack-plugin": "^5.2.4",
"ts-lit-plugin": "^1.2.1",
"ts-mocha": "^8.0.0",
"typescript": "^4.3.5",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"webpack": "^5.43.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"webpack-manifest-plugin": "^3.1.1",
"webpack": "^5.55.1",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.0",
"webpack-manifest-plugin": "^4.0.2",
"webpackbar": "^5.0.0-3",
"workbox-build": "^6.1.5"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",

View File

@ -1,34 +0,0 @@
// Check for support of native locale string options
function checkToLocaleDateStringSupportsOptions() {
try {
new Date().toLocaleDateString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
function checkToLocaleTimeStringSupportsOptions() {
try {
new Date().toLocaleTimeString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
function checkToLocaleStringSupportsOptions() {
try {
new Date().toLocaleString("i");
} catch (e) {
return e.name === "RangeError";
}
return false;
}
export const toLocaleDateStringSupportsOptions =
checkToLocaleDateStringSupportsOptions();
export const toLocaleTimeStringSupportsOptions =
checkToLocaleTimeStringSupportsOptions();
export const toLocaleStringSupportsOptions =
checkToLocaleStringSupportsOptions();

View File

@ -1,13 +1,15 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleDateStringSupportsOptions } from "./check_options_support";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// Tuesday, August 10
export const formatDateWeekday = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "dddd, MMMM D");
export const formatDateWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateWeekdayMem(locale).format(dateObj);
const formatDateWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -18,10 +20,9 @@ const formatDateWeekdayMem = memoizeOne(
);
// August 10, 2021
export const formatDate = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM D, YYYY");
export const formatDate = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMem(locale).format(dateObj);
const formatDateMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -32,10 +33,9 @@ const formatDateMem = memoizeOne(
);
// 10/08/2021
export const formatDateNumeric = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateNumericMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "M/D/YYYY");
export const formatDateNumeric = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateNumericMem(locale).format(dateObj);
const formatDateNumericMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -46,10 +46,9 @@ const formatDateNumericMem = memoizeOne(
);
// Aug 10
export const formatDateShort = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateShortMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMM D");
export const formatDateShort = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateShortMem(locale).format(dateObj);
const formatDateShortMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -59,10 +58,11 @@ const formatDateShortMem = memoizeOne(
);
// August 2021
export const formatDateMonthYear = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthYearMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM YYYY");
export const formatDateMonthYear = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateMonthYearMem(locale).format(dateObj);
const formatDateMonthYearMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -72,10 +72,9 @@ const formatDateMonthYearMem = memoizeOne(
);
// August
export const formatDateMonth = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "MMMM");
export const formatDateMonth = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateMonthMem(locale).format(dateObj);
const formatDateMonthMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -84,10 +83,9 @@ const formatDateMonthMem = memoizeOne(
);
// 2021
export const formatDateYear = toLocaleDateStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateYearMem(locale).format(dateObj)
: (dateObj: Date) => format(dateObj, "YYYY");
export const formatDateYear = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateYearMem(locale).format(dateObj);
const formatDateYearMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {

View File

@ -1,15 +1,16 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// August 9, 2021, 8:23 AM
export const formatDateTime = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatDateTime = (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeMem(locale).format(dateObj);
const formatDateTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -23,11 +24,11 @@ const formatDateTimeMem = memoizeOne(
);
// August 9, 2021, 8:23:15 AM
export const formatDateTimeWithSeconds = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "MMMM D, YYYY, HH:mm:ss" + useAmPm(locale) ? " A" : "");
export const formatDateTimeWithSeconds = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateTimeWithSecondsMem(locale).format(dateObj);
const formatDateTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -42,11 +43,11 @@ const formatDateTimeWithSecondsMem = memoizeOne(
);
// 9/8/2021, 8:23 AM
export const formatDateTimeNumeric = toLocaleStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatDateTimeNumericMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "M/D/YYYY, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatDateTimeNumeric = (
dateObj: Date,
locale: FrontendLocaleData
) => formatDateTimeNumericMem(locale).format(dateObj);
const formatDateTimeNumericMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {

View File

@ -1,30 +1,31 @@
import { format } from "fecha";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { toLocaleTimeStringSupportsOptions } from "./check_options_support";
import { useAmPm } from "./use_am_pm";
import { polyfillsLoaded } from "../translations/localize";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
// 9:15 PM || 21:15
export const formatTime = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "shortTime" + useAmPm(locale) ? " A" : "");
export const formatTime = (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeMem(locale).format(dateObj);
const formatTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
hour: useAmPm(locale) ? "numeric" : "2-digit",
hour: "numeric",
minute: "2-digit",
hour12: useAmPm(locale),
})
);
// 9:15:24 PM || 21:15:24
export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWithSecondsMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "mediumTime" + useAmPm(locale) ? " A" : "");
export const formatTimeWithSeconds = (
dateObj: Date,
locale: FrontendLocaleData
) => formatTimeWithSecondsMem(locale).format(dateObj);
const formatTimeWithSecondsMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
@ -36,17 +37,15 @@ const formatTimeWithSecondsMem = memoizeOne(
);
// Tuesday 7:00 PM || Tuesday 19:00
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWeekdayMem(locale).format(dateObj)
: (dateObj: Date, locale: FrontendLocaleData) =>
format(dateObj, "dddd, HH:mm" + useAmPm(locale) ? " A" : "");
export const formatTimeWeekday = (dateObj: Date, locale: FrontendLocaleData) =>
formatTimeWeekdayMem(locale).format(dateObj);
const formatTimeWeekdayMem = memoizeOne(
(locale: FrontendLocaleData) =>
new Intl.DateTimeFormat(locale.language, {
weekday: "long",
hour: useAmPm(locale) ? "numeric" : "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: useAmPm(locale),
})
);

View File

@ -1,48 +1,32 @@
import { LocalizeFunc } from "../translations/localize";
import { selectUnit } from "@formatjs/intl-utils";
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { polyfillsLoaded } from "../translations/localize";
/**
* Calculate a string representing a date object as relative time from now.
*
* Example output: 5 minutes ago, in 3 days.
*/
const tests = [60, 60, 24, 7];
const langKey = ["second", "minute", "hour", "day"];
export default function relativeTime(
dateObj: Date,
localize: LocalizeFunc,
options: {
compareTime?: Date;
includeTense?: boolean;
} = {}
): string {
const compareTime = options.compareTime || new Date();
let delta = (compareTime.getTime() - dateObj.getTime()) / 1000;
const tense = delta >= 0 ? "past" : "future";
delta = Math.abs(delta);
let roundedDelta = Math.round(delta);
if (roundedDelta === 0) {
return localize("ui.components.relative_time.just_now");
}
let unit = "week";
for (let i = 0; i < tests.length; i++) {
if (roundedDelta < tests[i]) {
unit = langKey[i];
break;
}
delta /= tests[i];
roundedDelta = Math.round(delta);
}
return localize(
options.includeTense === false
? `ui.components.relative_time.duration.${unit}`
: `ui.components.relative_time.${tense}_duration.${unit}`,
"count",
roundedDelta
);
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
}
const formatRelTimeMem = memoizeOne(
(locale: FrontendLocaleData) =>
// @ts-expect-error
new Intl.RelativeTimeFormat(locale.language, { numeric: "auto" })
);
export const relativeTime = (
from: Date,
locale: FrontendLocaleData,
to?: Date,
includeTense = true
): string => {
const diff = selectUnit(from, to);
if (includeTense) {
return formatRelTimeMem(locale).format(diff.value, diff.unit);
}
return Intl.NumberFormat(locale.language, {
style: "unit",
// @ts-expect-error
unit: diff.unit,
unitDisplay: "long",
}).format(Math.abs(diff.value));
};

View File

@ -4,7 +4,7 @@ import { FrontendLocaleData } from "../../data/translation";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
import { formatTime } from "../datetime/format_time";
import { formatNumber } from "../string/format_number";
import { formatNumber } from "../number/format_number";
import { LocalizeFunc } from "../translations/localize";
import { computeStateDomain } from "./compute_state_domain";

View File

@ -1,5 +1,5 @@
import { FrontendLocaleData, NumberFormat } from "../../data/translation";
import { round } from "../number/round";
import { round } from "./round";
export const numberFormatToLocale = (
localeOptions: FrontendLocaleData

View File

@ -1,4 +1,7 @@
import { shouldPolyfill } from "@formatjs/intl-pluralrules/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillLocale } from "@formatjs/intl-locale/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-pluralrules/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill";
import IntlMessageFormat from "intl-messageformat";
import { Resources } from "../../types";
@ -14,15 +17,35 @@ export interface FormatsType {
let loadedPolyfillLocale: Set<string> | undefined;
let polyfillLoaded = !shouldPolyfill();
const polyfillProm = polyfillLoaded
const polyfillPluralRules = shouldPolyfillPluralRules();
const polyfillRelativeTime = shouldPolyfillRelativeTime();
const polyfillDateTime = shouldPolyfillDateTime();
const polyfills: Promise<any>[] = [];
if (__BUILD__ === "latest") {
if (shouldPolyfillLocale()) {
polyfills.push(import("@formatjs/intl-locale/polyfill"));
}
if (polyfillPluralRules) {
polyfills.push(import("@formatjs/intl-pluralrules/polyfill"));
}
if (polyfillRelativeTime) {
polyfills.push(import("@formatjs/intl-relativetimeformat/polyfill"));
}
if (polyfillDateTime) {
polyfills.push(import("@formatjs/intl-datetimeformat/polyfill"));
}
}
let polyfillLoaded = polyfills.length === 0;
export const polyfillsLoaded = polyfillLoaded
? undefined
: import("@formatjs/intl-locale/polyfill")
.then(() => import("@formatjs/intl-pluralrules/polyfill"))
.then(() => {
loadedPolyfillLocale = new Set();
polyfillLoaded = true;
});
: Promise.all(polyfills).then(() => {
loadedPolyfillLocale = new Set();
polyfillLoaded = true;
// Load English so it becomes the default
return loadPolyfillLocales("en");
});
/**
* Adapted from Polymer app-localize-behavior.
@ -52,17 +75,10 @@ export const computeLocalize = async (
formats?: FormatsType
): Promise<LocalizeFunc> => {
if (!polyfillLoaded) {
await polyfillProm;
await polyfillsLoaded;
}
if (loadedPolyfillLocale && !loadedPolyfillLocale.has(language)) {
try {
loadedPolyfillLocale.add(language);
await import("@formatjs/intl-pluralrules/locale-data/en");
} catch (_e) {
// Ignore
}
}
loadPolyfillLocales(language);
// Everytime any of the parameters change, invalidate the strings cache.
cache._localizationCache = {};
@ -114,3 +130,23 @@ export const computeLocalize = async (
}
};
};
export const loadPolyfillLocales = async (language: string) => {
if (!loadedPolyfillLocale || loadedPolyfillLocale.has(language)) {
return;
}
loadedPolyfillLocale.add(language);
try {
if (polyfillPluralRules) {
await import(`@formatjs/intl-pluralrules/locale-data/${language}`);
}
if (polyfillRelativeTime) {
await import(`@formatjs/intl-relativetimeformat/locale-data/${language}`);
}
if (polyfillDateTime) {
await import(`@formatjs/intl-datetimeformat/locale-data/${language}`);
}
} catch (_e) {
// Ignore
}
};

View File

@ -5,7 +5,7 @@ import { getColorByIndex } from "../../common/color/colors";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
} from "../../common/number/format_number";
import { LineChartEntity, LineChartState } from "../../data/history";
import { HomeAssistant } from "../../types";
import "./ha-chart-base";

View File

@ -5,7 +5,7 @@ import { customElement, property, state } from "lit/decorators";
import { getColorByIndex } from "../../common/color/colors";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import { computeDomain } from "../../common/entity/compute_domain";
import { numberFormatToLocale } from "../../common/string/format_number";
import { numberFormatToLocale } from "../../common/number/format_number";
import { computeRTL } from "../../common/util/compute_rtl";
import { TimelineEntity } from "../../data/history";
import { HomeAssistant } from "../../types";

View File

@ -19,7 +19,7 @@ import { computeStateName } from "../../common/entity/compute_state_name";
import {
formatNumber,
numberFormatToLocale,
} from "../../common/string/format_number";
} from "../../common/number/format_number";
import {
getStatisticIds,
Statistics,

View File

@ -15,7 +15,7 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
import { stateIcon } from "../../common/entity/state_icon";
import { timerTimeRemaining } from "../../data/timer";
import { formatNumber } from "../../common/string/format_number";
import { formatNumber } from "../../common/number/format_number";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { HomeAssistant } from "../../types";
import "../ha-label-badge";

View File

@ -1,7 +1,7 @@
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import { CLIMATE_PRESET_NONE } from "../data/climate";
import type { HomeAssistant } from "../types";

View File

@ -2,7 +2,7 @@ import { css, LitElement, PropertyValues, svg, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import { afterNextRender } from "../common/util/render-status";
import { FrontendLocaleData } from "../data/translation";
import { getValueInPercentage, normalize } from "../util/calculate";

View File

@ -1,6 +1,6 @@
import { PropertyValues, ReactiveElement } from "lit";
import { customElement, property } from "lit/decorators";
import relativeTime from "../common/datetime/relative_time";
import { relativeTime } from "../common/datetime/relative_time";
import type { HomeAssistant } from "../types";
@customElement("ha-relative-time")
@ -55,10 +55,7 @@ class HaRelativeTime extends ReactiveElement {
if (!this.datetime) {
this.innerHTML = this.hass.localize("ui.components.relative_time.never");
} else {
this.innerHTML = relativeTime(
new Date(this.datetime),
this.hass.localize
);
this.innerHTML = relativeTime(new Date(this.datetime), this.hass.locale);
}
}
}

View File

@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import LocalizeMixin from "../mixins/localize-mixin";
/*

View File

@ -17,7 +17,7 @@ import {
import { customElement, property } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import relativeTime from "../../common/datetime/relative_time";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import { toggleAttribute } from "../../common/dom/toggle_attribute";
import { LogbookEntry } from "../../data/logbook";
@ -66,11 +66,7 @@ class RenderedTimeTracker {
renderTime(from: Date, to: Date): void {
this.entries.push(html`
<ha-timeline label>
${relativeTime(from, this.hass.localize, {
compareTime: to,
includeTense: false,
})}
later
${relativeTime(from, this.hass.locale, to, false)} later
</ha-timeline>
`);
this.lastReportedTime = to;

View File

@ -11,7 +11,7 @@ import {
} from "home-assistant-js-websocket";
import { css, html, svg, SVGTemplateResult, TemplateResult } from "lit";
import { styleMap } from "lit/directives/style-map";
import { formatNumber } from "../common/string/format_number";
import { formatNumber } from "../common/number/format_number";
import "../components/ha-icon";
import "../components/ha-svg-icon";
import type { HomeAssistant } from "../types";

View File

@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { formatTime } from "../../../common/datetime/format_time";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/ha-relative-time";
import { HomeAssistant } from "../../../types";

View File

@ -31,7 +31,7 @@ import {
import { customElement, property } from "lit/decorators";
import { formatDateWeekday } from "../../../common/datetime/format_date";
import { formatTimeWeekday } from "../../../common/datetime/format_time";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/ha-svg-icon";
import { getWeatherUnit, getWind } from "../../../data/weather";
import { HomeAssistant } from "../../../types";

View File

@ -17,7 +17,7 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
import {
formatNumber,
numberFormatToLocale,
} from "../../../../common/string/format_number";
} from "../../../../common/number/format_number";
import "../../../../components/chart/ha-chart-base";
import type HaChartBase from "../../../../components/chart/ha-chart-base";
import "../../../../components/ha-card";

View File

@ -15,7 +15,7 @@ import { css, html, LitElement, svg } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "@material/mwc-button";
import { formatNumber } from "../../../../common/string/format_number";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import {

View File

@ -32,7 +32,7 @@ import "../../../../components/chart/ha-chart-base";
import {
formatNumber,
numberFormatToLocale,
} from "../../../../common/string/format_number";
} from "../../../../common/number/format_number";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import { FrontendLocaleData } from "../../../../data/translation";
import {

View File

@ -3,7 +3,7 @@ import "@polymer/paper-tooltip";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { formatNumber } from "../../../../common/string/format_number";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-gauge";

View File

@ -33,7 +33,7 @@ import "../../../../components/chart/ha-chart-base";
import {
formatNumber,
numberFormatToLocale,
} from "../../../../common/string/format_number";
} from "../../../../common/number/format_number";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import { FrontendLocaleData } from "../../../../data/translation";
import {

View File

@ -19,7 +19,7 @@ import {
} from "../../../../common/color/convert-color";
import { labDarken } from "../../../../common/color/lab";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { formatNumber } from "../../../../common/string/format_number";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/chart/statistics-chart";
import "../../../../components/ha-card";
import {

View File

@ -17,7 +17,7 @@ import { computeStateName } from "../../../../common/entity/compute_state_name";
import {
formatNumber,
numberFormatToLocale,
} from "../../../../common/string/format_number";
} from "../../../../common/number/format_number";
import "../../../../components/chart/ha-chart-base";
import "../../../../components/ha-card";
import { EnergyData, getEnergyDataCollection } from "../../../../data/energy";

View File

@ -16,7 +16,7 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stateIcon } from "../../../common/entity/state_icon";
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import { iconColorCSS } from "../../../common/style/icon_color_css";
import "../../../components/ha-card";
import "../../../components/ha-icon";

View File

@ -9,7 +9,7 @@ import {
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import relativeTime from "../../../common/datetime/relative_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
@ -330,7 +330,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
: entityConf.show_last_changed
? relativeTime(
new Date(stateObj.last_changed),
this.hass!.localize
this.hass!.locale
)
: computeStateDisplay(
this.hass!.localize,

View File

@ -16,7 +16,7 @@ import { UNIT_F } from "../../../common/const";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/ha-card";
import type { HaCard } from "../../../components/ha-card";
import "../../../components/ha-icon-button";

View File

@ -14,7 +14,7 @@ import { computeStateDisplay } from "../../../common/entity/compute_state_displa
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stateIcon } from "../../../common/entity/state_icon";
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import { debounce } from "../../../common/util/debounce";
import "../../../components/ha-card";
import "../../../components/ha-icon";

View File

@ -3,7 +3,7 @@ import { customElement, property, state } from "lit/decorators";
import { formatDate } from "../../../common/datetime/format_date";
import { formatDateTime } from "../../../common/datetime/format_date_time";
import { formatTime } from "../../../common/datetime/format_time";
import relativeTime from "../../../common/datetime/relative_time";
import { relativeTime } from "../../../common/datetime/relative_time";
import { FrontendLocaleData } from "../../../data/translation";
import { HomeAssistant } from "../../../types";
import { TimestampRenderingFormat } from "./types";
@ -103,11 +103,13 @@ class HuiTimestampDisplay extends LitElement {
if (this.ts && this.hass!.localize) {
this._relative =
this._format === "relative"
? relativeTime(this.ts, this.hass!.localize)
: (this._relative = relativeTime(new Date(), this.hass!.localize, {
compareTime: this.ts,
includeTense: false,
}));
? relativeTime(this.ts, this.hass!.locale)
: (this._relative = relativeTime(
new Date(),
this.hass!.locale,
this.ts,
false
));
}
}
}

View File

@ -14,7 +14,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stateIcon } from "../../../common/entity/state_icon";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/entity/state-badge";
import { UNAVAILABLE_STATES } from "../../../data/entity";
import { ActionHandlerEvent } from "../../../data/lovelace";

View File

@ -8,7 +8,7 @@ import {
} from "lit";
import { customElement, property, state } from "lit/decorators";
import checkValidDate from "../../../common/datetime/check_valid_date";
import { formatNumber } from "../../../common/string/format_number";
import { formatNumber } from "../../../common/number/format_number";
import { HomeAssistant } from "../../../types";
import { formatAttributeValue } from "../../../util/hass-attributes-util";
import { hasConfigOrEntityChanged } from "../common/has-changed";

View File

@ -4,7 +4,7 @@ import { mdiDelete } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import relativeTime from "../../common/datetime/relative_time";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-card";
import "../../components/ha-settings-row";
@ -69,10 +69,7 @@ class HaLongLivedTokens extends LitElement {
${this.hass.localize(
"ui.panel.profile.long_lived_access_tokens.created",
"date",
relativeTime(
new Date(token.created_at),
this.hass.localize
)
relativeTime(new Date(token.created_at), this.hass.locale)
)}
</div>
<mwc-icon-button

View File

@ -3,7 +3,7 @@ import "@polymer/paper-listbox/paper-listbox";
import { html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { formatNumber } from "../../common/string/format_number";
import { formatNumber } from "../../common/number/format_number";
import "../../components/ha-card";
import "../../components/ha-paper-dropdown-menu";
import "../../components/ha-settings-row";

View File

@ -4,7 +4,7 @@ import "@polymer/paper-tooltip/paper-tooltip";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import relativeTime from "../../common/datetime/relative_time";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-card";
import "../../components/ha-settings-row";
@ -64,7 +64,7 @@ class HaRefreshTokens extends LitElement {
{
date: relativeTime(
new Date(token.created_at),
this.hass.localize
this.hass.locale
),
}
)}
@ -76,7 +76,7 @@ class HaRefreshTokens extends LitElement {
{
date: relativeTime(
new Date(token.last_used_at),
this.hass.localize
this.hass.locale
),
location: token.last_used_ip,
}

View File

@ -1,8 +1,19 @@
import "core-js";
import "regenerator-runtime/runtime";
import "lit/polyfill-support";
// For localize
// For localize & formatting
import "@formatjs/intl-getcanonicallocales/polyfill";
import "@formatjs/intl-locale/polyfill";
import "@formatjs/intl-pluralrules/polyfill";
import "@formatjs/intl-pluralrules/locale-data/en";
import "@formatjs/intl-numberformat/polyfill";
import "@formatjs/intl-numberformat/locale-data/en";
import "@formatjs/intl-relativetimeformat/polyfill";
import "@formatjs/intl-relativetimeformat/locale-data/en";
import "@formatjs/intl-datetimeformat/polyfill";
import "@formatjs/intl-datetimeformat/locale-data/en";
// To use comlink under ES5
import "proxy-polyfill";
import "unfetch/polyfill";

View File

@ -436,29 +436,7 @@
}
},
"relative_time": {
"never": "Never",
"just_now": "Just now",
"duration": {
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n}",
"minute": "{count} {count, plural,\n one {minute}\n other {minutes}\n}",
"hour": "{count} {count, plural,\n one {hour}\n other {hours}\n}",
"day": "{count} {count, plural,\n one {day}\n other {days}\n}",
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n}"
},
"past_duration": {
"second": "{count} {count, plural,\n one {second}\n other {seconds}\n} ago",
"minute": "{count} {count, plural,\n one {minute}\n other {minutes}\n} ago",
"hour": "{count} {count, plural,\n one {hour}\n other {hours}\n} ago",
"day": "{count} {count, plural,\n one {day}\n other {days}\n} ago",
"week": "{count} {count, plural,\n one {week}\n other {weeks}\n} ago"
},
"future_duration": {
"second": "In {count} {count, plural,\n one {second}\n other {seconds}\n}",
"minute": "In {count} {count, plural,\n one {minute}\n other {minutes}\n}",
"hour": "In {count} {count, plural,\n one {hour}\n other {hours}\n}",
"day": "In {count} {count, plural,\n one {day}\n other {days}\n}",
"week": "In {count} {count, plural,\n one {week}\n other {weeks}\n}"
}
"never": "Never"
},
"history_charts": {
"history_disabled": "History integration disabled",

View File

@ -1,217 +0,0 @@
import { assert } from "chai";
import relativeTime from "../../../src/common/datetime/relative_time";
describe("relativeTime", () => {
// Mock localize function for testing
const localize = (message, ...args) =>
message + (args.length ? ": " + args.join(",") : "");
it("now", () => {
const now = new Date();
assert.strictEqual(
relativeTime(now, localize, { compareTime: now }),
"ui.components.relative_time.just_now"
);
});
it("past_second", () => {
const inputdt = new Date("2021-02-03T11:22:00+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.past_duration.second: count,33"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.second: count,33"
);
});
it("past_minute", () => {
const inputdt = new Date("2021-02-03T11:20:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.past_duration.minute: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.minute: count,2"
);
});
it("past_hour", () => {
const inputdt = new Date("2021-02-03T09:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.past_duration.hour: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.hour: count,2"
);
});
it("past_day", () => {
let inputdt = new Date("2021-02-01T11:22:33+00:00");
let compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.past_duration.day: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,2"
);
// Test switch from days to weeks
inputdt = new Date("2021-01-28T11:22:33+00:00");
compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,6"
);
inputdt = new Date("2021-01-27T11:22:33+00:00");
compare = new Date("2021-02-03T11:22:33+00:00");
assert.notStrictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,7"
);
});
it("past_week", () => {
const inputdt = new Date("2021-01-03T11:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.past_duration.week: count,4"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.week: count,4"
);
});
it("future_second", () => {
const inputdt = new Date("2021-02-03T11:22:55+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.future_duration.second: count,22"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.second: count,22"
);
});
it("future_minute", () => {
const inputdt = new Date("2021-02-03T11:24:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.future_duration.minute: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.minute: count,2"
);
});
it("future_hour", () => {
const inputdt = new Date("2021-02-03T13:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.future_duration.hour: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.hour: count,2"
);
});
it("future_day", () => {
let inputdt = new Date("2021-02-05T11:22:33+00:00");
let compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.future_duration.day: count,2"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,2"
);
// Test switch from days to weeks
inputdt = new Date("2021-02-09T11:22:33+00:00");
compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,6"
);
inputdt = new Date("2021-02-10T11:22:33+00:00");
compare = new Date("2021-02-03T11:22:33+00:00");
assert.notStrictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.day: count,7"
);
});
it("future_week", () => {
const inputdt = new Date("2021-03-03T11:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, localize, { compareTime: compare }),
"ui.components.relative_time.future_duration.week: count,4"
);
assert.strictEqual(
relativeTime(inputdt, localize, {
compareTime: compare,
includeTense: false,
}),
"ui.components.relative_time.duration.week: count,4"
);
});
});

View File

@ -16,7 +16,7 @@ describe("formatDateTime", () => {
number_format: NumberFormat.language,
time_format: TimeFormat.am_pm,
}),
"November 18, 2017, 11:12 PM"
"November 18, 2017 at 11:12 PM"
);
assert.strictEqual(
formatDateTime(dateObj, {
@ -24,7 +24,7 @@ describe("formatDateTime", () => {
number_format: NumberFormat.language,
time_format: TimeFormat.twenty_four,
}),
"November 18, 2017, 23:12"
"November 18, 2017 at 23:12"
);
});
});
@ -39,7 +39,7 @@ describe("formatDateTimeWithSeconds", () => {
number_format: NumberFormat.language,
time_format: TimeFormat.am_pm,
}),
"November 18, 2017, 11:12:13 PM"
"November 18, 2017 at 11:12:13 PM"
);
assert.strictEqual(
formatDateTimeWithSeconds(dateObj, {
@ -47,7 +47,7 @@ describe("formatDateTimeWithSeconds", () => {
number_format: NumberFormat.language,
time_format: TimeFormat.twenty_four,
}),
"November 18, 2017, 23:12:13"
"November 18, 2017 at 23:12:13"
);
});
});

View File

@ -0,0 +1,115 @@
import { assert } from "chai";
import { relativeTime } from "../../../src/common/datetime/relative_time";
import { NumberFormat, TimeFormat } from "../../../src/data/translation";
describe("relativeTime", () => {
const locale = {
language: "en",
number_format: NumberFormat.language,
time_format: TimeFormat.language,
};
it("now", () => {
const now = new Date();
assert.strictEqual(relativeTime(now, locale, now), "now");
assert.strictEqual(relativeTime(now, locale, now, false), "0 seconds");
});
it("past_second", () => {
const inputdt = new Date("2021-02-03T11:22:00+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(
relativeTime(inputdt, locale, compare),
"33 seconds ago"
);
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"33 seconds"
);
});
it("past_minute", () => {
const inputdt = new Date("2021-02-03T11:20:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 minutes ago");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"2 minutes"
);
});
it("past_hour", () => {
const inputdt = new Date("2021-02-03T09:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 hours ago");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"2 hours"
);
});
it("past_day", () => {
const inputdt = new Date("2021-02-01T11:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "2 days ago");
assert.strictEqual(relativeTime(inputdt, locale, compare, false), "2 days");
});
it("future_second", () => {
const inputdt = new Date("2021-02-03T11:22:55+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 22 seconds");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"22 seconds"
);
});
it("future_minute", () => {
const inputdt = new Date("2021-02-03T11:24:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 minutes");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"2 minutes"
);
});
it("future_hour", () => {
const inputdt = new Date("2021-02-03T13:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 hours");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"2 hours"
);
});
it("future_day", () => {
const inputdt = new Date("2021-02-05T11:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 2 days");
assert.strictEqual(relativeTime(inputdt, locale, compare, false), "2 days");
});
it("future_week", () => {
const inputdt = new Date("2021-03-24T11:22:33+00:00");
const compare = new Date("2021-03-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "in 3 weeks");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"3 weeks"
);
});
it("future_month", () => {
const inputdt = new Date("2021-03-03T11:22:33+00:00");
const compare = new Date("2021-02-03T11:22:33+00:00");
assert.strictEqual(relativeTime(inputdt, locale, compare), "next month");
assert.strictEqual(
relativeTime(inputdt, locale, compare, false),
"1 month"
);
});
});

View File

@ -172,14 +172,14 @@ describe("computeStateDisplay", () => {
it("Uses am/pm time format", () => {
assert.strictEqual(
computeStateDisplay(localize, stateObj, localeData),
"November 18, 2017, 11:12 PM"
"November 18, 2017 at 11:12 PM"
);
});
it("Uses 24h time format", () => {
localeData.time_format = TimeFormat.twenty_four;
assert.strictEqual(
computeStateDisplay(localize, stateObj, localeData),
"November 18, 2017, 23:12"
"November 18, 2017 at 23:12"
);
});
});
@ -259,7 +259,7 @@ describe("computeStateDisplay", () => {
localeData,
"2021-07-04 15:40:03"
),
"July 4, 2021, 3:40 PM"
"July 4, 2021 at 3:40 PM"
);
});
it("Uses 24h time format", () => {
@ -271,7 +271,7 @@ describe("computeStateDisplay", () => {
localeData,
"2021-07-04 15:40:03"
),
"July 4, 2021, 15:40"
"July 4, 2021 at 15:40"
);
});
});

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { formatNumber } from "../../../src/common/string/format_number";
import { formatNumber } from "../../../src/common/number/format_number";
import {
FrontendLocaleData,
NumberFormat,

12
test/setup.js Normal file
View File

@ -0,0 +1,12 @@
const fs = require("fs");
const path = require("path");
process.env.TZ = "Etc/UTC";
process.env.IS_TEST = "true";
const MDI_OUTPUT_DIR = path.resolve(__dirname, "../build/mdi");
if (!fs.existsSync(MDI_OUTPUT_DIR)) {
fs.mkdirSync(MDI_OUTPUT_DIR, { recursive: true });
fs.writeFileSync(path.resolve(MDI_OUTPUT_DIR, "iconMetadata.json"), "{}");
}

7
test/webpack.config.js Normal file
View File

@ -0,0 +1,7 @@
const { createAppConfig } = require("../build-scripts/webpack.js");
module.exports = createAppConfig({
isProdBuild: false,
latestBuild: true,
isStatsBuild: false,
});

1060
yarn.lock

File diff suppressed because it is too large Load Diff