[FIX] web: Force proper numbering system for server dates

Before this commit, the numbering system used by luxon to parse and
format dates was the one set by the current locale. This means that
any date sent to and received from the server would also be expected
to use the current locale and numbering system. The consequence of
this behavior is that any domain generated by arbitrary dates would
trigger a server error.

This commit forces the numbering system to 'latn' (browser default) for
dates used by the server.

closes odoo/odoo#85487

X-original-commit: c8ef1fcb2277993f90a8e237dbd415de7d1b88bc
Related: odoo/enterprise#24822
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Julien Mougenot (jum) <jum@odoo.com>
This commit is contained in:
Julien Mougenot
2022-02-28 14:47:03 +00:00
parent 2ac7e228f9
commit 424e46bcfe
3 changed files with 140 additions and 9 deletions
+53 -4
View File
@@ -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",
});
};
+9 -5
View File
@@ -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
@@ -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, {