[IMP] web: account for local timezone when formatting dates
formatDate and formatDateTime had their default 'timezone' option set to false but this was changed to true in order to have the same behavior as the legacy formatDate and formatDateTime functions. Part-of: odoo/odoo#96946
This commit is contained in:
@@ -44,8 +44,10 @@ registerModel({
|
||||
return format.date(moment.utc(this.value));
|
||||
}
|
||||
return format.date(this.value);
|
||||
case 'datetime':
|
||||
return formatters.get("datetime")(deserializeDateTime(this.value), { timezone: true });
|
||||
case 'datetime': {
|
||||
const value = this.value ? deserializeDateTime(this.value) : this.value;
|
||||
return formatters.get("datetime")(value, { timezone: true });
|
||||
}
|
||||
case 'float':
|
||||
return format.float(this.value);
|
||||
case 'integer':
|
||||
|
||||
@@ -9,6 +9,7 @@ const { DateTime, Settings } = luxon;
|
||||
|
||||
const SERVER_DATE_FORMAT = "yyyy-MM-dd";
|
||||
const SERVER_TIME_FORMAT = "HH:mm:ss";
|
||||
const SERVER_DATETIME_FORMAT = `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -173,11 +174,11 @@ export function formatDate(value, options = {}) {
|
||||
*
|
||||
* Default=the session localization format.
|
||||
*
|
||||
* @param {boolean} [options.timezone]
|
||||
* @param {boolean} [options.timezone=true]
|
||||
* - True = input will be set in local time before being formatted.
|
||||
* - False = input will be set in UTC time before being formatted.
|
||||
*
|
||||
* Default=false.
|
||||
* Default=true.
|
||||
*
|
||||
* @param {string} [options.numberingSystem]
|
||||
* Provided numbering system used to parse the input value.
|
||||
@@ -193,7 +194,7 @@ export function formatDateTime(value, options = {}) {
|
||||
}
|
||||
const format = options.format || localization.dateTimeFormat;
|
||||
const numberingSystem = options.numberingSystem || Settings.defaultNumberingSystem || "latn";
|
||||
const zone = options.timezone ? "default" : "utc";
|
||||
const zone = !("timezone" in options) || options.timezone ? "default" : "utc";
|
||||
value = value.setZone(zone, { keepLocaltime: options.timezone });
|
||||
return value.toFormat(format, { numberingSystem });
|
||||
}
|
||||
@@ -236,7 +237,7 @@ export function parseDate(value, options = {}) {
|
||||
*
|
||||
* Default=the session localization format
|
||||
*
|
||||
* @param {boolean} [options.timezone]
|
||||
* @param {boolean} [options.timezone=false]
|
||||
* - True = input value is considered being in localtime.
|
||||
* - False = input value is considered being in utc time, and the returned
|
||||
* value will have the UTC zone.
|
||||
@@ -345,10 +346,7 @@ export function parseDateTime(value, options = {}) {
|
||||
* @returns {DateTime | false}
|
||||
*/
|
||||
export function deserializeDate(value) {
|
||||
return parseDate(value, {
|
||||
format: SERVER_DATE_FORMAT,
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
return DateTime.fromSQL(value, { zone: "utc", numberingSystem: "latn" });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,10 +355,7 @@ export function deserializeDate(value) {
|
||||
* @returns {DateTime | false}
|
||||
*/
|
||||
export function deserializeDateTime(value) {
|
||||
return parseDateTime(value, {
|
||||
format: `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`,
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
return DateTime.fromSQL(value, { zone: "utc", numberingSystem: "latn" });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,10 +364,7 @@ export function deserializeDateTime(value) {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function serializeDate(value) {
|
||||
return formatDate(value, {
|
||||
format: SERVER_DATE_FORMAT,
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
return value.setZone("utc").toFormat(SERVER_DATE_FORMAT, { numberingSystem: "latn" });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,8 +373,5 @@ export function serializeDate(value) {
|
||||
* @returns {string}
|
||||
*/
|
||||
export function serializeDateTime(value) {
|
||||
return formatDateTime(value, {
|
||||
format: `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`,
|
||||
numberingSystem: "latn",
|
||||
});
|
||||
return value.setZone("utc").toFormat(SERVER_DATETIME_FORMAT, { numberingSystem: "latn" });
|
||||
}
|
||||
|
||||
@@ -60,12 +60,8 @@ class GetMetadataDialog extends Component {
|
||||
this.state.creator = formatMany2one(metadata.create_uid);
|
||||
this.state.lastModifiedBy = formatMany2one(metadata.write_uid);
|
||||
this.state.noupdate = metadata.noupdate;
|
||||
this.state.createDate = formatDateTime(parseDateTime(metadata.create_date), {
|
||||
timezone: true,
|
||||
});
|
||||
this.state.writeDate = formatDateTime(parseDateTime(metadata.write_date), {
|
||||
timezone: true,
|
||||
});
|
||||
this.state.createDate = formatDateTime(parseDateTime(metadata.create_date));
|
||||
this.state.writeDate = formatDateTime(parseDateTime(metadata.write_date));
|
||||
}
|
||||
}
|
||||
GetMetadataDialog.template = "web.DebugMenu.GetMetadataDialog";
|
||||
|
||||
@@ -479,7 +479,7 @@ export class Record extends DataPoint {
|
||||
if (data[fieldName]) {
|
||||
const deserialize =
|
||||
fieldType === "date" ? deserializeDate : deserializeDateTime;
|
||||
data[fieldName] = deserialize(JSON.stringify(data[fieldName]));
|
||||
data[fieldName] = deserialize(data[fieldName].toJSON());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -158,12 +158,8 @@ class GetMetadataDialog extends Component {
|
||||
this.state.noupdate = metadata.noupdate;
|
||||
this.state.creator = formatMany2one(metadata.create_uid);
|
||||
this.state.lastModifiedBy = formatMany2one(metadata.write_uid);
|
||||
this.state.createDate = formatDateTime(parseDateTime(metadata.create_date), {
|
||||
timezone: true,
|
||||
});
|
||||
this.state.writeDate = formatDateTime(parseDateTime(metadata.write_date), {
|
||||
timezone: true,
|
||||
});
|
||||
this.state.createDate = formatDateTime(parseDateTime(metadata.create_date));
|
||||
this.state.writeDate = formatDateTime(parseDateTime(metadata.write_date));
|
||||
}
|
||||
}
|
||||
GetMetadataDialog.template = "web.DebugMenu.GetMetadataDialog";
|
||||
|
||||
@@ -10,7 +10,7 @@ const { Component } = owl;
|
||||
|
||||
export class DateTimeField extends Component {
|
||||
get formattedValue() {
|
||||
return formatDateTime(this.props.value, { timezone: true });
|
||||
return formatDateTime(this.props.value);
|
||||
}
|
||||
|
||||
onDateTimeChanged(date) {
|
||||
|
||||
@@ -2468,15 +2468,9 @@ export class Group extends DataPoint {
|
||||
return false;
|
||||
}
|
||||
if (type === "date") {
|
||||
return serializeDate(
|
||||
DateTime.fromFormat(range.to, "yyyy-MM-dd", { zone: "utc" }).minus({
|
||||
day: 1,
|
||||
})
|
||||
);
|
||||
return serializeDate(deserializeDate(range.to).minus({ day: 1 }));
|
||||
} else {
|
||||
return serializeDateTime(
|
||||
DateTime.fromFormat(range.to, "yyyy-MM-dd HH:mm:ss").minus({ second: 1 })
|
||||
);
|
||||
return serializeDateTime(deserializeDateTime(range.to).minus({ second: 1 }));
|
||||
}
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -19,7 +19,7 @@ import session from "web.session";
|
||||
import test_utils from "web.test_utils";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import { defaultLocalization } from "../../helpers/mock_services";
|
||||
import { patchWithCleanup } from "../../helpers/utils";
|
||||
import { patchDate, patchWithCleanup } from "../../helpers/utils";
|
||||
|
||||
const { DateTime, Settings } = luxon;
|
||||
|
||||
@@ -119,42 +119,51 @@ QUnit.module(
|
||||
unpatch(localization, "dateformat");
|
||||
});
|
||||
|
||||
QUnit.test("formatDate (with DateTime.fromISO)", async (assert) => {
|
||||
patchWithCleanup(localization, { dateFormat: "MM/dd/yyyy" });
|
||||
|
||||
// NB: the local timezone in test env is UTC+1
|
||||
let date = DateTime.fromISO("2022-07-21T22:00:00Z");
|
||||
assert.strictEqual(formatDate(date), "07/21/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/21/2022");
|
||||
|
||||
date = DateTime.fromISO("2022-07-21T23:00:00Z");
|
||||
assert.strictEqual(formatDate(date), "07/21/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/22/2022");
|
||||
|
||||
date = DateTime.fromISO("2022-07-22T00:00:00Z");
|
||||
assert.strictEqual(formatDate(date), "07/22/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/22/2022");
|
||||
|
||||
// Here are the 3 same instants as above, but expressed from a different ISO string
|
||||
date = DateTime.fromISO("2022-07-22T00:00:00+02:00");
|
||||
assert.strictEqual(formatDate(date), "07/21/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/21/2022");
|
||||
|
||||
date = DateTime.fromISO("2022-07-22T01:00:00+02:00");
|
||||
assert.strictEqual(formatDate(date), "07/21/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/22/2022");
|
||||
|
||||
date = DateTime.fromISO("2022-07-22T02:00:00+02:00");
|
||||
assert.strictEqual(formatDate(date), "07/22/2022");
|
||||
assert.strictEqual(formatDate(date, { timezone: true }), "07/22/2022");
|
||||
});
|
||||
|
||||
QUnit.test("formatDateTime", async (assert) => {
|
||||
patch(localization, "datetimeformat", { dateTimeFormat: "MM/dd/yyyy HH:mm:ss" });
|
||||
|
||||
let formatted = formatDateTime(DateTime.utc(2009, 5, 4, 12, 34, 23));
|
||||
let expected = "05/04/2009 12:34:23";
|
||||
assert.strictEqual(formatted, expected);
|
||||
|
||||
formatted = formatDateTime(DateTime.utc(2009, 5, 4, 12, 34, 23), { timezone: false });
|
||||
assert.strictEqual(formatted, expected);
|
||||
|
||||
formatted = formatDateTime(DateTime.utc(2009, 5, 4, 12, 34, 23), { timezone: true });
|
||||
expected = "05/04/2009 13:34:23";
|
||||
assert.strictEqual(formatted, expected);
|
||||
|
||||
unpatch(localization, "datetimeformat");
|
||||
patchWithCleanup(localization, { dateTimeFormat: "MM/dd/yyyy HH:mm:ss" });
|
||||
const date = DateTime.utc(2009, 5, 4, 12, 34, 23);
|
||||
assert.strictEqual(formatDateTime(date), "05/04/2009 13:34:23");
|
||||
assert.strictEqual(formatDateTime(date, { timezone: false }), "05/04/2009 12:34:23");
|
||||
});
|
||||
|
||||
QUnit.test("formatDateTime (with different timezone offset)", async (assert) => {
|
||||
patch(localization, "datetimeformat", { dateTimeFormat: "MM/dd/yyyy HH:mm:ss" });
|
||||
|
||||
let str = formatDateTime(DateTime.utc(2017, 1, 1, 10, 0, 0, 0));
|
||||
assert.strictEqual(str, "01/01/2017 10:00:00");
|
||||
str = formatDateTime(DateTime.utc(2017, 6, 1, 10, 0, 0, 0));
|
||||
assert.strictEqual(str, "06/01/2017 10:00:00");
|
||||
|
||||
str = formatDateTime(DateTime.utc(2017, 1, 1, 10, 0, 0, 0), { timezone: false });
|
||||
assert.strictEqual(str, "01/01/2017 10:00:00");
|
||||
str = formatDateTime(DateTime.utc(2017, 6, 1, 10, 0, 0, 0), { timezone: false });
|
||||
assert.strictEqual(str, "06/01/2017 10:00:00");
|
||||
|
||||
str = formatDateTime(DateTime.utc(2017, 1, 1, 10, 0, 0, 0), { timezone: true });
|
||||
assert.strictEqual(str, "01/01/2017 11:00:00");
|
||||
str = formatDateTime(DateTime.utc(2017, 6, 1, 10, 0, 0, 0), { timezone: true });
|
||||
assert.strictEqual(str, "06/01/2017 11:00:00");
|
||||
|
||||
unpatch(localization, "datetimeformat");
|
||||
patchWithCleanup(localization, { dateTimeFormat: "MM/dd/yyyy HH:mm:ss" });
|
||||
let date = DateTime.fromISO("2017-01-01T11:00:00+01:00");
|
||||
assert.strictEqual(formatDateTime(date), "01/01/2017 11:00:00");
|
||||
assert.strictEqual(formatDateTime(date, { timezone: false }), "01/01/2017 10:00:00");
|
||||
date = DateTime.fromISO("2017-06-01T11:00:00+02:00");
|
||||
assert.strictEqual(formatDateTime(date), "06/01/2017 10:00:00");
|
||||
assert.strictEqual(formatDateTime(date, { timezone: false }), "06/01/2017 09:00:00");
|
||||
});
|
||||
|
||||
QUnit.test("parseDateTime", async (assert) => {
|
||||
@@ -223,6 +232,24 @@ QUnit.module(
|
||||
unpatch(localization, "weird loc");
|
||||
});
|
||||
|
||||
QUnit.test("parseDate", async (assert) => {
|
||||
patchWithCleanup(localization, defaultLocalization);
|
||||
|
||||
let str = "07/21/2022";
|
||||
assert.strictEqual(parseDate(str).toISO(), "2022-07-21T00:00:00.000Z");
|
||||
assert.strictEqual(
|
||||
parseDate(str, { timezone: true }).toISO(),
|
||||
"2022-07-21T00:00:00.000+01:00"
|
||||
);
|
||||
|
||||
str = "07/22/2022";
|
||||
assert.strictEqual(parseDate(str).toISO(), "2022-07-22T00:00:00.000Z");
|
||||
assert.strictEqual(
|
||||
parseDate(str, { timezone: true }).toISO(),
|
||||
"2022-07-22T00:00:00.000+01:00"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("parseDate without separator", async (assert) => {
|
||||
const dateFormat = strftimeToLuxonFormat("%d.%m/%Y");
|
||||
const timeFormat = strftimeToLuxonFormat("%H:%M:%S");
|
||||
@@ -373,6 +400,24 @@ QUnit.module(
|
||||
assert.strictEqual(serializeDate(date), "2022-02-21");
|
||||
});
|
||||
|
||||
QUnit.test("serializeDate, with DateTime.now()", async (assert) => {
|
||||
patchDate(2022, 1, 21, 16, 11, 42);
|
||||
const date = DateTime.now();
|
||||
assert.strictEqual(date.toFormat("yyyy-MM-dd"), "2022-02-21");
|
||||
assert.strictEqual(serializeDate(date), "2022-02-21");
|
||||
});
|
||||
|
||||
QUnit.test("serializeDate, with DateTime.now(), midnight", async (assert) => {
|
||||
patchDate(2022, 1, 21, 0, 0, 0);
|
||||
const date = DateTime.now();
|
||||
assert.strictEqual(date.toFormat("yyyy-MM-dd"), "2022-02-21");
|
||||
assert.strictEqual(
|
||||
serializeDate(date),
|
||||
"2022-02-20",
|
||||
"serializeDate should output an UTC converted string"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("serializeDate with different numbering system", async (assert) => {
|
||||
patchWithCleanup(Settings, { defaultNumberingSystem: "arab" });
|
||||
const date = DateTime.utc(2022, 2, 21, 16, 11, 42);
|
||||
@@ -386,6 +431,28 @@ QUnit.module(
|
||||
assert.strictEqual(serializeDateTime(date), "2022-02-21 16:11:42");
|
||||
});
|
||||
|
||||
QUnit.test("serializeDateTime, with DateTime.now()", async (assert) => {
|
||||
patchDate(2022, 1, 21, 16, 11, 42);
|
||||
const date = DateTime.now();
|
||||
assert.strictEqual(date.toFormat("yyyy-MM-dd HH:mm:ss"), "2022-02-21 16:11:42");
|
||||
assert.strictEqual(
|
||||
serializeDateTime(date),
|
||||
"2022-02-21 15:11:42",
|
||||
"serializeDateTime should output an UTC converted string"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("serializeDateTime, with DateTime.now(), midnight", async (assert) => {
|
||||
patchDate(2022, 1, 21, 0, 0, 0);
|
||||
const date = DateTime.now();
|
||||
assert.strictEqual(date.toFormat("yyyy-MM-dd HH:mm:ss"), "2022-02-21 00:00:00");
|
||||
assert.strictEqual(
|
||||
serializeDateTime(date),
|
||||
"2022-02-20 23:00:00",
|
||||
"serializeDateTime should output an UTC converted string"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("serializeDateTime with different numbering system", async (assert) => {
|
||||
patchWithCleanup(Settings, { defaultNumberingSystem: "arab" });
|
||||
const date = DateTime.utc(2022, 2, 21, 16, 11, 42);
|
||||
|
||||
Reference in New Issue
Block a user