[FIX] web: use right datetime format in daterange field

- First issue
The format passed to the daterangepicker locale options could have been
wrong as the daterangepicker lib uses moment and we would pass to it
the date(time) format expressed with luxon's format tokens.
Furthermore, there was no need to pass the format to the daterangepicker
library as the DateRangeField component already handles this.

- Second issue
There was also another issue with the DateRangeField component:
When applying the daterangepicker values, the conversion between a
moment object to a luxon DateTime object was wrong because of the format
used to convert the moment object to a string.

- Explanation
The format tokens are different between luxon and moment.

- After this commit
An helper function has been introduced in order to transform a luxon format into a moment format.
The DateRangeField component now properly converts a moment object to
a luxon's DateTime object when applying the daterangepicker selection.

- Additional note
Also, the DatePicker component was locally converting the luxon's
datetime format and thus has been adapted to use the new helper.

closes odoo/odoo#98578

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Bruno Boi
2022-08-23 16:29:22 +02:00
committed by Aaron Bohy
parent 83719ce376
commit fff7334d09
4 changed files with 49 additions and 14 deletions
@@ -4,6 +4,7 @@ import {
areDateEquals,
formatDate,
formatDateTime,
luxonToMomentFormat,
parseDate,
parseDateTime,
} from "@web/core/l10n/dates";
@@ -33,14 +34,6 @@ function luxonDateToMomentDate(date) {
return window.moment(String(date));
}
/**
* @param {string} format
* @returns {string}
*/
function luxonFormatToMomentFormat(format) {
return format.replace(/[dy]/g, (x) => x.toUpperCase());
}
/**
* @param {string} format
* @returns {boolean}
@@ -198,7 +191,7 @@ export class DatePicker extends Component {
const params = {
...commandOrParams,
date: this.date || null,
format: luxonFormatToMomentFormat(format),
format: luxonToMomentFormat(format),
locale: commandOrParams.locale || (this.date && this.date.locale),
};
for (const prop in params) {
+21
View File
@@ -44,6 +44,14 @@ const normalizeFormatTable = {
X: "HH:mm:ss",
};
const luxonToMomentFormatTable = {
c: "d",
d: "D",
o: "DDDD",
a: "A",
y: "Y",
};
const smartDateUnits = {
d: "days",
m: "months",
@@ -143,6 +151,19 @@ export const strftimeToLuxonFormat = memoize(function strftimeToLuxonFormat(valu
return output.join("");
});
/**
* Converts a Luxon format to a moment.js format.
* NB: this is not a complete conversion, only the supported tokens are converted.
*
* @param {string} value original format
* @returns {string} valid moment.js format
*/
export const luxonToMomentFormat = memoize(function luxonToMomentFormat(format) {
return format.replace(alphaRegex, (match) => {
return luxonToMomentFormatTable[match] || match;
});
});
// -----------------------------------------------------------------------------
// Formatting
// -----------------------------------------------------------------------------
@@ -3,6 +3,7 @@
import { localization } from "@web/core/l10n/localization";
import { registry } from "@web/core/registry";
import { loadJS } from "@web/core/assets";
import { luxonToMomentFormat } from "@web/core/l10n/dates";
import { useService } from "@web/core/utils/hooks";
import { standardFieldProps } from "../standard_field_props";
@@ -16,6 +17,9 @@ export class DateRangeField extends Component {
this.root = useRef("root");
this.isPickerShown = false;
this.pickerContainer;
this.momentFormat = luxonToMomentFormat(
this.isDateTime ? localization.dateTimeFormat : localization.dateFormat
);
useExternalListener(window, "scroll", this.onWindowScroll, { capture: true });
onWillStart(() => loadJS("/web/static/lib/daterangepicker/daterangepicker.js"));
@@ -32,9 +36,6 @@ export class DateRangeField extends Component {
locale: {
applyLabel: this.env._t("Apply"),
cancelLabel: this.env._t("Cancel"),
format: this.isDateTime
? localization.dateTimeFormat
: localization.dateFormat,
},
startDate: start ? window.moment(start) : window.moment(),
endDate: end ? window.moment(end) : window.moment(),
@@ -126,10 +127,9 @@ export class DateRangeField extends Component {
async onPickerApply(ev, picker) {
const start = this.isDateTime ? picker.startDate : picker.startDate.startOf("day");
const end = this.isDateTime ? picker.endDate : picker.endDate.startOf("day");
const format = this.isDateTime ? localization.dateTimeFormat : localization.dateFormat;
const parser = parsers.get(this.props.formatType);
const dates = [start, end].map((date) => {
return parser(date.format(format.toUpperCase()), {
return parser(date.format(this.momentFormat), {
timezone: this.isDateTime,
});
});
@@ -191,6 +191,27 @@ QUnit.module("Fields", (hooks) => {
"30",
"active end date minute should be '30' in date range picker"
);
// Select a new range and check that inputs are updated
await triggerEvent(datepicker, ".start-date", "mousedown"); // 02/08/2017
await triggerEvent(datepicker, ".start-date + .available", "mousedown"); // 02/09/2017
await click(datepicker, ".applyBtn");
assert.equal(
target.querySelector("[name=datetime] input").value,
"02/08/2017 15:30:00"
);
assert.equal(
target.querySelector("[name=datetime_end] input").value,
"02/09/2017 05:30:00"
);
// Save
await click(target, ".o_form_button_save");
fields = target.querySelectorAll(".o_field_daterange");
// Check date after save
assert.strictEqual(fields[0].textContent, "02/08/2017 15:30:00");
assert.strictEqual(fields[fields.length - 1].textContent, "02/09/2017 05:30:00");
}
);