Skip to Content
OWL forms

Date Time Field

Odoo 19 OWL component — Date Time Field (views)

Live preview Interactive
Source excerpt web/static/src/views/fields/datetime/datetime_field.js
import { Component, onWillRender, useEffect, useRef, useState } from "@odoo/owl";
import { useDateTimePicker } from "@web/core/datetime/datetime_picker_hook";
import { areDatesEqual, deserializeDate, deserializeDateTime, today } from "@web/core/l10n/dates";
import { _t } from "@web/core/l10n/translation";
import { evaluateBooleanExpr } from "@web/core/py_js/py";
import { registry } from "@web/core/registry";
import { ensureArray } from "@web/core/utils/arrays";
import { exprToBoolean } from "@web/core/utils/strings";
import { FIELD_WIDTHS } from "@web/views/list/column_width_hook";
import { formatDate, formatDateTime } from "../formatters";
import { standardFieldProps } from "../standard_field_props";

const { DateTime } = luxon;

function getFormattedPlaceholder(value, type, options) {
    if (value instanceof luxon.DateTime) {
        return type === "date" ? formatDate(value, options) : formatDateTime(value, options);
    }
    return value || "";
}

/**
 * @typedef {luxon.DateTime} DateTime
 *
 * @typedef {import("../standard_field_props").StandardFieldProps & {
 *  endDateField?: string;
 *  maxDate?: string;
 *  minDate?: string;
 *  placeholder?: string;
 *  required?: boolean;
 *  rounding?: number;
 *  startDateField?: string;
 *  warnFuture?: boolean;
 *  showSeconds?: boolean;
 *  showTime?: boolean;
 *  numeric?: boolean;
 *  minPrecision?: string;
 *  maxPrecision?: string;
 * }} DateTimeFieldProps
 *
 * @typedef {import("@web/core/datetime/datetime_picker").DateTimePickerProps} DateTimePickerProps
 */

/** @extends {Component<DateTimeFieldProps>} */
export class DateTimeField extends Component {
    static props = {
        ...standardFieldProps,
        endDateField: { type: String, optional: true },
        maxDate: { type: String, optional: true },
        minDate: { type: String, optional: true },
        alwaysRange: { type: Boolean, optional: true },
        placeholder: { type: String, optional: true },
        required: { type: Boolean, optional: true },
        rounding: { type: Number, optional: true },
        startDateField: { type: String, optional: true },
        numeric: { type: Boolean, optional: true },
        warnFuture: { type: Boolean, optional: true },
        showSeconds: { type: Boolean, optional: true },
        showTime: { type: Boolean, optional: true },
        minPrecision: {
            type: String,
            optional: true,
            validate: (props) => ["days", "months", "years", "decades"].includes(props),
        },
        maxPrecision: {
            type: String,
            optional: true,
            validate: (props) => ["days", "months", "years", "decades"].includes(props),
        },
    };
    static defaultProps = {
        showSeconds: false,
        showTime: true,
        numeric: false,
    };

    static template = "web.DateTimeField";

    //-------------------------------------------------------------------------
    // Getters
    //-------------------------------------------------------------------------

    get endDateField() {
        return this.relatedField ? this.props.endDateField || this.props.name : null;
    }

    get field() {
        return this.props.record.fields[this.props.name];
    }

    get relatedField() {
        return this.props.startDateField || this.props.endDateField;
    }

    get startDateField() {
        return this.props.startDateField || this.props.name;
    }

    get values() {
        return ensureArray(this.state.value);
    }

    //-------------------------------------------------------------------------
    // Lifecycle
    //-------------------------------------------------------------------------

    setup() {
        const getPickerProps = () => {
            const value = this.getRecordValue();
            /** @type {DateTimePickerProps} */
            const pickerProps = {
                value,
                type: this.field.type,
                range: this.isRange(value),
                showRangeToggler:
                    this.relatedField && !this.props.required && !this.props.alwaysRange,
                onToggleRange,
            };
            if (this.props.maxDate) {
                pickerProps.maxDate = this.parseLimitDate(this.props.maxDate);
Registry / API
Registry name
DateTimeField
Category
Module
web
Slug
date-time-field
Nav group
forms