[REF][IMP] web: tidying up core/l10n/dates

- file is now ordered by clean sections
- `confirm()` helper has been renamed to something more appropriate
- `strftimeToLuxonFormat()` was manually cached. It now uses `memoize()` from `core/utils/functions`
- memoization of `stripAlphaDupes()` because it is prone to get called really often with same arguments.
- no more export `parseSmartDateInput()` as it is never imported elsewhere.
- remove unecessary `setLocale` call in `luxon.DateTime.prototype.toJSON` patch

closes odoo-dev/odoo#867

Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
This commit is contained in:
Bruno Boi
2021-06-18 21:31:29 +02:00
committed by Aaron Bohy
parent 8daff40725
commit f05ab3cb43
+118 -120
View File
@@ -1,5 +1,6 @@
/** @odoo-module **/
import { memoize } from "../utils/functions";
import { sprintf } from "../utils/strings";
import { localization } from "./localization";
import { _lt } from "./translation";
@@ -10,14 +11,20 @@ const { DateTime } = luxon;
* Change the method toJSON to return the formated value to send server side.
*/
DateTime.prototype.toJSON = function () {
return this.setLocale("en").toFormat("yyyy-MM-dd HH:mm:ss");
return this.toFormat("yyyy-MM-dd HH:mm:ss");
};
// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------
const alphaRegex = /[a-zA-Z]/g;
const nonAlphaRegex = /[^a-zA-Z]/g;
const nonDigitsRegex = /[^0-9]/g;
const normalizeFormatTable = {
// Python strftime to luxon.js conversion table
// See openerp/addons/base/views/res_lang_views.xml
// See odoo/addons/base/views/res_lang_views.xml
// for details about supported directives
a: "ccc",
A: "cccc",
@@ -40,7 +47,71 @@ const normalizeFormatTable = {
X: "HH:mm:ss",
};
const _normalize_format_cache = {};
const smartDateUnits = {
d: "days",
m: "months",
w: "weeks",
y: "years",
};
const smartDateRegex = new RegExp(`^([+-])(\\d+)([${Object.keys(smartDateUnits).join("")}]?)$`);
/**
* Smart date inputs are shortcuts to write dates quicker.
* These shortcuts should respect the format ^[+-]\d+[dmwy]?$
*
* e.g.
* "+1d" or "+1" will return now + 1 day
* "-2w" will return now - 2 weeks
* "+3m" will return now + 3 months
* "-4y" will return now + 4 years
*
* @param {string} value
* @returns {DateTime|false} Luxon datetime object (in the UTC timezone)
*/
function parseSmartDateInput(value) {
const match = smartDateRegex.exec(value);
if (match) {
let date = DateTime.utc();
const offset = parseInt(match[2], 10);
const unit = smartDateUnits[match[3] || "d"];
if (match[1] === "+") {
date = date.plus({ [unit]: offset });
} else {
date = date.minus({ [unit]: offset });
}
return date;
}
return false;
}
/**
* Enforces some restrictions to a Luxon DateTime object.
* Returns it if within those restrictions.
* Returns false otherwise.
*
* @param {DateTime | false} date
* @returns {DateTime | false}
*/
function constrain(dt) {
let valid = dt !== false;
valid = valid && dt.isValid;
valid = valid && dt.year >= 1000;
valid = valid && dt.year < 10000;
return valid ? dt : false;
}
/**
* Removes any duplicated alphabetic characters in a given string.
* Example: "aa-bb-CCcc-ddD xxxx-Yy-ZZ" -> "a-b-Cc-dD x-Yy-Z"
*
* @param {string} str
* @returns {string}
*/
const stripAlphaDupes = memoize(function _stripAlphaDupes(str) {
return str.replace(alphaRegex, (letter, index, str) => {
return letter === str[index - 1] ? "" : letter;
});
});
/**
* Convert Python strftime to escaped luxon.js format.
@@ -48,32 +119,30 @@ const _normalize_format_cache = {};
* @param {string} value original format
* @returns {string} valid Luxon format
*/
export function strftimeToLuxonFormat(value) {
if (_normalize_format_cache[value] === undefined) {
const isletter = /[a-zA-Z]/;
const output = [];
let inToken = false;
for (let index = 0; index < value.length; ++index) {
let character = value[index];
if (character === "%" && !inToken) {
inToken = true;
continue;
}
if (isletter.test(character)) {
if (inToken && normalizeFormatTable[character] !== undefined) {
character = normalizeFormatTable[character];
} else {
character = "[" + character + "]"; // moment.js escape
}
}
output.push(character);
inToken = false;
export const strftimeToLuxonFormat = memoize(function _strftimeToLuxonFormat(value) {
const output = [];
let inToken = false;
for (let index = 0; index < value.length; ++index) {
let character = value[index];
if (character === "%" && !inToken) {
inToken = true;
continue;
}
_normalize_format_cache[value] = output.join("");
if (character.match(alphaRegex)) {
if (inToken && normalizeFormatTable[character] !== undefined) {
character = normalizeFormatTable[character];
} else {
character = "[" + character + "]"; // moment.js escape
}
}
output.push(character);
inToken = false;
}
return _normalize_format_cache[value];
}
return output.join("");
});
// -----------------------------------------------------------------------------
// Formatting
// -----------------------------------------------------------------------------
/**
@@ -105,38 +174,28 @@ export function formatDateTime(value, options = {}) {
}
// -----------------------------------------------------------------------------
const alphaRegex = /[a-zA-Z]/g;
const nonAlphaRegex = /[^a-zA-Z]/g;
const nonDigitsRegex = /[^0-9]/g;
// Parsing
// -----------------------------------------------------------------------------
/**
* Removes any duplicated alphabetic characters in a given string.
* Example: "aa-bb-CCcc-ddD xxxx-Yy-ZZ" -> "a-b-Cc-dD x-Yy-Z"
* Parses a string value to an UTC Luxon DateTime object.
*
* @param {string} str
* @returns {string}
*/
function stripAlphaDupes(str) {
return str.replace(alphaRegex, (letter, index, str) => {
return letter === str[index - 1] ? "" : letter;
});
}
/**
* Confirms a Luxon DateTime object.
* Returns it if within some defined properties.
* Returns false otherwise.
* @param {string} value value to parse.
* - Value can take the form of a smart date:
* e.g. "+3w" for three weeks from now.
* (`options.format` and `options.timezone` are ignored in this case)
*
* @param {DateTime | false} date
* @returns {DateTime | false}
* - If value cannot be parsed within the provided format,
* ISO8601 and SQL formats are then tried.
*
* @param {object} options
* @param {string} [options.format]
* Provided format used to parse the input value.
* Default=the session localization format
* @returns {DateTime|false} Luxon DateTime object (in the UTC timezone)
*/
function confirm(date) {
let valid = date !== false;
valid = valid && date.isValid;
valid = valid && date.year >= 1000;
valid = valid && date.year < 10000;
return valid ? date : false;
export function parseDate(value, options = {}) {
return parseDateTime(value, { dateOnly: true, format: options.format });
}
/**
@@ -185,7 +244,7 @@ export function parseDateTime(value, options = {}) {
zone: options.timezone ? "local" : "utc",
};
let result = confirm(parseSmartDateInput(value));
let result = constrain(parseSmartDateInput(value));
if (!result) {
const fmt = options.format || localization.dateTimeFormat;
@@ -200,9 +259,9 @@ export function parseDateTime(value, options = {}) {
};
result =
confirm(DateTime.fromFormat(value, fmt, parseOpts)) ||
confirm(DateTime.fromFormat(value, fmtWoZero, parseOpts)) ||
confirm(DateTime.fromFormat(woSeps.val, woSeps.fmt, parseOpts));
constrain(DateTime.fromFormat(value, fmt, parseOpts)) ||
constrain(DateTime.fromFormat(value, fmtWoZero, parseOpts)) ||
constrain(DateTime.fromFormat(woSeps.val, woSeps.fmt, parseOpts));
}
if (!result) {
@@ -211,8 +270,8 @@ export function parseDateTime(value, options = {}) {
// four digit characters as this could get misinterpreted as the time of
// the actual date.
result =
confirm(DateTime.fromISO(value, parseOpts)) || // ISO8601
confirm(DateTime.fromSQL(value, parseOpts)); // last try: SQL
constrain(DateTime.fromISO(value, parseOpts)) || // ISO8601
constrain(DateTime.fromSQL(value, parseOpts)); // last try: SQL
}
}
@@ -223,64 +282,3 @@ export function parseDateTime(value, options = {}) {
result = result.toUTC();
return options.dateOnly ? result.startOf("day") : result;
}
/**
* Parses a string value to an UTC Luxon DateTime object.
*
* @param {string} value value to parse.
* - Value can take the form of a smart date:
* e.g. "+3w" for three weeks from now.
* (`options.format` and `options.timezone` are ignored in this case)
*
* - If value cannot be parsed within the provided format,
* ISO8601 and SQL formats are then tried.
*
* @param {object} options
* @param {string} [options.format]
* Provided format used to parse the input value.
* Default=the session localization format
* @returns {DateTime|false} Luxon DateTime object (in the UTC timezone)
*/
export function parseDate(value, options = {}) {
return parseDateTime(value, { dateOnly: true, format: options.format });
}
// -----------------------------------------------------------------------------
const dateUnits = {
d: "days",
m: "months",
w: "weeks",
y: "years",
};
const smartDateRegex = new RegExp(`^([+-])(\\d+)([${Object.keys(dateUnits).join("")}]?)$`);
/**
* Smart date inputs are shortcuts to write dates quicker.
* These shortcuts should respect the format ^[+-]\d+[dmwy]?$
*
* e.g.
* "+1d" or "+1" will return now + 1 day
* "-2w" will return now - 2 weeks
* "+3m" will return now + 3 months
* "-4y" will return now + 4 years
*
* @param {string} value
* @returns {DateTime|false} Luxon datetime object (in the UTC timezone)
*/
export function parseSmartDateInput(value) {
const match = smartDateRegex.exec(value);
if (match) {
let date = DateTime.utc();
const offset = parseInt(match[2], 10);
const unit = dateUnits[match[3] || "d"];
if (match[1] === "+") {
date = date.plus({ [unit]: offset });
} else {
date = date.minus({ [unit]: offset });
}
return date;
}
return false;
}