Skip to main content
iVentureTeam

formatted_date

New in Odoo 19, formatted_date is a display-only date widget with per-view format control: month names instead of numbers, conditional colors from record data, and the label Indefinite when the date is empty.

Siddharth JambukiyaSiddharth JambukiyaOdoo Techno-Functional Consultant
August 19, 2026Updated August 19, 20265 min read
Technical nameformatted_date
Field typesdate
Viewslist, form (read-only display)
Modulehr_skills (Employee Skills)
Used in core6 occurrences across 1 module: the certification date columns in hr_skills
VersionsOdoo 20.0, Odoo 19.0
No-code setupNo: the format and color options are XML only
Alternativesdate, remaining_days, daterange

What the Formatted Date widget does

Odoo's standard date widget always renders in the user's locale format, digits and all. formatted_date exists for the cases where the view, not the locale, should decide how a date reads: a certifications list where Mar 2026 scans faster than 03/01/2026, a deadline column that turns red when a condition on the record is met.

It is a pure display widget. The template renders one <span> with the date passed through toLocaleString using the day, month and year formats you configure, so the actual spelling (whether "short" means Mar or mars) still follows the user's language. There is no input, no picker, no editing path at all.

Its second trick is the empty state: no date renders as the word Indefinite, muted and half transparent. That default is hardcoded, and it tells you where the widget comes from: certification expiry dates in the Employee Skills app, where an empty To date genuinely means "does not expire".

What this means for your team

This widget earns its keep in list views that people scan daily.

Readable beats precise. An HR team reviewing certifications does not need day-level precision in the overview; "Aug 2026" columns are faster to scan and compare than full numeric dates. Precision stays one click away on the form.

Conditional color is cheap risk visibility. The color option evaluates expressions against each record, so an expiry column can render text-danger when a certification is already expired and text-warning when it expires within the quarter, computed from a helper field. That is a no-backend-logic way to make a list triage itself.

The caveat we flag to clients: it requires the hr_skills module. On non-HR databases, achieving the same effect means copying a 60-line widget into a custom module, which is a small, low-risk customization.

Supported options in Odoo 19

Verified against formatted_date.js in the Odoo 19.0 hr_skills module. All four options are declared in supportedOptions and read in extractProps with numeric (or an empty mapping for color) as the fallback.

OptionTypeWhat it does
day_formatstringHow the day renders: numeric (2) or 2-digit (02). Passed straight to the browser's date formatting, so the output respects the user's language.(default: numeric)(since Odoo 19.0)
month_formatstringHow the month renders: numeric, 2-digit, long (March), short (Mar) or narrow (M). Core's certification columns use short.(default: numeric)(since Odoo 19.0)
year_formatstringHow the year renders: numeric (2026) or 2-digit (26).(default: numeric)(since Odoo 19.0)
colorobjectMapping of Bootstrap color suffixes to Python expressions evaluated against the record, e.g. {'danger': 'is_expired'}. The first truthy expression applies its text-<color> class to the label.(default: {})(since Odoo 19.0)

The format values are browser date-part tokens. day_format accepts numeric or 2-digit; month_format accepts numeric, 2-digit, long, short or narrow; year_format accepts numeric or 2-digit. There is no way to drop a part entirely: all three parts always render, so a month-and-year-only label is not achievable with options alone.

Working examples

Short month, as core uses it

<field name="valid_from" string="From" widget="formatted_date"
       options="{'month_format': 'short'}"/>

Renders as Mar 2, 2026 for an English user, with day and year still numeric.

Conditional colors from record data

<field name="valid_to" widget="formatted_date"
       options="{'month_format': 'short', 'color': {'danger': 'is_expired', 'warning': 'expires_soon'}}"/>

Each key is a Bootstrap color suffix, each value a Python expression evaluated against the record. The first truthy one applies as text-danger, text-warning and so on.

Fully spelled out

<field name="issue_date" widget="formatted_date"
       options="{'day_format': '2-digit', 'month_format': 'long', 'year_format': 'numeric'}"/>

Color expressions and the Indefinite label

Two implementation details decide whether this widget fits your use case.

The color expressions run in the record's evaluation context. The source evaluates each expression with evaluateBooleanExpr against evalContextWithVirtualIds, the same machinery behind invisible and readonly conditions. That means the expressions can reference any field loaded in the view, but comparisons against "today" are not available in that context, so time-based coloring needs a computed boolean or date field on the model to compare against.

The empty-state label is not configurable. "Indefinite" is a hardcoded, translatable string in the component. If your business language for an empty date is "none" or "open-ended", the placeholder attribute will not help; that string only changes by extending the widget.

Also worth knowing: the widget declares supportedTypes: ["date"] only. Pointing it at a datetime field is not supported; convert with a related date field instead.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. The source file on the development branch is byte-identical to 19.0.
Odoo 19.0VerifiedIntroduced in 19.0 by hr_skills; verified against the shipped source.
Odoo 18.0Not availableDoes not exist.
Odoo 17.0Not availableDoes not exist.
Odoo 16.0Not availableDoes not exist.

Upgrade note. The widget does not exist before Odoo 19. Views built with it will not degrade gracefully on 18: the field falls back to the missing-widget warning, so backports need the widget copied along.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The notes below are read from the public development branch and are not final until release.

No changes: the source file on master is byte-identical to Odoo 19, options and Indefinite label included. Views should carry over untouched. We re-verify this page against the shipped release.

Common problems and fixes

SymptomCause and fix
Missing widget warning on a non-HR databaseThe widget ships with hr_skills; without that module it is not in the registry. Install hr_skills, add it as a dependency, or copy the 60-line widget into your own module.
Users cannot edit the dateBy design: the widget renders a text span with no input or picker. Use the standard date widget where editing is needed; keep formatted_date for display columns.
The color never changesThe expression references a field that is not loaded in the view, or it needs a comparison against today, which the eval context cannot do. Add the referenced field to the view (invisible is fine) or compute a boolean like is_expired on the model and test that.
Empty dates show "Indefinite" but you want different wordingThe placeholder string is hardcoded in the component; the placeholder attribute is not read. Extend the widget in JavaScript and override the placeholder getter, or accept the stock label.
It does nothing on a datetime fieldsupportedTypes is ["date"] only. Add a related or computed date field and point the widget at that.

Formatted Date widget vs the alternatives

WidgetBest forKey difference
formatted_dateRead-only date columns that need custom formatting or conditional colorView-level format control and expression-driven colors; never editable
dateAny date users actually setFull date picker and editing, but always the locale's numeric format
remaining_daysDeadlines where urgency matters more than the dateRenders relative labels like In 5 days or Today instead of the date
daterangeA period with a start and an end in one controlEditable two-date picker pair, not a formatted label

Choose by editability first: formatted_date is display-only, so if users must set the date in the same view, use date and accept the locale format. Use remaining_days when the point is urgency ("In 5 days") rather than the date itself.

Frequently asked questions

How do I show an Odoo date as "Mar 2026" style text?+
Use widget="formatted_date" with options="{'month_format': 'short'}" (Odoo 19+, hr_skills installed). Day and year stay numeric unless you set their formats too; all three parts always render.
Can formatted_date color the date red when it is overdue?+
Yes, via the color option: {'color': {'danger': 'is_expired'}}, where is_expired is a field or expression on the record. Comparisons against today are not possible in the expression itself, so compute a boolean on the model.
Why does an empty date say Indefinite?+
That placeholder is hardcoded in the widget, inherited from its home use case of certification expiry dates where empty means never expires. Changing the wording requires extending the widget.
Does formatted_date follow the user's language?+
Yes. The options choose which parts are spelled out; the actual month names and part order come from the user's locale, so short renders Mar for English and mars for French users.
Can I use it outside HR?+
Technically yes on any date field, but the widget is registered by hr_skills, so that module must be installed or declared as a dependency. On non-HR projects we usually copy the small widget into the client's module instead.

Lists your team can read at a glance?

Readable date labels, self-coloring deadline columns, and views that triage themselves are small customizations with daily payoff. We tune Odoo list and form views field by field, and when a widget like this one lives in the wrong module, we port it cleanly into yours.

Polish our Odoo views

How this page was produced

The option table and behaviors were read from formatted_date.js and its template in the Odoo 19.0 hr_skills module, including the extractProps defaults, the evaluateBooleanExpr color path and the hardcoded Indefinite placeholder. Its absence in 18.0 and the byte-identical file on the development branch were confirmed by checking those branches directly. Spotted an error or a version difference? Tell us and we will correct the page.