diff --git a/addons/bus/static/tests/helpers/mock_python_environment.js b/addons/bus/static/tests/helpers/mock_python_environment.js index a4a22456089..b462daf1df6 100644 --- a/addons/bus/static/tests/helpers/mock_python_environment.js +++ b/addons/bus/static/tests/helpers/mock_python_environment.js @@ -6,6 +6,8 @@ import { registry } from "@web/core/registry"; import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { makeMockServer } from "@web/../tests/helpers/mock_server"; import core from "@web/legacy/js/services/core"; +import { serializeDateTime, serializeDate } from "@web/core/l10n/dates"; +const { DateTime } = luxon; const modelDefinitionsPromise = new Promise((resolve) => { QUnit.begin(() => resolve(getModelDefinitions())); @@ -56,8 +58,8 @@ async function getModelDefinitions() { if (["date", "datetime"].includes(field.type) && !field.default) { const defaultFieldValue = field.type === "date" - ? () => moment.utc().format("YYYY-MM-DD") - : () => moment.utc().format("YYYY-MM-DD HH:mm:ss"); + ? () => serializeDate(DateTime.utc()) + : () => serializeDateTime(DateTime.utc()); field.default = defaultFieldValue; } else if (fname === "active" && !("default" in field)) { // records are active by default. diff --git a/addons/calendar/static/src/activity/activity_menu_patch.js b/addons/calendar/static/src/activity/activity_menu_patch.js index 5979974b2ac..3a7b27c8e2b 100644 --- a/addons/calendar/static/src/activity/activity_menu_patch.js +++ b/addons/calendar/static/src/activity/activity_menu_patch.js @@ -2,8 +2,8 @@ import { ActivityMenu } from "@mail/core/web/activity_menu"; import { patch } from "@web/core/utils/patch"; -import fieldUtils from "@web/legacy/js/fields/field_utils"; -import time from "@web/legacy/js/core/time"; +import { deserializeDateTime, formatDateTime } from "@web/core/l10n/dates"; +import { localization } from "@web/core/l10n/localization"; patch(ActivityMenu.prototype, "calendar", { async fetchSystrayActivities() { @@ -12,11 +12,8 @@ patch(ActivityMenu.prototype, "calendar", { if (group.type === "meeting") { for (const meeting of group.meetings) { if (meeting.start) { - meeting.formattedStart = moment( - fieldUtils.parse.datetime(meeting.start, false, { isUTC: true }) - ) - .local() - .format(time.getLangTimeFormat()); + const date = deserializeDateTime(meeting.start); + meeting.formattedStart = formatDateTime(date, { format: localization.timeFormat }); } } } diff --git a/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js b/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js index c1f5e04ca9e..6b8fa0b1478 100644 --- a/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js +++ b/addons/mail/static/tests/helpers/mock_server/models/discuss_channel.js @@ -7,6 +7,7 @@ import { datetime_to_str } from "@web/legacy/js/core/time"; import { assignDefined } from "@mail/utils/common/misc"; import { formatDate } from "@web/core/l10n/dates"; import { Command } from "@mail/../tests/helpers/command"; +const { DateTime } = luxon; patch(MockServer.prototype, "mail/models/discuss_channel", { async _performRPC(route, args) { @@ -953,7 +954,7 @@ patch(MockServer.prototype, "mail/models/discuss_channel", { */ _mockDiscussChannelWriteImage128(id) { this.pyEnv["discuss.channel"].write([id], { - avatarCacheKey: moment.utc().format("YYYYMMDDHHmmss"), + avatarCacheKey: DateTime.utc().toFormat("yyyyMMddHHmmss"), }); const channel = this.pyEnv["discuss.channel"].searchRead([["id", "=", id]])[0]; this.pyEnv["bus.bus"]._sendone(channel, "mail.record/insert", { diff --git a/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js b/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js index 901526c04ba..6442629d3c3 100644 --- a/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js +++ b/addons/mail/static/tests/helpers/mock_server/models/mail_activity.js @@ -3,7 +3,7 @@ import { sortBy } from "@web/core/utils/arrays"; import { patch } from "@web/core/utils/patch"; import { MockServer } from "@web/../tests/helpers/mock_server"; -import { serializeDate } from "@web/core/l10n/dates"; +import { serializeDate, deserializeDate } from "@web/core/l10n/dates"; const { DateTime} = luxon; @@ -140,7 +140,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { group.activity_type_id = group.activity_type_id[0]; let minDate; activites.forEach(function (activity) { - if (!minDate || moment(activity.date_deadline) < moment(minDate)) { + if (!minDate || deserializeDate(activity.date_deadline) < deserializeDate(minDate)) { minDate = activity.date_deadline; } }); @@ -149,7 +149,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { let state; if (group.date_deadline === serializeDate(DateTime.now())) { state = "today"; - } else if (moment(group.date_deadline) > moment()) { + } else if (deserializeDate(group.date_deadline) > DateTime.now()) { state = "planned"; } else { state = "overdue"; @@ -183,7 +183,7 @@ patch(MockServer.prototype, "mail/models/mail_activity", { }), activity_res_ids: sortBy( records.map((x) => x.id), - (id) => moment(resIdToDeadline[id]) + (id) => deserializeDate(resIdToDeadline[id]) ), grouped_activities: groupedActivities, }; diff --git a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js index fdc322a8b28..3931ff3e80d 100644 --- a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js +++ b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js @@ -3,7 +3,7 @@ import { Order } from "@point_of_sale/app/store/models"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { deserializeDateTime } from "@web/core/l10n/dates"; +import { deserializeDateTime, deserializeDate } from "@web/core/l10n/dates"; import { parseFloat } from "@web/views/fields/parsers"; import { _lt } from "@web/core/l10n/translation"; @@ -333,7 +333,7 @@ export class TicketScreen extends Component { return this._getOrderList().filter(predicate); } getDate(order) { - return moment(order.validation_date).format("YYYY-MM-DD hh:mm A"); + return deserializeDate(order.validation_date).toFormat("yyyy-MM-dd HH:mm a"); } getTotal(order) { return this.env.utils.formatCurrency(order.get_total_with_tax()); @@ -580,7 +580,7 @@ export class TicketScreen extends Component { modelField: "pos_reference", }, DATE: { - repr: (order) => moment(order.creation_date).format("YYYY-MM-DD hh:mm A"), + repr: (order) => deserializeDate(order.creation_date).toFormat("yyyy-MM-dd HH:mm a"), displayName: this.env._t("Date"), modelField: "date_order", }, diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index 8d9a4fbf183..ee6dbde4d62 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -4,7 +4,7 @@ import { formatFloat } from "@web/views/fields/formatters"; import { uuidv4 } from "@point_of_sale/utils"; // FIXME POSREF - unify use of native parseFloat and web's parseFloat. We probably don't need the native version. import { parseFloat as oParseFloat } from "@web/views/fields/parsers"; -import { formatDate, formatDateTime, serializeDateTime } from "@web/core/l10n/dates"; +import { formatDate, formatDateTime, serializeDateTime, deserializeDate } from "@web/core/l10n/dates"; import { roundDecimals as round_di, roundPrecision as round_pr, @@ -227,8 +227,8 @@ export class Product extends PosModel { const categories = this.parent_category_ids.concat(this.categ.id); return ( (!item.categ_id || categories.includes(item.categ_id[0])) && - (!item.date_start || moment.utc(item.date_start).isSameOrBefore(date)) && - (!item.date_end || moment.utc(item.date_end).isSameOrAfter(date)) + (!item.date_start || deserializeDate(item.date_start) <= date) && + (!item.date_end || deserializeDate(item.date_end) >= date) ); } // Port of _get_product_price on product.pricelist. @@ -242,7 +242,7 @@ export class Product extends PosModel { // and were automatically sorted based on their _order by the // ORM. After that they are added in this order to the pricelists. get_price(pricelist, quantity, price_extra = 0, recurring = false) { - var date = moment(); + const date = DateTime.now(); // In case of nested pricelists, it is necessary that all pricelists are made available in // the POS. Display a basic alert to the user in the case where there is a pricelist item diff --git a/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js b/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js index 2ea1e314515..5dc991bd83a 100644 --- a/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js +++ b/addons/pos_sale/static/src/app/order_management_screen/sale_order_row/sale_order_row.js @@ -1,6 +1,7 @@ /** @odoo-module */ import { Component, useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; +import { deserializeDateTime } from "@web/core/l10n/dates"; /** * @props {models.Order} order @@ -29,7 +30,7 @@ export class SaleOrderRow extends Component { return this.order.name; } get date() { - return moment(this.order.date_order).format("YYYY-MM-DD hh:mm A"); + return deserializeDateTime(this.order.date_order).toFormat("yyyy-MM-dd HH:mm a"); } get partner() { const partner = this.order.partner_id; diff --git a/addons/project/static/src/js/portal_rating.js b/addons/project/static/src/js/portal_rating.js index 9c65101cd2d..6cc7ff0b1a6 100644 --- a/addons/project/static/src/js/portal_rating.js +++ b/addons/project/static/src/js/portal_rating.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import time from '@web/legacy/js/core/time'; import publicWidget from '@web/legacy/js/public/public_widget'; +import { parseDate } from '@web/core/l10n/dates'; publicWidget.registry.ProjectRatingImage = publicWidget.Widget.extend({ selector: '.o_portal_project_rating .o_rating_image', @@ -18,8 +18,8 @@ publicWidget.registry.ProjectRatingImage = publicWidget.Widget.extend({ var $elem = $(this); var id = $elem.data('id'); var ratingDate = $elem.data('rating-date'); - var baseDate = time.auto_str_to_date(ratingDate); - var duration = moment(baseDate).fromNow(); + var baseDate = parseDate(ratingDate); + var duration = baseDate.toRelative(); var $rating = $('#rating_' + id); $rating.find('.rating_timeduration').text(duration); return $rating.html(); diff --git a/addons/test_mail/static/tests/activity_tests.js b/addons/test_mail/static/tests/activity_tests.js index 464ceb4b915..28ac785567f 100644 --- a/addons/test_mail/static/tests/activity_tests.js +++ b/addons/test_mail/static/tests/activity_tests.js @@ -89,7 +89,7 @@ QUnit.module("test_mail", {}, function () { }); var activityDateFormat = function (date) { - return date.toLocaleDateString(moment().locale(), { day: "numeric", month: "short" }); + return DateTime.now().toLocaleString({ day: "numeric", month: "short" }); }; QUnit.test("activity view: simple activity rendering", async function (assert) { diff --git a/addons/web/static/src/core/l10n/dates.js b/addons/web/static/src/core/l10n/dates.js index 09567e9ff74..374172c3e48 100644 --- a/addons/web/static/src/core/l10n/dates.js +++ b/addons/web/static/src/core/l10n/dates.js @@ -297,6 +297,7 @@ export function formatDateTime(value, options = {}) { /** * Formats the given DateTime to the server date format. * @param {DateTime} value + * @returns {string} */ export function serializeDate(value) { if (!dateCache.has(value)) { @@ -308,6 +309,7 @@ export function serializeDate(value) { /** * Formats the given DateTime to the server datetime format. * @param {DateTime} value + * @returns {string} */ export function serializeDateTime(value) { if (!dateTimeCache.has(value)) { diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 26c19a6367c..ba963f6a70f 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -49,6 +49,7 @@ var _t = core._t; const _lt = core._lt; const preserveCursor = OdooEditorLib.preserveCursor; const descendants = OdooEditorLib.descendants; +const { DateTime } = luxon; /** * @param {HTMLElement} el @@ -1849,7 +1850,7 @@ const DatetimePickerUserValueWidget = InputUserValueWidget.extend({ */ init: function () { this._super(...arguments); - this._value = moment().unix().toString(); + this._value = DateTime.now().toUnixInteger().toString(); this.__libInput = 0; }, /** diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index 084c3a44fda..333c303bc56 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -4,6 +4,8 @@ import { sprintf } from "@web/core/utils/strings"; import {_t} from "@web/legacy/js/services/core"; import publicWidget from "@web/legacy/js/public/public_widget"; import time from "@web/legacy/js/core/time"; +import { parseDate, formatDate } from "@web/core/l10n/dates"; +const { DateTime } = luxon; publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ selector: '#wrapwrap:has(.interested_partner_assign_form, .desinterested_partner_assign_form, .opp-stage-button, .new_opp_form)', @@ -252,9 +254,10 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ $('.edit_opp_form .activity_summary').val($selected.attr('activity_summary')); } if ($selected.attr('delay_count')) { - var now = moment(); - var date = now.add(parseInt($selected.attr('delay_count')), $selected.attr('delay_unit')); - $('.edit_opp_form .activity_date_deadline').val(date.format(time.getLangDateFormat())); + const value = +$selected.attr('delay_count'); + const unit = $selected.attr('delay_unit'); + const date = DateTime.now().plus({ [unit]: value}); + $('.edit_opp_form .activity_date_deadline').val( formatDate(date) ); } }, /** @@ -277,9 +280,9 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ _parse_date: function (value) { console.log(value); - var date = moment(value, time.getLangDateFormat(), true); - if (date.isValid() && date.year() >= 1900) { - return time.date_to_str(date.toDate()); + var date = parseDate(value); + if (date.isValid && date.year >= 1900) { + return formatDate(date); } else { return false;