diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index 5ed42e54e048241bcff67a365e71a5a1b1553174..4767f54d998b40705b0f911596a5359c7c3b9ad3 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -379,11 +379,12 @@ var AbstractField = Widget.extend({
      *
      * @private
      * @param {any} value (from the field type)
+     * @param {string} [formatType=this.formatType] the formatter to use
      * @returns {string}
      */
-    _formatValue: function (value) {
+    _formatValue: function (value, formatType) {
         var options = _.extend({}, this.nodeOptions, { data: this.recordData }, this.formatOptions);
-        return field_utils.format[this.formatType](value, this.field, options);
+        return field_utils.format[formatType || this.formatType](value, this.field, options);
     },
     /**
      * Returns the className corresponding to a given decoration. A
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 721d6618e83c83a3d9116df8d1609f343b1c15b7..4c5bef2c3662a834961bd4f58290acb8aa3372b4 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -887,6 +887,49 @@ var FieldDateTime = FieldDate.extend({
     },
 });
 
+const RemainingDays = FieldDate.extend({
+    description: _lt("Remaining Days"),
+    supportedFieldTypes: ['date', 'datetime'],
+
+    //--------------------------------------------------------------------------
+    // Private
+    //--------------------------------------------------------------------------
+
+    /**
+     * Displays the delta (in days) between the value of the field and today. If
+     * the delta is larger than 99 days, displays the date as usual (without
+     * time).
+     *
+     * @override
+     */
+    _renderReadonly() {
+        if (this.value === false) {
+            this.$el.removeClass('text-bf text-danger text-warning');
+            return;
+        }
+        // compare the value (in the user timezone) with now (also in the user
+        // timezone), to get a meaningful delta for the user
+        const nowUTC = moment().utc();
+        const nowUserTZ = nowUTC.clone().add(session.getTZOffset(nowUTC), 'minutes');
+        const valueUserTZ = this.value.clone().add(session.getTZOffset(this.value), 'minutes');
+        const diffDays = valueUserTZ.startOf('day').diff(nowUserTZ.startOf('day'), 'days');
+        let text;
+        if (Math.abs(diffDays) > 99) {
+            text = this._formatValue(this.value, 'date');
+        } else if (diffDays === 0) {
+            text = _t("Today");
+        } else if (diffDays < 0) {
+            text = diffDays === -1 ? _t("Yesterday") : _t(`${-diffDays} days ago`);
+        } else {
+            text = diffDays === 1 ? _t("Tomorrow") : _t(`In ${diffDays} days`);
+        }
+        this.$el.text(text).attr('title', this._formatValue(this.value, 'date'));
+        this.$el.toggleClass('text-bf', diffDays <= 0);
+        this.$el.toggleClass('text-danger', diffDays < 0);
+        this.$el.toggleClass('text-warning', diffDays === 0);
+    },
+});
+
 var FieldMonetary = NumericField.extend({
     description: _lt("Monetary"),
     className: 'o_field_monetary o_field_number',
@@ -3437,6 +3480,7 @@ return {
     FieldDate: FieldDate,
     FieldDateTime: FieldDateTime,
     FieldDateRange: FieldDateRange,
+    RemainingDays: RemainingDays,
     FieldDomain: FieldDomain,
     FieldFloat: FieldFloat,
     FieldFloatTime: FieldFloatTime,
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index b8b3fe614522917926ba47629f28f2931e381cca..3cadf6cf6fafc102b417b5c000c39575a75a30ed 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -34,6 +34,7 @@ registry
     .add('date', basic_fields.FieldDate)
     .add('datetime', basic_fields.FieldDateTime)
     .add('daterange', basic_fields.FieldDateRange)
+    .add('remaining_days', basic_fields.RemainingDays)
     .add('domain', basic_fields.FieldDomain)
     .add('text', basic_fields.FieldText)
     .add('list.text', basic_fields.ListFieldText)
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index 1f78efeb7f278403ed14a349fc9e7657643f42c8..5f4c4eb8dcbe35bd886a0feb687869c8e03f38eb 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -15,6 +15,8 @@ var testUtilsDom = require('web.test_utils_dom');
 var field_registry = require('web.field_registry');
 
 var createView = testUtils.createView;
+var patchDate = testUtils.mock.patchDate;
+
 var DebouncedField = basicFields.DebouncedField;
 var JournalDashboardGraph = basicFields.JournalDashboardGraph;
 var _t = core._t;
@@ -4193,6 +4195,201 @@ QUnit.module('basic_fields', {
         form.destroy();
     });
 
+    QUnit.module('RemainingDays');
+
+    QUnit.test('remaining_days widget on a date field in list view', async function (assert) {
+        assert.expect(16);
+
+        const unpatchDate = patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
+        this.data.partner.records = [
+            { id: 1, date: '2017-10-08' }, // today
+            { id: 2, date: '2017-10-09' }, // tomorrow
+            { id: 3, date: '2017-10-07' }, // yesterday
+            { id: 4, date: '2017-10-10' }, // + 2 days
+            { id: 5, date: '2017-10-05' }, // - 3 days
+            { id: 6, date: '2018-02-08' }, // + 4 months (diff >= 100 days)
+            { id: 7, date: '2017-06-08' }, // - 4 months (diff >= 100 days)
+            { id: 8, date: false },
+        ];
+
+        const list = await createView({
+            View: ListView,
+            model: 'partner',
+            data: this.data,
+            arch: '<tree><field name="date" widget="remaining_days"/></tree>',
+        });
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0)').text(), 'Today');
+        assert.strictEqual(list.$('.o_data_cell:nth(1)').text(), 'Tomorrow');
+        assert.strictEqual(list.$('.o_data_cell:nth(2)').text(), 'Yesterday');
+        assert.strictEqual(list.$('.o_data_cell:nth(3)').text(), 'In 2 days');
+        assert.strictEqual(list.$('.o_data_cell:nth(4)').text(), '3 days ago');
+        assert.strictEqual(list.$('.o_data_cell:nth(5)').text(), '02/08/2018');
+        assert.strictEqual(list.$('.o_data_cell:nth(6)').text(), '06/08/2017');
+        assert.strictEqual(list.$('.o_data_cell:nth(7)').text(), '');
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0) .o_field_widget').attr('title'), '10/08/2017');
+
+        assert.hasClass(list.$('.o_data_cell:nth(0) span'), 'text-bf text-warning');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(1) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(2) span'), 'text-bf text-danger');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(3) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(4) span'), 'text-bf text-danger');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(5) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(6) span'), 'text-bf text-danger');
+
+        list.destroy();
+        unpatchDate();
+    });
+
+    QUnit.test('remaining_days widget on a date field in form view', async function (assert) {
+        assert.expect(6);
+
+        const unpatchDate = patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
+        this.data.partner.records = [
+            { id: 1, date: '2017-10-08' }, // today
+        ];
+
+        const form = await createView({
+            View: FormView,
+            model: 'partner',
+            data: this.data,
+            arch: '<form><field name="date" widget="remaining_days"/></form>',
+            res_id: 1,
+        });
+
+        assert.strictEqual(form.$('.o_field_widget').text(), 'Today');
+        assert.hasClass(form.$('.o_field_widget'), 'text-bf text-warning');
+
+        // in edit mode, this widget should behave like a regular date(time) widget
+        await testUtils.form.clickEdit(form);
+
+        assert.hasClass(form.$('.o_form_view'), 'o_form_editable');
+        assert.containsOnce(form, '.o_datepicker');
+        assert.strictEqual(form.$('.o_datepicker_input').val(), '10/08/2017');
+
+        await testUtils.dom.openDatepicker(form.$('.o_datepicker'));
+
+        assert.containsOnce(document.body, '.bootstrap-datetimepicker-widget:visible');
+
+        form.destroy();
+        unpatchDate();
+    });
+
+    QUnit.test('remaining_days widget on a datetime field in list view in UTC', async function (assert) {
+        assert.expect(16);
+
+        const unpatchDate = patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
+        this.data.partner.records = [
+            { id: 1, datetime: '2017-10-08 20:00:00' }, // today
+            { id: 2, datetime: '2017-10-09 08:00:00' }, // tomorrow
+            { id: 3, datetime: '2017-10-07 18:00:00' }, // yesterday
+            { id: 4, datetime: '2017-10-10 22:00:00' }, // + 2 days
+            { id: 5, datetime: '2017-10-05 04:00:00' }, // - 3 days
+            { id: 6, datetime: '2018-02-08 04:00:00' }, // + 4 months (diff >= 100 days)
+            { id: 7, datetime: '2017-06-08 04:00:00' }, // - 4 months (diff >= 100 days)
+            { id: 6, datetime: false },
+        ];
+
+        const list = await createView({
+            View: ListView,
+            model: 'partner',
+            data: this.data,
+            arch: '<tree><field name="datetime" widget="remaining_days"/></tree>',
+            session: {
+                getTZOffset: () => 0,
+            },
+        });
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0)').text(), 'Today');
+        assert.strictEqual(list.$('.o_data_cell:nth(1)').text(), 'Tomorrow');
+        assert.strictEqual(list.$('.o_data_cell:nth(2)').text(), 'Yesterday');
+        assert.strictEqual(list.$('.o_data_cell:nth(3)').text(), 'In 2 days');
+        assert.strictEqual(list.$('.o_data_cell:nth(4)').text(), '3 days ago');
+        assert.strictEqual(list.$('.o_data_cell:nth(5)').text(), '02/08/2018');
+        assert.strictEqual(list.$('.o_data_cell:nth(6)').text(), '06/08/2017');
+        assert.strictEqual(list.$('.o_data_cell:nth(7)').text(), '');
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0) .o_field_widget').attr('title'), '10/08/2017');
+
+        assert.hasClass(list.$('.o_data_cell:nth(0) span'), 'text-bf text-warning');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(1) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(2) span'), 'text-bf text-danger');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(3) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(4) span'), 'text-bf text-danger');
+        assert.doesNotHaveClass(list.$('.o_data_cell:nth(5) span'), 'text-bf text-warning text-danger');
+        assert.hasClass(list.$('.o_data_cell:nth(6) span'), 'text-bf text-danger');
+
+        list.destroy();
+        unpatchDate();
+    });
+
+    QUnit.test('remaining_days widget on a datetime field in list view in UTC+6', async function (assert) {
+        assert.expect(6);
+
+        const unpatchDate = patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11, UTC+6
+        this.data.partner.records = [
+            { id: 1, datetime: '2017-10-08 20:00:00' }, // tomorrow
+            { id: 2, datetime: '2017-10-09 08:00:00' }, // tomorrow
+            { id: 3, datetime: '2017-10-07 18:30:00' }, // today
+            { id: 4, datetime: '2017-10-07 12:00:00' }, // yesterday
+            { id: 5, datetime: '2017-10-09 20:00:00' }, // + 2 days
+        ];
+
+        const list = await createView({
+            View: ListView,
+            model: 'partner',
+            data: this.data,
+            arch: '<tree><field name="datetime" widget="remaining_days"/></tree>',
+            session: {
+                getTZOffset: () => 360,
+            },
+        });
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0)').text(), 'Tomorrow');
+        assert.strictEqual(list.$('.o_data_cell:nth(1)').text(), 'Tomorrow');
+        assert.strictEqual(list.$('.o_data_cell:nth(2)').text(), 'Today');
+        assert.strictEqual(list.$('.o_data_cell:nth(3)').text(), 'Yesterday');
+        assert.strictEqual(list.$('.o_data_cell:nth(4)').text(), 'In 2 days');
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0) .o_field_widget').attr('title'), '10/09/2017');
+
+        list.destroy();
+        unpatchDate();
+    });
+
+    QUnit.test('remaining_days widget on a datetime field in list view in UTC-8', async function (assert) {
+        assert.expect(5);
+
+        const unpatchDate = patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11, UTC-8
+        this.data.partner.records = [
+            { id: 1, datetime: '2017-10-08 20:00:00' }, // today
+            { id: 2, datetime: '2017-10-09 07:00:00' }, // today
+            { id: 3, datetime: '2017-10-09 10:00:00' }, // tomorrow
+            { id: 4, datetime: '2017-10-08 06:00:00' }, // yesterday
+            { id: 5, datetime: '2017-10-07 02:00:00' }, // - 2 days
+        ];
+
+        const list = await createView({
+            View: ListView,
+            model: 'partner',
+            data: this.data,
+            arch: '<tree><field name="datetime" widget="remaining_days"/></tree>',
+            session: {
+                getTZOffset: () => -560,
+            },
+        });
+
+        assert.strictEqual(list.$('.o_data_cell:nth(0)').text(), 'Today');
+        assert.strictEqual(list.$('.o_data_cell:nth(1)').text(), 'Today');
+        assert.strictEqual(list.$('.o_data_cell:nth(2)').text(), 'Tomorrow');
+        assert.strictEqual(list.$('.o_data_cell:nth(3)').text(), 'Yesterday');
+        assert.strictEqual(list.$('.o_data_cell:nth(4)').text(), '2 days ago');
+
+        list.destroy();
+        unpatchDate();
+    });
+
     QUnit.module('FieldMonetary');
 
     QUnit.test('monetary field in form view', async function (assert) {
diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst
index 7d5b6e6af35ba99d4e122edf7489ea2b15586138..92c6a292ff96a421520e35bbca9d47a995b4a293 100644
--- a/doc/reference/javascript_reference.rst
+++ b/doc/reference/javascript_reference.rst
@@ -1686,7 +1686,7 @@ order.
         <field name="datetimefield" options='{"datepicker": {"daysOfWeekDisabled": [0, 6]}}'/>
 
 - daterange (FieldDateRange)
-    This widget allow user to select start and end date into single picker.
+    This widget allows the user to select start and end date into a single picker.
 
     - Supported field types: *date*, *datetime*
 
@@ -1702,6 +1702,13 @@ order.
 
         <field name="start_date" widget="daterange" options='{"related_end_date": "end_date"}'/>
 
+- remaining_days (RemainingDays)
+    This widget can be used on date and datetime fields. In readonly, it displays
+    the delta (in days) between the value of the field and today. It edit, it
+    behaves like a regular date(time) widget.
+
+    - Supported field types: *date*, *datetime*
+
 - monetary (FieldMonetary)
     This is the default field type for fields of type 'monetary'. It is used to
     display a currency.  If there is a currency fields given in option, it will