diff --git a/addons/web/static/src/core/l10n/dates.js b/addons/web/static/src/core/l10n/dates.js index 9a1fd980306..a6ace94719a 100644 --- a/addons/web/static/src/core/l10n/dates.js +++ b/addons/web/static/src/core/l10n/dates.js @@ -5,7 +5,7 @@ import { _t } from "@web/core/l10n/translation"; import { memoize } from "@web/core/utils/functions"; import { sprintf } from "@web/core/utils/strings"; -const { DateTime } = luxon; +const { DateTime, Settings } = luxon; const SERVER_DATE_FORMAT = "yyyy-MM-dd"; const SERVER_TIME_FORMAT = "HH:mm:ss"; @@ -174,6 +174,12 @@ export function formatDate(value, options = {}) { * * Default=false. * + * @param {string} [options.numberingSystem] + * Provided numbering system used to parse the input value. + * + * Default=the default numbering system assigned to luxon + * @see localization_service.js + * * @returns {string} */ export function formatDateTime(value, options = {}) { @@ -181,9 +187,10 @@ export function formatDateTime(value, options = {}) { return ""; } const format = options.format || localization.dateTimeFormat; + const numberingSystem = options.numberingSystem || Settings.defaultNumberingSystem; const zone = options.timezone ? "system" : "utc"; value = value.setZone(zone, { keepLocaltime: options.timezone }); - return value.toFormat(format); + return value.toFormat(format, { numberingSystem }); } // ----------------------------------------------------------------------------- @@ -231,6 +238,17 @@ export function parseDate(value, options = {}) { * * Default=false. * + * @param {string} [options.locale] + * Provided locale used to parse the input value. + * + * Default=the session localization locale + * + * @param {string} [options.numberingSystem] + * Provided numbering system used to parse the input value. + * + * Default=the default numbering system assigned to luxon + * @see localization_service.js + * * @returns {DateTime | false} Luxon DateTime object */ export function parseDateTime(value, options = {}) { @@ -243,6 +261,7 @@ export function parseDateTime(value, options = {}) { setZone: true, zone: options.timezone ? "system" : "utc", locale: options.locale, + numberingSystem: options.numberingSystem || Settings.defaultNumberingSystem, }; let result = constrain(parseSmartDateInput(value)); @@ -283,13 +302,40 @@ export function parseDateTime(value, options = {}) { return options.timezone ? result : result.toUTC(); } +/** + * Returns a date object parsed from the given serialized string. + * @param {string} value + * @returns {DateTime | false} + */ +export const deserializeDate = (value) => { + return parseDate(value, { + format: SERVER_DATE_FORMAT, + numberingSystem: "latn", + }); +}; + +/** + * Returns a datetime object parsed from the given serialized string. + * @param {string} value + * @returns {DateTime | false} + */ +export const deserializeDateTime = (value) => { + return parseDateTime(value, { + format: `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`, + numberingSystem: "latn", + }); +}; + /** * Returns a serialized string representing the given date. * @param {DateTime} value * @returns {string} */ export const serializeDate = (value) => { - return formatDate(value, { format: SERVER_DATE_FORMAT }); + return formatDate(value, { + format: SERVER_DATE_FORMAT, + numberingSystem: "latn", + }); }; /** @@ -298,5 +344,8 @@ export const serializeDate = (value) => { * @returns {string} */ export const serializeDateTime = (value) => { - return formatDateTime(value, { format: `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}` }); + return formatDateTime(value, { + format: `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`, + numberingSystem: "latn", + }); }; diff --git a/addons/web/static/src/search/utils/dates.js b/addons/web/static/src/search/utils/dates.js index 864cbf8f9c2..50f7039a86d 100644 --- a/addons/web/static/src/search/utils/dates.js +++ b/addons/web/static/src/search/utils/dates.js @@ -2,6 +2,7 @@ import { _lt } from "@web/core/l10n/translation"; import { Domain } from "@web/core/domain"; +import { serializeDate, serializeDateTime } from "@web/core/l10n/dates"; import { localization } from "@web/core/l10n/localization"; export const DEFAULT_PERIOD = "this_month"; @@ -209,18 +210,21 @@ export function constructDateRange(params) { setParam.month = QUARTERS[setParam.quarter].coveredMonths[0]; delete setParam.quarter; } - const date = referenceMoment.set(setParam).plus(plusParam || {}); + const date = referenceMoment + .set(setParam) + .plus(plusParam || {}) + .setZone("utc", { keepLocalTime: true }); // compute domain let leftDate = date.startOf(granularity); let rightDate = date.endOf(granularity); let leftBound; let rightBound; if (fieldType === "date") { - leftBound = leftDate.toFormat("yyyy-MM-dd"); - rightBound = rightDate.toFormat("yyyy-MM-dd"); + leftBound = serializeDate(leftDate); + rightBound = serializeDate(rightDate); } else { - leftBound = leftDate.toUTC().toFormat("yyyy-MM-dd HH:mm:ss"); - rightBound = rightDate.toUTC().toFormat("yyyy-MM-dd HH:mm:ss"); + leftBound = serializeDateTime(leftDate); + rightBound = serializeDateTime(rightDate); } const domain = new Domain(["&", [fieldName, ">=", leftBound], [fieldName, "<=", rightBound]]); // compute description diff --git a/addons/web/static/tests/core/l10n/dates_tests.js b/addons/web/static/tests/core/l10n/dates_tests.js index f5729a5de61..e9033d84628 100644 --- a/addons/web/static/tests/core/l10n/dates_tests.js +++ b/addons/web/static/tests/core/l10n/dates_tests.js @@ -6,6 +6,10 @@ import { parseDate, parseDateTime, strftimeToLuxonFormat, + serializeDate, + serializeDateTime, + deserializeDate, + deserializeDateTime, } from "@web/core/l10n/dates"; import { localization } from "@web/core/l10n/localization"; import { patch, unpatch } from "@web/core/utils/patch"; @@ -359,6 +363,80 @@ QUnit.module( unpatch(localization, "default loc"); }); + QUnit.test("serializeDate", async (assert) => { + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual(date.toFormat("yyyy-MM-dd"), "2022-02-21"); + assert.strictEqual(serializeDate(date), "2022-02-21"); + }); + + QUnit.test("serializeDate with different numbering system", async (assert) => { + patchWithCleanup(Settings, { defaultNumberingSystem: "arab" }); + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual(date.toFormat("yyyy-MM-dd"), "٢٠٢٢-٠٢-٢١"); + assert.strictEqual(serializeDate(date), "2022-02-21"); + }); + + QUnit.test("serializeDateTime", async (assert) => { + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual(date.toFormat("yyyy-MM-dd HH:mm:ss"), "2022-02-21 16:11:42"); + assert.strictEqual(serializeDateTime(date), "2022-02-21 16:11:42"); + }); + + QUnit.test("serializeDateTime with different numbering system", async (assert) => { + patchWithCleanup(Settings, { defaultNumberingSystem: "arab" }); + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual(date.toFormat("yyyy-MM-dd HH:mm:ss"), "٢٠٢٢-٠٢-٢١ ١٦:١١:٤٢"); + assert.strictEqual(serializeDateTime(date), "2022-02-21 16:11:42"); + }); + + QUnit.test("deserializeDate", async (assert) => { + const date = DateTime.utc(2022, 2, 21); + assert.strictEqual( + DateTime.fromFormat("2022-02-21", "yyyy-MM-dd", { zone: "utc" }).toMillis(), + date.toMillis() + ); + assert.strictEqual(deserializeDate("2022-02-21").toMillis(), date.toMillis()); + }); + + QUnit.test("deserializeDate with different numbering system", async (assert) => { + patchWithCleanup(Settings, { defaultNumberingSystem: "arab" }); + const date = DateTime.utc(2022, 2, 21); + assert.strictEqual( + DateTime.fromFormat("٢٠٢٢-٠٢-٢١", "yyyy-MM-dd", { zone: "utc" }).toMillis(), + date.toMillis() + ); + assert.strictEqual(deserializeDate("2022-02-21").toMillis(), date.toMillis()); + }); + + QUnit.test("deserializeDateTime", async (assert) => { + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual( + DateTime.fromFormat("2022-02-21 16:11:42", "yyyy-MM-dd HH:mm:ss", { + zone: "utc", + }).toMillis(), + date.toMillis() + ); + assert.strictEqual( + deserializeDateTime("2022-02-21 16:11:42").toMillis(), + date.toMillis() + ); + }); + + QUnit.test("deserializeDateTime with different numbering system", async (assert) => { + patchWithCleanup(Settings, { defaultNumberingSystem: "arab" }); + const date = DateTime.utc(2022, 2, 21, 16, 11, 42); + assert.strictEqual( + DateTime.fromFormat("٢٠٢٢-٠٢-٢١ ١٦:١١:٤٢", "yyyy-MM-dd HH:mm:ss", { + zone: "utc", + }).toMillis(), + date.toMillis() + ); + assert.strictEqual( + deserializeDateTime("2022-02-21 16:11:42").toMillis(), + date.toMillis() + ); + }); + QUnit.module("dates utils compatibility with legacy", { beforeEach() { patchWithCleanup(localization, {