Skip to main content
iVentureTeam

time_hour_uom

A one-getter widget that reformats a duration from 07:30 to 7h30, and drops the trailing minutes when they are zero. It loses every option the widget it extends had.

September 18, 2026Updated September 18, 20264 min read
Technical nametime_hour_uom
Field typesfloat
Viewsform, list
Modulehr_timesheet, installed with Timesheets
Used in core0 uses in Odoo 19 Community or Enterprise. Unchanged in Odoo 20.
VersionsOdoo 19.0, Odoo 20.0
No-code setupNo. There is no Studio entry for this widget.
Alternativesfloat_time, timesheet_uom, float_factor

What the time_hour_uom widget does

The entire widget is one getter:

export class TimeHourField extends FloatTimeField {
    get formattedValue() {
        const unitAmount = super.formattedValue;
        const [hourStr, minuteStr] = unitAmount.split(":");
        const hours = parseInt(hourStr, 10);
        const minutes = parseInt(minuteStr, 10);
        return minutes ? _t("%(hours)sh%(minutes)s", { hours, minutes })
                       : _t("%(hours)sh", { hours });
    }
}

It lets float_time do the formatting, then takes the HH:MM string back apart and reassembles it in the 7h30 style. Because it parses with parseInt, the leading zeros disappear: 07:30 becomes 7h30, and 08:00 becomes 8h.

Both output forms go through the translation system, so a localization can change the separator.

What this means for your team

This is a small readability decision with a real effect on timesheet screens. 07:30 reads like a clock time, which is exactly wrong for a duration: people glance at it and think half past seven rather than seven and a half hours. 7h30 cannot be misread that way.

On a weekly timesheet grid full of durations, that distinction removes a whole category of confused questions. If your team regularly asks whether a number is a time or a length, this is the change.

Working examples

On a timesheet duration field:

<field name="unit_amount" widget="time_hour_uom"/>

This is ignored, because the descriptor does not carry float_time's options:

<!-- show_seconds is a float_time option and is not inherited here -->
<field name="unit_amount" widget="time_hour_uom"
       options="{'show_seconds': True}"/>

Extending a component is not extending a widget

This widget is a clean illustration of a distinction that catches people writing their own.

There are two separate things: the component, which is the OWL class doing the rendering, and the descriptor, the object registered in the fields registry that tells Odoo which options and types the widget supports.

Subclassing the component inherits rendering behavior. It does not inherit the descriptor. Here, TimeHourField extends FloatTimeField gives you all of float_time's rendering, but the descriptor is written fresh:

export const timeHourField = {
    component: TimeHourField,
};

Compare that with how float_factor does the same job:

export const floatFactorField = {
    ...floatField,
    component: FloatFactorField,
    supportedOptions: [ ...floatField.supportedOptions, ... ],
};

The spread is what carries the options across. Without it, the widget silently supports nothing, and there is no warning: an unknown option is parsed by the view and discarded.

If you are writing a widget by extending an existing one, spread the descriptor as well as extending the class, or you will ship a widget whose documented options do not work.

Version compatibility

VersionStatusNotes
Odoo 19.0VerifiedVerified against the shipped 19.0 source.
Odoo 20.0VerifiedVerified against the 20.0 branch. No changes.

Unchanged between the two branches we checked.

What is changing in Odoo 20

No change to this widget. Diffing the registration between the 19.0 and 20.0 branches shows the same single-key descriptor.

The widget it extends did change, which matters indirectly. In Odoo 20 float_time renames display_seconds to show_seconds and gains numeric and unit. Since this descriptor never inherited those options, none of that reaches it, but the underlying formattedValue it reparses comes from the updated component.

Common problems and fixes

SymptomCause and fix
float_time options such as show_seconds do nothingThe descriptor does not spread floatTimeField, so none of its options are inherited. Use float_time directly, or write a small widget that extends this component and spreads the float_time descriptor.
The value shows as 8h when it should show 8h00Deliberate. When minutes parse to zero the widget uses the hours-only format. Use float_time if you need a constant HH:MM shape.
Leading zeros disappearedThe widget reparses with parseInt, which drops them. Expected behavior for this widget.
No warning when applied to a non-float fieldThe descriptor declares no supportedTypes, so Odoo's type-mismatch warning never fires. Check the field type yourself; this widget will not tell you.

Time_hour_uom widget vs the alternatives

WidgetBest forKey difference
time_hour_uomDurations that must not read like clock timesReformats to 7h30 but inherits none of float_time's options
float_timeDurations in HH:MM with full option supportKeeps leading zeros and supports show_seconds, unit and more
timesheet_uomDurations shown in the company's configured timesheet unitSwitches between hours and days based on configuration
float_factorA duration rescaled to another unitScales the number rather than restyling the format

Use float_time directly when you want HH:MM and access to its options, which is most of the time. Use this one purely for the 7h30 reading, accepting that you give up configuration to get it. If you need both the format and the options, that is a small custom widget: extend this component and spread floatTimeField into the descriptor.

Frequently asked questions

How do I show Odoo durations as 7h30 instead of 07:30?+
Use widget="time_hour_uom" on the float field. It reformats float_time's output, dropping leading zeros and omitting the minutes entirely when they are zero, so 08:00 renders as 8h.
Why do float_time options not work on time_hour_uom?+
Because the descriptor is written as { component: TimeHourField } without spreading floatTimeField. The component extends float_time, but the widget descriptor inherits no options, so anything you pass is discarded.
Does time_hour_uom validate the field type?+
No. It declares no supportedTypes, so Odoo's console warning for a widget and type mismatch never fires. Applying it to a non-float field fails silently.

Timesheets your team argues with?

Duration formats, rounding rules and the difference between hours and days cause more timesheet disputes than the hours themselves. We configure Timesheets so what people enter, what managers approve and what gets invoiced all agree.

Book a free consultation

How this page was produced

Verified by reading addons/hr_timesheet/static/src/components/time_hour_field/time_hour_field.js on the 19.0 branch of a local clone of the official Odoo repository. The component is quoted in full above, and the bare descriptor is quoted verbatim. The comparison with float_factor comes from addons/web/static/src/views/fields/float_factor/float_factor_field.js on the same branch. The registration was diffed against the 20.0 branch. Corrections welcome via our contact page.